using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using Emgu.CV;
using Emgu.CV.Structure;
using System.Net;

namespace jPicoFishfinder1._0
{

    public partial class Form1 : Form
    {
        //colorbar for GPS version
        double[] m_dLevel = { 1000, 900, 850, 800, 750, 700, 650 };
        
       
        
        //fishfinder 
        private Color[] colorbar = new Color[8] { Color.Navy, Color.Blue, Color.Aqua, Color.LimeGreen, Color.Yellow, Color.Orange, Color.Red, Color.DarkRed };
  
        Thread m_threadcvImg;
        int m_cvImgStart = 0;
        Point m_pLetfTop;
        Point m_pRightBottom;
        Bitmap m_cvBmp;
        bool m_bOdd=true;

        //1.1.1
        short[] m_sPingData;

        //AD 2 SV
        double m_dSpeed ;// Double.Parse(txtSpeed.Text);

        double m_dSR ;// Double.Parse(txtSampRate.Text);

        double m_dAbsorb;// Double.Parse(txtAbsorb.Text);

        double m_dTau;// Double.Parse(txtPulseW.Text);

        double m_dVar; // m_dVar = 10 * Math.Log10(0.5 * m_dSpeed * m_dTau * m_dPhi) - m_dK;


        //pico below
        private readonly short _handle;
        public const int BUFFER_SIZE = 800;   //sample rage ==20k, set the range here, 1 sample ==0.0375m, 1m==26.7samples  (for 1500m/s); so (30m:800samples, 40m:1068samples) //20150323 
        public const int MAX_CHANNELS = 4;
        public const int QUAD_SCOPE = 4;
        public const int DUAL_SCOPE = 2;

        uint _timebase=1001;
        int _timeInterval;
        bool _logFlag = false;
        bool _saveFlag = false;

        short _oversample = 1;
        bool _scaleVoltages = true;

        ushort[] inputRanges = { 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000 };
        bool _ready = false;
        short _trig = 0;
        uint _trigAt = 0;
        int _sampleCount = 0;
        uint _startIndex = 0;
        bool _autoStop;
        private ChannelSettings[] _channelSettings;
        private int _channelCount;
        private Imports.Range _firstRange;
        private Imports.Range _lastRange;
        private Imports.ps4000BlockReady _callbackDelegate;
        long second = 10;

        Thread m_threadLoggin;

        public Form1(short handle)
        {
            _handle = handle;
            InitializeComponent();
            GetDeviceInfo();
            pictureBox_echograph.BackColor = Color.DarkBlue;
      //      m_cvBmp = new Bitmap(pictureBox_echograph.Width, pictureBox_echograph.Height);
        }

