// ALL RIGHTS RESERVED:
// ====================
// The contents of this file, and associated files in this directory, are 
// Copyright (C) SPT , all rights reserved, 2009.
// All software Source Code, Images, Database-Design and code, Graphics Design 
// and source files, and related content (collectively referred to as SOURCE) are 
// Copyright (c) SPT, 2009, All Rights Reserved.
// http://SerialPortTool.com

// PTZ Controller is a Registered Trademark of SerialPortTool.com.


using System;
using System.IO;
using System.Xml;
using System.Data;
using System.Text;
using System.Drawing;
using System.Threading;
using System.Diagnostics;
using System.ComponentModel;
using System.Windows.Forms;
using System.Reflection;
using System.Xml.Serialization;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using Microsoft.VisualBasic;

using SPT.PTZ.Core;
using PTZController.Logic;
using PTZController.Core;
using PTZController.Controls;

using Spt.Share.Controls;

namespace PTZController
{
    public partial class MainForm : SkinForm
    {
        #region definitions


        #endregion

        #region form load/close

        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            helpProvider1.HelpNamespace = @"PTZ Helper.chm";

#if !DEBUG
            Splasher.Show(typeof(SplashScreen));
            Splasher.Status = "Initializing Main Form ...";

            System.Threading.Thread.Sleep(2000);
            Splasher.Close();
#endif

            // load configure
            string path = HelpFunction.GetConfigFile();
            m_configure = PtzConfigure.Load(path, this.Size);

            Left = m_configure.MainFormX;
            Top = m_configure.MainFormY;

            // initialize
            InitializeController();
            InitializeRegions();
            InitializePanels();
            InitializeMenuItems();
            InitializeListBoxes();

            freeBox.MoveCommand = new FreeMoveBox.MoveCommandHandler(DoMoveCommand);

            // focus window
            Activate();

#if DEBUG
            // for testing Tour purpose
            // DoTour();
#endif
        }

        private void InitializeRegions()
        {
            m_regions[NoneIndex] = HelpFunction.CalculateBitmapRegion(Properties.Resources.main_bg_none);
            m_regions[CameraIndex] = HelpFunction.CalculateBitmapRegion(Properties.Resources.main_bg_camera);
            m_regions[PresetIndex] = HelpFunction.CalculateBitmapRegion(Properties.Resources.main_bg_preset);
            m_regions[AllIndex] = HelpFunction.CalculateBitmapRegion(Properties.Resources.main_bg_all);

            ApplyViews(0, m_configure.ViewType);
        }

        private void InitializePanels()
        {
            ShowAUX(m_configure.States[AuxIndex].Visible);
            ShowCustomize(m_configure.States[CustomizeIndex].Visible);
        }

        private void ShowCustomize(bool showFlag)
        {
            CustomizeForm customForm = new CustomizeForm();

            customForm.CustomizeEvent = new CustomizeForm.CustomizeEventHandler(DoCustomizeCommand);
            customForm.Customs = m_configure.Customs;
            m_panels[CustomizeIndex] = customForm;

            foreach (PanelForm i in m_panels)
            {
                i.Owner = this;
                i.OwnerForm = this;
            }

            ShowPanel(CustomizeIndex, showFlag);
        }

        private void InitializeListBoxes()
        {
            // initialize cameras
            CameraItemList cameras = m_configure.Cameras;

            cameraListBox.Items.Clear();

            for (int i = 0; i < cameras.Length; i++)
            {
                cameraListBox.Items.Add(cameras[i].ToString());
            }

            if (cameraListBox.Items.Count > 0)
            {
                cameraListBox.SelectedIndex = m_configure.CameraIndex;
            }
        }

        private void InitializeMenuItems()
        {
            // protocols
            string[] protocols = Controller.ProtocolNames;

            for (int i = 0; i < protocols.Length; i++)
            {
                ToolStripMenuItem item = protocolMenuItem.DropDownItems.Add(protocols[i]) as ToolStripMenuItem;

                item.Click += new EventHandler(protocolMenuItem_Click);
                item.Checked = (protocols[i] == m_configure.ProtocolName);
                item.Tag = protocols[i];
            }
            // ports
            string[] ports = Controller.PortNames;

            for (int i = 0; i < ports.Length; i++)
            {
                ToolStripMenuItem item = comPortMenuItem.DropDownItems.Add(ports[i]) as ToolStripMenuItem;

                item.Click += new EventHandler(comPortMenuItem_Click);
                item.Checked = (ports[i] == m_configure.ComPort);
                item.Tag = ports[i];
            }

            // baud rates
            int[] rates = Controller.BaudRates;

            for (int i = 0; i < rates.Length; i++)
            {
                ToolStripMenuItem item = baudRateMenuItem.DropDownItems.Add(rates[i].ToString()) as ToolStripMenuItem;

                item.Click += new EventHandler(baudRateMenuItem_Click);
                item.Checked = (rates[i] == m_configure.BaudRate);
                item.Tag = rates[i];
            }
        }

