using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using PS_SDK_Net;

namespace Sample
{
    public partial class MainForm : Form
    {
        const int MaxMsgsCount = 100;
        Queue<string> allMsgs = new Queue<string>();

        CameraManager cameraManager;
        Session session = null;

        bool isVideo = false;
        
        public MainForm()
        {
            InitializeComponent();

            try
            {
                cameraManager = new CameraManager();

                cameraManager.CameraListChanged += cameraManager_CameraListChanged;
                cameraManager.CameraInitializationError += cameraManager_CameraInitializationError;

                List<PSCameraInfo> ci = cameraManager.CameraList;

                RealizeButtons();
            }
            catch ( PSResultException exc )
            {
                if (exc.Result == PSResult.PS_PSSDK_ALREADY_RUNNING)
                {
                    MessageBox.Show("PS SDK Already Running");
                    Shown += (object sender, EventArgs e) => { Close(); };
                }
            }
        }

        void cameraManager_CameraListChanged(PSCameraInfo cameraInfo, 
            PSCameraConnectionState state)
        {
            ShowMsg(string.Format(
                "CameraListChanged event: {0}\r\n" +
                "    Id: {1}\r\n" +
                "    Name: {2}\r\n" +
                "    PSM Version: {3}\r\n" +
                "    System Id: {4}\r\n" +
                "    Serial Number: {5}\r\n", 
                state, cameraInfo.id, cameraInfo.name, 
                cameraInfo.psmVersion, cameraInfo.systemId,
                cameraInfo.serialNumber));
        }

        void CameraDisconnect()
        {
            if (session == null)
                return;

            cbPropVal.Items.Clear();
            cbPropName.Items.Clear();
            tbFocus.Text = "";
            session.NewPreviewFrame -= session_NewPreviewFrame;
            session.PropertyListChanged -= session_PropertyListChanged;
            session.ZoomChanged -= session_ZoomChanged;
            session.FocusChanged -= session_FocusChanged;
            session.CameraError -= session_CameraError;
            session.Close();
            session = null;
            pbPreview.Image = null;
            RealizeButtons();
        }

        void cameraManager_CameraInitializationError(PSResult res, string camSystemId)
        {
            ShowMsg(string.Format(
                "CameraInitializationError event:\r\n" +
                "   System id: {0}\r\n" +
                "   Error: {1}\r\n", camSystemId, res));
        }