        /****************************************************************************
         * Initialise unit' structure with Variant specific defaults
         ****************************************************************************/
        void GetDeviceInfo()
        {
            int variant = 0;
            string[] description = {
                           "Driver Version    ",
                           "USB Version       ",
                           "Hardware Version  ",
                           "Variant Info      ",
                           "Serial            ",
                           "Cal Date          ",
                           "Kernel Ver        "
                         };
            System.Text.StringBuilder line = new System.Text.StringBuilder(80);

            if (_handle >= 0)
            {
                string srLable2="";
                textBox1.Clear();
                for (int i = 0; i < 7; i++)
                {
                    short requiredSize;
                    Imports.GetUnitInfo(_handle, line, 80, out requiredSize, i);
                    if (i == 3)
                    {
                        variant = Convert.ToInt16(line.ToString());
                    }
                    srLable2 = String.Format("{0}: {1}", description[i], line);
                    srLable2 = srLable2 + "\r\n";
                    textBox1.AppendText(srLable2);
                }
                srLable2 = String.Format("Sampling Rate     : {0} ksps", 1000000/(50*(_timebase-1)));
                textBox1.AppendText(srLable2);

                switch (variant)
                {
                    case (int)Imports.Model.PS4223:
                        _firstRange = Imports.Range.Range_50MV;
                        _lastRange = Imports.Range.Range_100V;
                        _channelCount = DUAL_SCOPE;
                        break;

                    case (int)Imports.Model.PS4224:
                        _firstRange = Imports.Range.Range_50MV;
                        _lastRange = Imports.Range.Range_20V;
                        _channelCount = DUAL_SCOPE;
                        break;

                    case (int)Imports.Model.PS4423:
                        _firstRange = Imports.Range.Range_50MV;
                        _lastRange = Imports.Range.Range_100V;
                        _channelCount = QUAD_SCOPE;
                        break;

                    case (int)Imports.Model.PS4424:
                        _firstRange = Imports.Range.Range_50MV;
                        _lastRange = Imports.Range.Range_20V;
                        _channelCount = QUAD_SCOPE;
                        break;

                    case (int)Imports.Model.PS4226:
                        _firstRange = Imports.Range.Range_50MV;
                        _lastRange = Imports.Range.Range_20V;
                        _channelCount = DUAL_SCOPE;
                        break;

                    case (int)Imports.Model.PS4227:
                        _firstRange = Imports.Range.Range_50MV;
                        _lastRange = Imports.Range.Range_20V;
                        _channelCount = DUAL_SCOPE;
                        break;

                    case (int)Imports.Model.PS4262:
                        _firstRange = Imports.Range.Range_10MV;
                        _lastRange = Imports.Range.Range_20V;
                        _channelCount = DUAL_SCOPE;
                        break;
                }
            }
        }

        /****************************************************************************
         * Callback
         * used by PS4000 data block collection calls, on receipt of data.
         * used to set global flags etc checked by user routines
         ****************************************************************************/
        void BlockCallback(short handle, short status, IntPtr pVoid)
        {
            // flag to say done reading data
            _ready = true;
        }

        /****************************************************************************
         * SetDefaults - restore default settings
         ****************************************************************************/
        void SetDefaults()
        {
            for (int i = 0; i < _channelCount; i++) // reset channels to most recent settings
            {
                Imports.SetChannel(_handle, Imports.Channel.ChannelA + i,
                                   (short)(_channelSettings[(int)(Imports.Channel.ChannelA + i)].enabled ? 1 : 0),
                                   (short)(_channelSettings[(int)(Imports.Channel.ChannelA + i)].DCcoupled ? 1 : 0),
                                   _channelSettings[(int)(Imports.Channel.ChannelA + i)].range);
            }
        }

        /****************************************************************************
         *
         * Select _timebase, set _oversample to on and time units as nano seconds
         *
         ****************************************************************************/
        void SetTimebase(uint timebase)
        {
            _timebase = timebase;
            int maxSamples;

            while (Imports.GetTimebase(_handle, _timebase, BUFFER_SIZE, out _timeInterval, 1, out maxSamples, 0) != 0)
            {
                _timebase++;
            }
            _oversample = 1;
        }