        private void InitializeController()
        {
            try
            {
                if (m_configure.ComPort == null)
                {
                    throw new Exception(Properties.Resources.MessageNoComPort);
                }

                m_controller.OpenPort(m_configure.ComPort, m_configure.BaudRate);
                m_controller.ChangeProtocol(m_configure.ProtocolName);

                Debug.WriteLine(string.Format(
                    "Initialize controller, {0}, {1}, {2}",
                    m_configure.ComPort,
                    m_configure.BaudRate,
                    m_configure.ProtocolName));

                m_controller.Address = (byte)(m_configure.CameraIndex + 1);

                commandTimer.Interval = m_controller.Rate;
                commandTimer.Enabled = m_controller.Repeatable;

                StatusLabel1.Text = m_configure.ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Text, MessageBoxButtons.OK);
                StatusLabel1.Text = Properties.Resources.TextNoComPort;
            }
        }

        private void MainForm_Closed(object sender, FormClosedEventArgs e)
        {
            // save configure
            m_configure.MainFormX = Left;
            m_configure.MainFormY = Top;

            string path = HelpFunction.GetConfigFile();
            m_configure.Save(path, m_panels);
        }
        #endregion 

        #region window buttons
        private void minimizeButton_Click(object sender, EventArgs e)
        {
            WindowState = FormWindowState.Minimized;
        }

        private void menuButton_Click(object sender, EventArgs e)
        {
            Point pt = new Point(menuButton.Left, menuButton.Bottom);
            popupMenu.Show(PointToScreen(pt));
        }

        private void closeButton_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void menuButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextMenuButton;
        }

        private void helpButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextHelpButton;
        }

        private void minimizeButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextMinimizeButton;
        }

        private void closeButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextCloseButton;
        }

        #endregion

        #region panels
        private void menuViewButton_Click(object sender, EventArgs e)
        {
			if (m_configure.IsPanasonic())
			{
				PanasonicMenuForm f = new PanasonicMenuForm();

				f.MenuEvent = new MenuEventHandler(DoMenuCommand);
				f.ShowDialog();
			}
			else // regular form
			{
				MenuForm f = new MenuForm();

				f.MenuEvent = new MenuEventHandler(DoMenuCommand);
				f.ShowDialog();
			}
		}

        private void autoScanViewButton_Click(object sender, EventArgs e)
        {
            DoTour();
        }

        private void auxViewButton_Click(object sender, EventArgs e)
        {
            ShowAUX(true);
        }

        private void ShowAUX(bool showFlag)
        {
            PanelForm panel = null;

            if (m_configure.ProtocolName == Controller.Bosch)
            {
                BoschAUXForm auxForm = new BoschAUXForm();

                auxForm.AuxEvent = new AuxEventHandler(DoAuxCommand);

                panel = auxForm;
            }
            else
            {
                AuxForm auxForm = new AuxForm();

                auxForm.AuxEvent = new AuxEventHandler(DoAuxCommand);

                panel = auxForm;
            }

            panel.Owner = this;
            panel.OwnerForm = this;

            if (m_panels[AuxIndex] != null)
            {
                m_panels[AuxIndex].Close();
            }

            m_panels[AuxIndex] = panel;
            ShowPanel(AuxIndex, showFlag);
        }

        private void DoAuxCommand(PtzFunction function, int index)
        {
            try
            {
                m_controller.SendCommand(function, (byte)index);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Text, MessageBoxButtons.OK);
            }
        }

        private void customizeViewButton_Click(object sender, EventArgs e)
        {
            ShowCustomize(true);
        }

        private void ShowPanel(int index, bool show)
        {
            if (show)
            {
                if (!m_panels[index].Visible)
                {
                    Point pt = new Point(
                        Left + m_configure.States[index].OffsetX,
                        Top + m_configure.States[index].OffsetY);
                    bool flag = false;

                    for (int i = 0; i < Screen.AllScreens.Length; i++)
                    {
                        if (Screen.AllScreens[i].Bounds.Contains(pt))
                        {
                            flag = true;
                            break;
                        }    
                    }

                    if (!flag) // reset default position
                    {
                        m_configure.States[index].OffsetX = PtzConfigure.PanelPoints[index].X;
                        m_configure.States[index].OffsetY = PtzConfigure.PanelPoints[index].Y;
                    }

                    m_panels[index].Visible = true;

                    m_panels[index].Left = Left + m_configure.States[index].OffsetX;
                    m_panels[index].Top = Top + m_configure.States[index].OffsetY;
                }
            }
            else
            {
                m_panels[index].Visible = false;
            }
        }

        private void menuViewButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextMenu;
        }

        private void autoScanViewButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextAutoScan;
        }

        private void auxViewButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextAux;
        }

        private void customizeViewButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextCustomize;
        }
        #endregion

        #region camera/preset views
        private void viewCameraButton_Click(object sender, EventArgs e)
        {
            DoApply(PtzConfigure.CameraView);
        }

        private void viewPresetButton_Click(object sender, EventArgs e)
        {
            DoApply(PtzConfigure.PresetView);
        }

        private void DoApply(UInt32 mask)
        {
            UInt32 newV;

            if ((m_configure.ViewType & mask) == 0)
            {
                newV = m_configure.ViewType | mask;
                ApplyViews(m_configure.ViewType, newV);
            }
            else
            {
                newV = m_configure.ViewType & ~(mask);
                ApplyViews(m_configure.ViewType, newV);
            }

            m_configure.ViewType = newV;
        }

        private void ApplyViews(UInt32 oldView, UInt32 newView)
        {
            bool flagC = false; // flag indicating change of camera view
            bool flagP = false; // flag indicating change of preset view
            bool camera = false;// flag for target camera view state
            bool preset = false;// flag for target preset view state

            if (((oldView & PtzConfigure.CameraView) == 0) &&
                ((newView & PtzConfigure.CameraView) != 0))
            {
                cameraNameLabel.Top -= CameraLabelOffset;
                viewCameraButton.Top -= CameraLabelOffset;

                flagC = true;
                camera = true;
            }

            if (((oldView & PtzConfigure.CameraView) != 0) &&
                ((newView & PtzConfigure.CameraView) == 0))
            {
                cameraNameLabel.Top += CameraLabelOffset;
                viewCameraButton.Top += CameraLabelOffset;

                flagC = true;
                camera = false;
            }

            if (((oldView & PtzConfigure.PresetView) == 0) &&
                ((newView & PtzConfigure.PresetView) != 0))
            {
                viewPresetLabel.Top += PresetLabelOffset;
                viewPresetButton.Top += PresetLabelOffset;

                flagP = true;
                preset = true;
            }

            if (((oldView & PtzConfigure.PresetView) != 0) &&
                ((newView & PtzConfigure.PresetView) == 0))
            {
                viewPresetLabel.Top -= PresetLabelOffset;
                viewPresetButton.Top -= PresetLabelOffset;

                flagP = true;
                preset = false;
            }

            if (flagC)
            {
                UseArrowButton(viewCameraButton, camera);
                renameCameraButton.Visible = camera;
                cameraListBox.Visible = camera;
            }

            if (flagP)
            {
                UseArrowButton(viewPresetButton, !preset);
                setPresetButton.Visible = preset;
                gotoPresetButton.Visible = preset;
                clearPresetButton.Visible = preset;
                renamePresetButton.Visible = preset;
                presetListBox.Visible = preset;
            }

            UpdateRegion(newView);
        }

        private void UpdateRegion(UInt32 newView)
        {
            Image img = null;

            switch (newView)
            {
                case NoneIndex:
                    img = Properties.Resources.main_bg_none;
                    break;
                case CameraIndex:
                    img = Properties.Resources.main_bg_camera;
                    break;
                case PresetIndex:
                    img = Properties.Resources.main_bg_preset;
                    break;
                case AllIndex:
                    img = Properties.Resources.main_bg_all;
                    break;
                default:
                    break;
            }

            BackgroundImage = img;
            HelpFunction.CreateControlRegion(this, m_regions[newView].Clone());
        }

        private void UseArrowButton(Spt.Share.Controls.ctlSkinButton button,  bool down)
        {
            if (!down)
            {
                button.Hover = Properties.Resources.btn_go_up_hover;
                button.Normal = Properties.Resources.btn_go_up_normal;
                button.Pressed = Properties.Resources.btn_go_up_press;
            }
            else
            {
                button.Hover = Properties.Resources.btn_go_down_hover;
                button.Normal = Properties.Resources.btn_go_down_normal;
                button.Pressed = Properties.Resources.btn_go_down_press;
            }
        }
        #endregion

        #region menu commands
        private void protocolMenuItem_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem o = sender as ToolStripMenuItem;

            m_configure.ProtocolName = o.Tag.ToString();

            foreach (ToolStripMenuItem i in protocolMenuItem.DropDownItems)
            {
                i.Checked = (i.Text == o.Text);
            }

            InitializeController();
            ShowAUX(m_panels[AuxIndex].Visible);
        }

        private void comPortMenuItem_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem o = sender as ToolStripMenuItem;

            m_configure.ComPort = o.Tag.ToString();

            foreach (ToolStripMenuItem i in comPortMenuItem.DropDownItems)
            {
                i.Checked = (i.Text == o.Text);
            }

            InitializeController();
        }

        private void baudRateMenuItem_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem o = sender as ToolStripMenuItem;

            m_configure.BaudRate = (int) o.Tag;

            foreach (ToolStripMenuItem i in baudRateMenuItem.DropDownItems)
            {
                i.Checked = (i.Text == o.Text);
            }

            InitializeController();
        }

        private void editCustomizationMenuItem_Click(object sender, EventArgs e)
        {
            EditForm f = new EditForm();

            f.Customs = m_configure.Customs.Items;
            f.Cameras = m_configure.Cameras;

            if (f.ShowDialog() == DialogResult.OK)
            {
                m_configure.Customs.Items = f.Customs;

                CustomizeForm cf = m_panels[CustomizeIndex] as CustomizeForm;

                cf.Customs.Items = f.Customs;
                cf.ApplyCustomData();
            }
        }

        #endregion

        #region camera view
        private void cameraListBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            ChangeCamera();
        }

        private void ChangeCamera()
        {
	        int n = cameraListBox.SelectedIndex;

            if (n < 0)
            {
                return;
            }

            EnsureVisible(cameraListBox, n);
            
            m_configure.CameraIndex = n;

            try
            {
                int address = n;
                m_controller.Address = (byte)address;
                m_controller.SendCommand(PtzFunction.SelectCamera, address);
            }
            catch (Exception)
            {
            }

            cameraNameLabel.Text = string.Format("Camera - {0:D3}", cameraListBox.SelectedIndex);

            PresetItemList presets = m_configure.Cameras[n].Presets;

            if (presets != null)
            {
                presetListBox.Items.Clear();

                for (int i = 0; i < presets.Length; i++)
                {
                    presetListBox.Items.Add(presets[i].ToString());
                }

                presetListBox.SelectedIndex = m_configure.PresetIndex;
            }
        }

        private void EnsureVisible(ListBox box, int n)
        {
            // make sure item visible
            int visibleN = box.ClientRectangle.Height / box.ItemHeight;

            if (n < box.TopIndex)
            {
                box.TopIndex = Math.Max(n - visibleN, 0);
            }
            else if (n > box.TopIndex + visibleN)
            {
                box.TopIndex = Math.Min(n - visibleN, cameraListBox.Items.Count);
            }
        }
        private void renameCameraButton_Click(object sender, EventArgs e)
        {
            RenameCamera();
        }

        private void cameraListBox_DoubleClick(object sender, EventArgs e)
        {
            m_configure.CameraIndex = cameraListBox.SelectedIndex;

            RenameCamera();
        }

        private void RenameCamera()
        {
            int n = m_configure.CameraIndex;
            RenameForm f = new RenameForm();

            f.Text = string.Format("Rename Camera {0:D3}", n);
            f.ItemName = m_configure.Cameras[n].Name;

            if (f.ShowDialog() == DialogResult.OK)
            {
                m_configure.Cameras[n].Name = f.ItemName;

                cameraListBox.Items[n] = m_configure.Cameras[n].ToString();
                // cameraNameLabel.Text = string.Format("Cam - {0}", m_configure.Cameras[n].Name);
            }
        }

        private void renameCameraButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextRenameCamera;
        }

        private void cameraListBox_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextCameraListBox;
        }

        private void viewCameraButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextToggleCamera;
        }
        #endregion

        #region preset view
        private void renamePresetButton_Click(object sender, EventArgs e)
        {
            RenamePreset();
        }

        private void presetListBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            int n = presetListBox.SelectedIndex;

            viewPresetLabel.Text = string.Format("Preset - {0:D3}", n + 1);

            m_configure.PresetIndex = n;
            EnsureVisible(presetListBox, n);
        }

        private void RenamePreset()
        {
            int n = presetListBox.SelectedIndex;
            RenameForm f = new RenameForm();

            CameraItem cam = m_configure.Cameras[m_configure.CameraIndex];

            f.Text = string.Format("Rename Preset {0:D3}", n + 1);
            f.ItemName = cam.Presets[n].Name;

            if (f.ShowDialog() == DialogResult.OK)
            {
                cam.Presets[n].Name = f.ItemName;
                presetListBox.Items[n] = cam.Presets[n].ToString();
            }
        }

        private void setPresetButton_Click(object sender, EventArgs e)
        {
            int address = m_configure.PresetIndex + 1;
            m_controller.SendCommand(PtzFunction.SetPreset, address);
        }

        private void gotoPresetButton_Click(object sender, EventArgs e)
        {
            GoToPreset();
        }

        private void GoToPreset()
        {
            int address = m_configure.PresetIndex + 1;
            m_controller.SendCommand(PtzFunction.GotoPreset, address);
        }

        private void clearPresetButton_Click(object sender, EventArgs e)
        {
            int address = m_configure.PresetIndex + 1;
            m_controller.SendCommand(PtzFunction.ClearPreset, address);
        }

        /// <summary>
        /// Double click preset list box, go to selected preset
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void presetListBox_DoubleClick(object sender, EventArgs e)
        {
            GoToPreset();
        }

        private void presetListBox_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextPreseListBox;
        }

        private void setPresetButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextSetPreset;
        }

        private void gotoPresetButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextGotoPreset;
        }

        private void clearPresetButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextClearPreset;
        }

        private void renamePresetButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextRenamePreset;
        }

        private void viewPresetButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextTogglePreset;
        }


        #endregion

        #region iris, focus, zoom
        private void focusFarButton_MouseDown(object sender, MouseEventArgs e)
        {
            m_controller.SendCommand(PtzFunction.FocusFar);
        }

        private void focusNearButton_MouseDown(object sender, MouseEventArgs e)
        {
            m_controller.SendCommand(PtzFunction.FocusNear);
        }

        private void irisDarkButton_MouseDown(object sender, MouseEventArgs e)
        {
            m_controller.SendCommand(PtzFunction.IrisClose);
        }

        private void irisLightButton_MouseDown(object sender, MouseEventArgs e)
        {
            m_controller.SendCommand(PtzFunction.IrisOpen);
        }

        private void zoomOutButton_MouseDown(object sender, MouseEventArgs e)
        {
            m_controller.SendCommand(PtzFunction.ZoomOut);
        }

        private void zoomOutButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextZoomOut;
        }

        private void zoomInButton_MouseDown(object sender, MouseEventArgs e)
        {
            m_controller.SendCommand(PtzFunction.ZoomIn);
        }

        private void zoomInButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextZoomIn;
        }

        private void buttons_MouseUp(object sender, MouseEventArgs e)
        {
            m_controller.SendCommand(PtzFunction.StopAction);
        }

        private void focusFarButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextFocusFar;
        }

        private void focusNearButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextFocusNear;
        }

        private void irisDarkButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextIrisDark;
        }

        private void irisLightButton_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextIrisLight;
        }

        private void buttons_MouseLeave(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextReady;
        }

        #endregion

        #region free move
        private void DoMoveCommand(byte[] param, string text)
        {
            if (param != null)
            {
                m_controller.SendCommand(PtzFunction.FreeMove, param);
            }
            else
            {
                m_controller.SendCommand(PtzFunction.StopAction);
            }

            StatusLabel2.Text = text;
            StatusLabel2.Refresh();
            System.Diagnostics.Debug.WriteLine(text);
        }

        private void freeBox_MouseEnter(object sender, EventArgs e)
        {
            StatusLabel2.Text = Properties.Resources.TextFreeMove;
        }
        #endregion

        #region aux
        private void DoAuxCommand(int index, bool on)
        {
            try
            {
                m_controller.SendCommand(
                    on ? PtzFunction.AuxiliaryOn : PtzFunction.AuxiliaryOff,
                    (byte)index);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Text, MessageBoxButtons.OK);
            }
        }
        #endregion

        #region ptz menu
        private void DoMenuCommand(PtzFunction function, int extra)
        {
            try
            {
                if (function == PtzFunction.MenuMove)
                {
                    m_controller.SendCommand(function, extra);

                    // For Panasonic, we don't send stop command
                    if (!m_configure.IsPanasonic())
                    {
                    Thread.Sleep(250);
                    m_controller.SendCommand(PtzFunction.StopAction);
                }
                }
                else
                {
                    m_controller.SendCommand(function);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Text, MessageBoxButtons.OK);
            }
        }
        #endregion

        #region customize command
        private void DoCustomizeCommand(int index)
        {
            try
            {
                CustomItem item = m_configure.Customs[index];

                if (item.Command == CustomItem.CustomCommand.Function)
                {
                    PtzFunction func = PtzFunction.StopAction;

                    switch (item.Mode)
                    {
                        case CustomItem.FunctionMode.AuxOn:
                            func = PtzFunction.AuxiliaryOn;
                            break;
                        case CustomItem.FunctionMode.AuxOff:
                            func = PtzFunction.AuxiliaryOff;
                            break;
                        case CustomItem.FunctionMode.GotoPreset:
                            func = PtzFunction.GotoPreset;
                            break;
                    }

                    if (item.Value > 0) // 0 is invalid item value
                    {
                        // save old address
                        byte oldAddr = m_controller.Address;

                        m_controller.Address = (byte)item.Camera;
                        m_controller.SendCommand(func, item.Value);
                        m_controller.Address = oldAddr;
                    }
                }
                else
                {
                    m_controller.SendCommand(item.HexValue, 0, item.HexValue.Length);
                }
            }
            catch (Exception)
            {

            }
        }

        #endregion

        #region repeat command
        private void commandTimer_Tick(object sender, EventArgs e)
        {
            m_controller.RepeatCommand();
        }
        #endregion


        private const int CameraLabelOffset = 184;
        private const int PresetLabelOffset = 214;

        private const int AuxIndex = 0;
        private const int CustomizeIndex = 1;

        private PanelForm[] m_panels = new PanelForm[PtzConfigure.PanelCount];

        private const int RegionCount = 4;

        private const int NoneIndex = 0;
        private const int CameraIndex = 1;
        private const int PresetIndex = 2;
        private const int AllIndex = 3;

        private Region[] m_regions = new Region[RegionCount];

        private PtzConfigure m_configure;
        private Controller m_controller = new Controller();

        private void helpButton_Click(object sender, EventArgs e)
        {
            Help.ShowHelp(this, ProductInfo.HelpFile, HelpNavigator.Topic, "MainGUI.htm");
        }

        private void toolStripMenuAbout_Click(object sender, EventArgs e)
        {
            AboutForm dlg = new AboutForm();
            dlg.ShowDialog();
        }

        private void toolStripMenuRegister_Click(object sender, EventArgs e)
        {
            ShowRegisterDialog();
        }

        private void ShowRegisterDialog()
        {
        }

        private void _tourMenuItem_Click(object sender, EventArgs e)
        {
            DoTour();
        }

        /// <summary>
        /// Do tour operation
        /// </summary>
        private void DoTour()
        {
            TourForm tourF = new TourForm();

            tourF.ScanWay = m_configure.ScanWay;

            tourF.Span = m_configure.TourSpan;
            tourF.Cameras = m_configure.Cameras;
            tourF.Controller = m_controller;
            tourF.TourItems = m_configure.TourItems;
            tourF.ScanSpeed = m_configure.ScanSpeed;
            tourF.ClockWise = m_configure.ClockWise;

            tourF.ShowDialog();

            m_configure.ScanWay = tourF.ScanWay;
            m_configure.TourSpan = tourF.Span;
            m_configure.TourItems = tourF.TourItems;
            m_configure.ScanSpeed = tourF.ScanSpeed;
            m_configure.ClockWise = tourF.ClockWise;
        }

        #region Hot keys
        /// <summary>
        /// Initial speed of move
        /// </summary>
        const int DefaultSpeed = 1;

        /// <summary>
        /// Repeat for this times to increase speed level
        /// </summary>
        const int RepeatStride = 5;    

        byte[] _freeMoveData = new byte[3];
        int _freeMoveCounter = 0;
        int _freeMoveSpeed = DefaultSpeed;

        PtzFunction _latestCommand = PtzFunction.StopAction;

		string _inputtedDigits = "";

        private void MainForm_KeyDown(object sender, KeyEventArgs e)
        {
            Debug.Print("Key down, {0}", e.ToString());

            ctlSkinButton button;

            if (GetCommandTarget(e, true, out button, out _latestCommand))
            {
                if (_latestCommand == PtzFunction.FreeMove)
                {
                    freeBox.SetMoveData(_freeMoveData);
                }
                else if (_latestCommand == PtzFunction.GotoPreset)
                {
                    int presetN = (int)_freeMoveData[0];

                    m_controller.SendCommand(_latestCommand, presetN);
                    presetListBox.SelectedIndex = presetN - 1;
                }
                else if (_latestCommand != PtzFunction.None)
                {
                    button.DoMouseEnter();
                    button.DoMouseDown();
                    m_controller.SendCommand(_latestCommand);
                }
            }
        }

        private void MainForm_KeyUp(object sender, KeyEventArgs e)
        {
            Debug.Print("Key up, {0}", e.ToString());

            ctlSkinButton button;

            if (GetCommandTarget(e, false, out button, out _latestCommand))
            {
                if (_latestCommand == PtzFunction.FreeMove)
                {
                    freeBox.SetMoveData(null);
                    m_controller.SendCommand(PtzFunction.StopAction);
                }
                else if ((_latestCommand != PtzFunction.None) && 
                    (_latestCommand != PtzFunction.GotoPreset))
                {
                    button.DoMouseUp();
                    button.DoMouseLeave();
                    m_controller.SendCommand(PtzFunction.StopAction);
                }

                StatusLabel2.Text = Properties.Resources.TextReady;
            }

            _freeMoveSpeed = DefaultSpeed;
            _freeMoveCounter = 0;
        }

        private bool GetCommandTarget(
            KeyEventArgs e, 
            bool isKeyDownEvent,
            out ctlSkinButton button, 
            out PtzFunction function)
        {
            bool flag = true;

            button = null;
            function = PtzFunction.None;

            if (isKeyDownEvent && HandleCameraSwitchKeys(e)) // we only handle camera switch in key down event
            {
                return flag;
            }

            if (HandleRegularKeys(e, ref button, ref function))
            {
                return flag;
            }

			if (HandleCameraOrPresetKeys(isKeyDownEvent, e, ref function))
            {
                return flag;
            }

            return HandleFreeMoveKeys(e, ref function);
        }

        private bool HandleCameraSwitchKeys(KeyEventArgs e)
        {
            bool flag = true;
            int index = cameraListBox.SelectedIndex;

            switch (e.KeyCode)
            {
                case Keys.PageUp:
                    if (index > 0)
                    {
                        cameraListBox.SelectedIndex = index - 1;
                    }
                    break;
                case Keys.PageDown:
                    if (index < cameraListBox.Items.Count - 1)
                    {
                        cameraListBox.SelectedIndex = index + 1;
                    }
                    break;
                default:
                    flag = false;
                    break;
            }

            return flag;
        }

        private bool HandleRegularKeys(KeyEventArgs e, ref ctlSkinButton button, ref PtzFunction function)
        {
            bool flag = true;

            switch (e.KeyCode)
            {
                case Keys.Q:
                    button = focusFarButton;
                    function = PtzFunction.FocusFar;
                    StatusLabel2.Text = Properties.Resources.TextFocusFar;
                    break;
                case Keys.W:
                    button = focusNearButton;
                    function = PtzFunction.FocusNear;
                    StatusLabel2.Text = Properties.Resources.TextFocusNear;
                    break;
                case Keys.A:
                    button = zoomOutButton;
                    function = PtzFunction.ZoomOut;
                    StatusLabel2.Text = Properties.Resources.TextZoomOut;
                    break;
                case Keys.S:
                    button = zoomInButton;
                    function = PtzFunction.ZoomIn;
                    StatusLabel2.Text = Properties.Resources.TextZoomIn;
                    break;
                case Keys.Z:
                    button = irisDarkButton;
                    function = PtzFunction.IrisClose;
                    StatusLabel2.Text = Properties.Resources.TextIrisDark;
                    break;
                case Keys.X:
                    button = irisLightButton;
                    function = PtzFunction.IrisOpen;
                    StatusLabel2.Text = Properties.Resources.TextIrisLight;
                    break;
                default:
                    flag = false;
                    break;
            }

            return flag;
        }

        private bool HandleCameraOrPresetKeys(bool keyDownFlag, KeyEventArgs e, ref PtzFunction function)
        {
            bool flag = false;

			if (!keyDownFlag)
			{
				if (e.KeyCode == Keys.Menu || e.KeyCode == Keys.ControlKey) // unpressed Alt or Ctrl key
				{
					_inputtedDigits = ""; // clear input buffer
					StatusLabel2.Text = "Ready";
				}

				return flag;
			}

            if (e.KeyCode == Keys.Add)
            {
                if (presetListBox.SelectedIndex < presetListBox.Items.Count - 1)
                {
                    function = PtzFunction.GotoPreset;
                    _freeMoveData[0] = (byte)(presetListBox.SelectedIndex + 2); // plus 1
                    flag = true;
                }
            }
            else if (e.KeyCode == Keys.Subtract)
            {
                if (presetListBox.SelectedIndex > 0)
                {
                    function = PtzFunction.GotoPreset;
                    _freeMoveData[0] = (byte)presetListBox.SelectedIndex; // minus 1
                    flag = true;
                }
            }
            // Ctrl + 0~9 is for going to preset 1~10
            else if ((e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9)) 
			{
				int index = (e.KeyCode - Keys.D0);

                // we map "0" to 10
				if (index == 0)
            {
					index = 10;
				}

				if (e.Control && !e.Alt && !e.Shift)
                {
                    function = PtzFunction.GotoPreset;

                    _freeMoveData[0] = (byte)index;
                    _freeMoveData[1] = 0;
                    _freeMoveData[2] = 0;

                    flag = true;
				}
				else if (!e.Control && e.Alt && !e.Shift)
				{
					cameraListBox.SelectedIndex = index;
                }
			}
			// Ctrl + number pad 0~9 is for 3-digits inputting preset
			else if (e.KeyCode >= Keys.NumPad0 && e.KeyCode <= Keys.NumPad9)
			{
				int n = e.KeyValue - (int)Keys.NumPad0;

				if (_inputtedDigits.Length == 3) // replace preset of last time
                    {
					_inputtedDigits = n.ToString();
                    }
				else 
				{
					_inputtedDigits = _inputtedDigits + n.ToString();

					if (_inputtedDigits.Length == 3)
					{
						int index = int.Parse(_inputtedDigits);

						if (e.Control && !e.Alt && !e.Shift) // Ctrl only
						{
							if (index > 0 && index < 128) // our preset is 1~127
							{
								function = PtzFunction.GotoPreset;

								_freeMoveData[0] = (byte)(index);
                    _freeMoveData[1] = 0;
                    _freeMoveData[2] = 0;

                    flag = true;
                }
							else
							{
								_inputtedDigits = "";
							}
						}
						else if (!e.Control && e.Alt && !e.Shift) // Alt only
						{
							if (index >= 0 && index < 256)
							{
								cameraListBox.SelectedIndex = index;
							}
							else
							{
								_inputtedDigits = "";
							}
						}
					}

					if (e.Control && !e.Alt && !e.Shift) // Ctrl only
					{
						StatusLabel2.Text = string.Format("Go to preset {0}", _inputtedDigits);
					}
					else if (!e.Control && e.Alt && !e.Shift) // Alt only
					{
						StatusLabel2.Text = string.Format("Go to camera {0}", _inputtedDigits);
					}
				}
            }

            return flag;
        }

        private bool HandleFreeMoveKeys(KeyEventArgs e, ref PtzFunction function)
        {
            bool flag = true;

			if (e.Control || e.Shift || e.Alt)
			{
				return false;
			}

            _freeMoveCounter++;

            int step = (_freeMoveCounter / RepeatStride) + 1;

            _freeMoveSpeed += step;

            if (_freeMoveSpeed >= FreeMoveBox.MaxSpeed)
            {
                _freeMoveSpeed = FreeMoveBox.MaxSpeed - 1;
            }

            switch (e.KeyCode)
            {
                case Keys.NumPad4: // left
                    function = PtzFunction.FreeMove;
                    _freeMoveData[0] = (byte)Direction.West;
                    _freeMoveData[1] = (byte)_freeMoveSpeed;
                    _freeMoveData[2] = 0;
                    break;
                case Keys.NumPad6: // right
                    function = PtzFunction.FreeMove;
                    _freeMoveData[0] = (byte)Direction.East;
                    _freeMoveData[1] = (byte)_freeMoveSpeed;
                    _freeMoveData[2] = 0;
                    break;
                case Keys.NumPad8: // up
                    function = PtzFunction.FreeMove;
                    _freeMoveData[0] = (byte)Direction.North;
                    _freeMoveData[1] = 0;
                    _freeMoveData[2] = (byte)_freeMoveSpeed;
                    break;
                case Keys.NumPad2: // down
                    function = PtzFunction.FreeMove;
                    _freeMoveData[0] = (byte)Direction.South;
                    _freeMoveData[1] = 0;
                    _freeMoveData[2] = (byte)_freeMoveSpeed;
                    break;
                case Keys.NumPad7: // left-up
                    function = PtzFunction.FreeMove;
                    _freeMoveData[0] = (byte)Direction.NorthWest;
                    _freeMoveData[1] = (byte)_freeMoveSpeed;
                    _freeMoveData[2] = (byte)_freeMoveSpeed;
                    break;
                case Keys.NumPad9: // right-up
                    function = PtzFunction.FreeMove;
                    _freeMoveData[0] = (byte)Direction.NorthEast;
                    _freeMoveData[1] = (byte)_freeMoveSpeed;
                    _freeMoveData[2] = (byte)_freeMoveSpeed;
                    break;
                case Keys.NumPad1: // left-down
                    function = PtzFunction.FreeMove;
                    _freeMoveData[0] = (byte)Direction.SouthWest;
                    _freeMoveData[1] = (byte)_freeMoveSpeed;
                    _freeMoveData[2] = (byte)_freeMoveSpeed;
                    break;
                case Keys.NumPad3: // right-down
                    function = PtzFunction.FreeMove;
                    _freeMoveData[0] = (byte)Direction.SouthEast;
                    _freeMoveData[1] = (byte)_freeMoveSpeed;
                    _freeMoveData[2] = (byte)_freeMoveSpeed;
                    break;
                default:
                    flag = false;
                    break;
            }

            return flag;
        }

        private void listBoxes_SupressKeyDown(object sender, KeyEventArgs e)
        {
            e.SuppressKeyPress = true;
        }
        #endregion
    }
}