        void ShowMsg(string msg)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new MethodInvoker(() => ShowMsg(msg)));
                return;
            }

            allMsgs.Enqueue(msg);

            if (allMsgs.Count > MaxMsgsCount)
                allMsgs.Dequeue();

            tbMsgs.Text = allMsgs.Aggregate(new StringBuilder(), 
                (a, n) => a.Append(n)).ToString();
            
            tbMsgs.SelectionStart = tbMsgs.Text.Length;
            tbMsgs.ScrollToCaret();
        }

        private void btnOpenSession_Click(object sender, EventArgs e)
        {
            OpenSessionDlg dlg = new OpenSessionDlg();
            if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                session = cameraManager.OpenSession(dlg.SelectedCameraId);

                session.NewPreviewFrame += session_NewPreviewFrame;
                session.PropertyListChanged += session_PropertyListChanged;
                session.PropertyChanged += session_PropertyChanged;
                session.ZoomChanged += session_ZoomChanged;
                session.FocusChanged += session_FocusChanged;
                session.CameraError += session_CameraError;

                RealizeButtons();
                RealizePropertyNameList();
                cbPropName.SelectedIndex = 0;
                RealizePropertyValueList();

                tbZoom.Maximum = session.Property.MaxZoom;
                int tf = 1;
                if (tbZoom.Maximum > 10)
                    tf = tbZoom.Maximum / 10;
                tbZoom.TickFrequency = tf;
                tbZoom.Value = session.Property.Zoom;
            }
        }

        void session_CameraError(PSResult err)
        {
            ShowMsg(string.Format("CameraError event: {0}\r\n",err));

            BeginInvoke(new MethodInvoker(() =>
            {
                CameraDisconnect();
            }));
        }

        void session_ZoomChanged()
        {
            ShowMsg("ZoomChanged event\r\n");

            BeginInvoke(new MethodInvoker(() =>
            {
                tbZoom.Value = session.Property.Zoom;
            }));
        }

        void session_FocusChanged()
        {
            ShowMsg("FocusChanged event\r\n");

            BeginInvoke(new MethodInvoker(() =>
            {
                ShowFocusVal();
            }));
        }

        void session_PropertyChanged(PSProp prop)
        {
            ShowMsg(string.Format(
               "PropertyChanged event:\r\n" +
               "   Property: {0}\r\n", prop));
            BeginInvoke(new MethodInvoker(() =>
            {
                Property c = cbPropName.SelectedItem as Property;

                if (c.Prop == prop)
                    RealizePropertyValueList();
            }));
        }

        void session_PropertyListChanged()
        {
            ShowMsg("PropertyListChanged event\r\n");
            BeginInvoke(new MethodInvoker(() =>
            {
                object prev = cbPropName.SelectedItem;
                if (prev == null)
                    return;
                RealizePropertyNameList();
                cbPropName.SelectedItem = prev;
                if (cbPropName.SelectedItem == null)
                    cbPropName.SelectedIndex = 0;
                RealizePropertyValueList();
            }));
        }

        void session_NewPreviewFrame()
        {
            BeginInvoke(new MethodInvoker(() =>
            {
                try
                {
                    if (session != null)
                        pbPreview.Image = session.GetPreviewFrame();
                }
                catch (PSException) { }
            }));
        }

        void session_ShootCompleted(PSFileInfo fileInfo)
        {
            BeginInvoke(new MethodInvoker(() =>
            {
                SaveFileDialog dlg = new SaveFileDialog();
                dlg.FileName = fileInfo.name;
                dlg.Filter = "jpg files (*.jpg)|*.jpg|All files (*.*)|*.*";
                dlg.RestoreDirectory = true;

                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    session.DownloadFileToAsync(fileInfo.id, dlg.FileName, 
                        true, () => {
                            ShowMsg(string.Format("Download complete: {0}\r\n",
                                dlg.FileName));
                        });
                }
            }));
        }
        
        private void RealizeButtons()
        {
            btnOpenSession.Enabled = session == null;
            btnUpdateAeAf.Enabled = session != null;
            btnEnablePreview.Enabled = session != null && 
                session.PreviewState == PSPreviewState.PS_PREVIEW_DISABLED;
            btnDisablePreview.Enabled = session != null &&
                session.PreviewState == PSPreviewState.PS_PREVIEW_ENABLED;
            btnShoot.Enabled = session != null && !isVideo;
            cbPropName.Enabled = session != null;
            cbPropVal.Enabled = session != null;
            tbZoom.Enabled = session != null;
            btnVideo.Enabled = session != null && session.VideoSupported;
            btnPowerDown.Enabled = session != null;
            RealizePropertyValueList_Enabled();
            RealizeFocusControls();
        }

        private void RealizeFocusControls()
        {
            bool enabled = false;
            bool hasProp = false;

            if (session != null)
            {
                foreach (var v in session.Property.AvailableProperty)
                {
                    if (v.Prop == PSProp.PS_ManualFocusMode)
                    {
                        enabled = v.Value.Name == "On";
                        hasProp = true;
                        break;
                    }
                }
            }

            tbFocus.Enabled = enabled;
            btnSetFocus.Enabled = enabled;
            btnSetInfinityFocus.Enabled = enabled;
            btnFocusShiftM1.Enabled = enabled;
            btnFocusShiftM5.Enabled = enabled;
            btnFocusShiftP1.Enabled = enabled;
            btnFocusShiftP5.Enabled = enabled;

            if (hasProp)
                ShowFocusVal();
        }

        private void ShowFocusVal()
        {
            int fv = session.Property.Focus;
            tbFocus.Text = fv != -1 ? fv.ToString() : "Inf";
        }

        private void btnSetFocus_Click(object sender, EventArgs e)
        {
            if (session == null)
                return;
            if (tbFocus.Text == "Inf")
                session.Property.Focus = -1;
            else
                session.Property.Focus = int.Parse(tbFocus.Text);
            ShowFocusVal();
        }

        private void btnSetInfinityFocus_Click(object sender, EventArgs e)
        {
            session.Property.Focus = -1;
            ShowFocusVal();
        }

        private void btnUpdateAeAf_Click(object sender, EventArgs e)
        {
            session.UpdateAEAF();
        }

        private void btnEnablePreview_Click(object sender, EventArgs e)
        {
            session.PreviewState = PSPreviewState.PS_PREVIEW_ENABLED;
            RealizeButtons();
        }

        private void btnDisablePreview_Click(object sender, EventArgs e)
        {
            DisablePreview();
        }

        private void DisablePreview()
        {
            session.PreviewState = PSPreviewState.PS_PREVIEW_DISABLED;
            pbPreview.Image = null;
            RealizeButtons();
        }

        private void btnShoot_Click(object sender, EventArgs e)
        {
            session.ShootAsync(session_ShootCompleted);
        }

        private void RealizePropertyNameList()
        {
            if (session == null)
                return;

            cbPropName.Items.Clear();

            foreach (var v in session.Property.AvailableProperty)
                cbPropName.Items.Add(v);
        }

        private void RealizePropertyValueList()
        {
            if (session == null)
                return;

            cbPropVal.Items.Clear();

            Property ap = cbPropName.SelectedItem as Property;

            foreach (var v in ap.AvailableValues)
            {
                cbPropVal.Items.Add(v);
            }

            cbPropVal.SelectedItem = ap.Value;
            RealizePropertyValueList_Enabled();
        }

        private void RealizePropertyValueList_Enabled()
        {
            if (session == null)
                return;

            Property ap = cbPropName.SelectedItem as Property;
            if ( ap != null )
                cbPropVal.Enabled = ap.isRW;
        }

        private void cbPropName_SelectedIndexChanged(object sender, EventArgs e)
        {
            RealizePropertyValueList();
        }

        private void cbPropVal_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (session == null)
                return;

            Property c = cbPropName.SelectedItem as Property;

            if ( !c.isRW )
                return;

            c.Value = cbPropVal.SelectedItem as ProperyValue;

            RealizeButtons();
        }

        private void tbZoom_MouseUp(object sender, MouseEventArgs e)
        {
            session.Property.Zoom = tbZoom.Value;
        }

        private void tbFocus_KeyPress(object sender, KeyPressEventArgs e)
        {
            e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
        }

        private void btnFosucShift_Click(object sender, EventArgs e)
        {
            Button s = (Button)sender;
            int shift = int.Parse((string)s.Tag);

            if (session == null)
                return;

            session.FocusShift(shift);
        }

        private void btnVideo_Click(object sender, EventArgs e)
        {
            if (!isVideo)
            {
                session.StartVideo();
                isVideo = true;
                btnVideo.Text = "Stop video";
                RealizeButtons();
            }
            else
            {
                PSFileInfo fi = session.StopVideo();
                isVideo = false;
                RealizeButtons();

                SaveFileDialog dlg = new SaveFileDialog();
                dlg.FileName = fi.name;
                dlg.Filter = "mov files (*.mov)|*.mov|All files (*.*)|*.*";
                dlg.RestoreDirectory = true;

                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    session.DownloadFileTo(fi.id, dlg.FileName, true);
                }

                btnVideo.Text = "Start video";
            }
        }

        private void btnPowerDown_Click(object sender, EventArgs e)
        {
            session.PowerDown();
        }

    }
}