        /****************************************************************************
        *  SetTrigger
        *  this function sets all the required trigger parameters, and calls the 
        *  triggering functions
        ****************************************************************************/
        short SetTrigger(Imports.TriggerChannelProperties[] channelProperties, short nChannelProperties, Imports.TriggerConditions[] triggerConditions, short nTriggerConditions, Imports.ThresholdDirection[] directions, Pwq pwq, uint delay, short auxOutputEnabled, int autoTriggerMs)
        {
            short status;

            if (
              (status =
               Imports.SetTriggerChannelProperties(_handle, channelProperties, nChannelProperties, auxOutputEnabled,
                                                   autoTriggerMs)) != 0)
            {
                return status;
            }

            if ((status = Imports.SetTriggerChannelConditions(_handle, triggerConditions, nTriggerConditions)) != 0)
            {
                return status;
            }

            if (directions == null) directions = new Imports.ThresholdDirection[] { Imports.ThresholdDirection.None, 
        Imports.ThresholdDirection.None, Imports.ThresholdDirection.None, Imports.ThresholdDirection.None, 
        Imports.ThresholdDirection.None, Imports.ThresholdDirection.None};

            if ((status = Imports.SetTriggerChannelDirections(_handle,
                                                              directions[(int)Imports.Channel.ChannelA],
                                                              directions[(int)Imports.Channel.ChannelB],
                                                              directions[(int)Imports.Channel.ChannelC],
                                                              directions[(int)Imports.Channel.ChannelD],
                                                              directions[(int)Imports.Channel.External],
                                                              directions[(int)Imports.Channel.Aux])) != 0)
            {
                return status;
            }

            if ((status = Imports.SetTriggerDelay(_handle, delay)) != 0)
            {
                return status;
            }

            if (pwq == null) pwq = new Pwq(null, 0, Imports.ThresholdDirection.None, 0, 0, Imports.PulseWidthType.None);

            status = Imports.SetPulseWidthQualifier(_handle, pwq.conditions,
                                                    pwq.nConditions, pwq.direction,
                                                    pwq.lower, pwq.upper, pwq.type);

            return status;
        }


        /****************************************************************************
         * adc_to_mv
         *
         * Convert an 16-bit ADC count into millivolts
         ****************************************************************************/
        int adc_to_mv(int raw, int ch)
        {
            return (raw * inputRanges[ch]) / Imports.MaxValue;
        }

        /****************************************************************************
         * mv_to_adc
         *
         * Convert a millivolt value into a 16-bit ADC count
         *
         *  (useful for setting trigger thresholds)
         ****************************************************************************/
        short mv_to_adc(short mv, short ch)
        {
            return (short)((mv * Imports.MaxValue) / inputRanges[ch]);
        }

        /****************************************************************************
       * CollectBlockTriggered
       *  this function demonstrates how to collect a single block of data from the
       *  unit, when a trigger event occurs.
       ****************************************************************************/
        void SetBlockTrigger()
        {
            short triggerVoltage = mv_to_adc(1000, (short)_channelSettings[(int)Imports.Channel.ChannelA].range); // ChannelInfo stores ADC counts
            Imports.TriggerChannelProperties[] sourceDetails = new Imports.TriggerChannelProperties[] {
        new Imports.TriggerChannelProperties(triggerVoltage,
                                             256*10,
                                             triggerVoltage,
                                             256*10,
                                             Imports.Channel.ChannelA,
                                             Imports.ThresholdMode.Level)};

            Imports.TriggerConditions[] conditions = new Imports.TriggerConditions[] {
              new Imports.TriggerConditions(Imports.TriggerState.True,
                                            Imports.TriggerState.DontCare,
                                            Imports.TriggerState.DontCare,
                                            Imports.TriggerState.DontCare,
                                            Imports.TriggerState.DontCare,
                                            Imports.TriggerState.DontCare,
                                            Imports.TriggerState.DontCare)};

            Imports.ThresholdDirection[] directions = new Imports.ThresholdDirection[]
	                                        { Imports.ThresholdDirection.Rising,
                                            Imports.ThresholdDirection.None, 
                                            Imports.ThresholdDirection.None, 
                                            Imports.ThresholdDirection.None, 
                                            Imports.ThresholdDirection.None,
                                            Imports.ThresholdDirection.None };
            SetDefaults();
            /* Trigger enabled
             * Rising edge
             * Threshold = 1000mV */
            SetTrigger(sourceDetails, 1, conditions, 1, directions, null, 0, 0, 0);  //4. SetTrigger
        }

        /****************************************************************************
        * DataLoggin - Logging Data to file
        ****************************************************************************/
        public void DataLoggin()
        {
            // setup devices
           
 //           _timebase = 1;

            _channelSettings = new ChannelSettings[MAX_CHANNELS];


            uint sampleCount = BUFFER_SIZE;
            PinnedArray<short>[] bufPinned = new PinnedArray<short>[1];

            int timeIndisposed;

            for (int i = 0; i < MAX_CHANNELS; i++)
            {
                _channelSettings[i].enabled = true;
                _channelSettings[i].DCcoupled = true;
                _channelSettings[i].range = Imports.Range.Range_5V;
            }

            SetDefaults(); //2. Select channel ranges and AC/DC coupling

            SetTimebase(1001); //3. Select timebase until the required nanoseconds per sample is located

            SetBlockTrigger();  //4. Use the trigger setup functions to set up the trigger


            short[] buffers = new short[sampleCount];
            bufPinned[0] = new PinnedArray<short>(buffers);
            int status = Imports.SetDataBuffer(_handle, (Imports.Channel)1, buffers, (int)sampleCount);//7. tell the driver where your memory buffer is

            if (!Directory.Exists("C:\\FishloggerData"))
                Directory.CreateDirectory("C:\\FishloggerData");
            TextWriter writer = new StreamWriter("C:\\FishloggerData\\" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".csv", false); //hh 12hour; HH 24hour

            //1.1 PingData
            m_sPingData = new short[sampleCount];

            double dLogData;
            double dShowData;

            while (_logFlag == true)
            {
                _ready = false;
                _callbackDelegate = BlockCallback;
                //5. Start the oscilloscope running
                Imports.RunBlock(_handle, 0, (int)sampleCount, _timebase, _oversample, out timeIndisposed, 0, _callbackDelegate, IntPtr.Zero);

                while (!_ready) //6. wait until the oscilloscope is ready using the ps4000BlockReady Callback
                {
                    Thread.Sleep(100);
                }

                //***********write data to file******************    
                if (_ready)   //
                {
                    short overflow;
                    Imports.GetValues(_handle, 0, ref sampleCount, 1, Imports.DownSamplingMode.None, 0, out overflow);  //8. Transfer the block of data from the ocilloscope
                    writer.Write(DateTime.Now.ToString("yyyyMMdd_HH:mm:ss,"));
                    for (int i = 0; i < sampleCount; i++)  //9 handle the data 
                    {

                        dLogData = adc_to_mv(bufPinned[0].Target[i], (short)_channelSettings[(int)Imports.Channel.ChannelB].range); //voltage 
                      //  dLogData = bufPinned[0].Target[i];      //AD

                        dShowData = dLogData;

                        writer.Write("{0},", dLogData);


                        if (dShowData > m_dLevel[0])
                            m_sPingData[i] = 7;
                        else if (dShowData > m_dLevel[1])
                            m_sPingData[i] = 6;
                        else if (dShowData > m_dLevel[2])
                            m_sPingData[i] = 5;
                        else if (dShowData > m_dLevel[3])
                            m_sPingData[i] = 4;
                        else if (dShowData > m_dLevel[4])
                            m_sPingData[i] = 3;
                        else if (dShowData > m_dLevel[5])
                            m_sPingData[i] = 2;
                        else if (dShowData > m_dLevel[6])
                            m_sPingData[i] = 1;
                        else
                            m_sPingData[i] = 0;

                    }
                    writer.WriteLine();
                    
                    //draw one Ping
          
                    //************************
                
                        //draw by Main thread using Invoker
                        MethodInvoker mi = new MethodInvoker(this.drawEchograph); 
                        this.BeginInvoke(mi);
                  
                    //***************/
                    
                }
                //***************************************************/
            }                      //10. repeat steps 5-9 

            _logFlag = false;
            Imports.Stop(_handle); //11.Stop the oscilloscope
            writer.Close();
            foreach (PinnedArray<short> p in bufPinned)
            {
                if (p != null)
                    p.Dispose();
            }
            _saveFlag = true;

        }

        private void btnDataLoggin_Click(object sender, EventArgs e)
        {
            timer1.Enabled = false;

            second=0;
            label1.Text = new DateTime(second * 10000000).ToLongTimeString();
            
            _logFlag = true;
            btnDataLoggin.Enabled = false;
            btnStopLoggin.Enabled = true;
            m_threadLoggin = new Thread(new ThreadStart(DataLoggin));
            m_threadLoggin.Start();
            timer2.Enabled = true;
            label3.Text = "Data is logging... ... ...";
        }

        private void btnStopLoggin_Click(object sender, EventArgs e)
        {
            timer2.Enabled = false; //no auto logging anymore

            m_threadLoggin.Suspend();
            _logFlag = false;
            btnStopLoggin.Enabled = false;
            btnDataLoggin.Enabled = true;
            m_threadLoggin.Resume();
            label3.Text = "Data logging Over";
            
        }

        private void timer1_Tick(object sender, EventArgs e) //one second interval
        {
            second--;
            label1.Text = new DateTime(second*10000000).ToLongTimeString();
            
            if (second == 0) 
            {
                timer1.Enabled = false;

                _logFlag = true;
                btnDataLoggin.Enabled = false;
                btnStopLoggin.Enabled = true;
                m_threadLoggin = new Thread(new ThreadStart(DataLoggin));
                m_threadLoggin.Start();
                timer2.Enabled = true;
                label3.Text = "Data is logging... ... ...";
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (MessageBox.Show("Sure to Stop Logging before closing the Application", "Attention", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
            {

            }
            else e.Cancel=true;
        }

        private void timer2_Tick(object sender, EventArgs e) //one hour interval auto logging file
        {
            //*************Close current file first***********
            m_threadLoggin.Suspend();
            _logFlag = false;
            btnStopLoggin.Enabled = false;
            btnDataLoggin.Enabled = true;
            m_threadLoggin.Resume();
            label3.Text = "Data logging Over";

            //*************New file, restart thread**************
            while (_saveFlag == false);  //wait for the terminating of current m_threadLogging
            _saveFlag = false; //clear;
            _logFlag = true;
            btnDataLoggin.Enabled = false;
            btnStopLoggin.Enabled = true;
            m_threadLoggin = new Thread(new ThreadStart(DataLoggin));
            m_threadLoggin.Start();
            label3.Text = "Data is logging... ... ...";
        }


        //jPicoFishfinder 1.1 add functions as below
        private void drawEchograph()
        {
            uint isamples = BUFFER_SIZE;

            Bitmap bitmap = new Bitmap(this.pictureBox_echograph.Width, this.pictureBox_echograph.Height);
            Graphics gEcho = Graphics.FromImage(bitmap);   //this is like doublebuffer, draw on the bitmap, and show on the picTureBox

            float y = pictureBox_echograph.Height / (float)isamples;
            float x = this.pictureBox_echograph.Width - 1;// gEcho.PageScale;
            //            int x =1311; //(840*3.125/2)

            if (this.pictureBox_echograph.Image == null)
            {
                for (int i = 0; i < isamples; i++)
                    gEcho.DrawLine(new Pen(colorbar[m_sPingData[i]]), x, i * y, x, (i + 1) * y);
            }
            else
            {
                gEcho.DrawImage(this.pictureBox_echograph.Image, -1, 0); //draw image from current picturebox to a new position DrawImage(image,x,y),==translate 
                //              gEcho.DrawImage(this.pictureBox_echograph.Image, new Rectangle(0, 0, pictureBox_echograph.Width-1, pictureBox_echograph.Height), new Rectangle(1, 0, this.pictureBox_echograph.Image.Width-1, this.pictureBox_echograph.Image.Height), GraphicsUnit.Pixel);
                for (int i = 0; i < isamples; i++)
                    gEcho.DrawLine(new Pen(colorbar[m_sPingData[i]]), x, i * y, x, (i + 1) * y);
            }
            //          DrawDepthLable(gEcho, irange);  // draw depth label
            this.pictureBox_echograph.Image = bitmap; //update to the picBox,   

            gEcho.Dispose();

        }
        
    }
}
