using A1560APICOM;
using System;
using System.Collections.Generic;
using System.Threading;

namespace A1560ApiTestCsharpApp
{
    class Program
    {
        //this objects provides access to parameters and commands of A1560 Device 
        private static readonly A1560 Device = new A1560();
        private static int _demoAscansReceived;
        private static int _maximumPositivePeak;

        //this demo method will set up and start the device in automatic consecutive measurements mode,
        //and will stop it after first 10 A-scans are processed. 
        //As with any other command, we rely on the fact that any exception that might occur
        //(e.g IP parsing error, device unavailability, etc) will be caught and displayed by ProgramMenuLoop code .
        //When this method is launched outside the ProgramMenuLoop - caller should take care of Exception handling.
        private static void FullCycleDemo()
        {
            //No operation possible until the connection to the device is established
            Device.Connect(Console.ReadLine());
            //Since the Device might be already running measurements we should stop it before changing settings.
            Device.Stop();
            //Some data may persist at internal 512MB buffer of the device after a previous run - let's clean it
            Device.Flush();
            //Set Device to make one measurement per second - measurements won't start immediately but only after a Start() command
            Device.TriggeringMode = 1; //triggered by internal timer
            Device.TriggeringIntervalUs = 100000; //0.1sec period between measurements
            //Subscribe dedicated processing function to be called for each new A-scan.
            //This should be done before the first measurement to prevent losing A-scans.
            Device.DataReceived += DeviceDataReceivedDemo;
            _demoAscansReceived = 0; //counter for received Ascans
            _maximumPositivePeak = int.MinValue; 
            Console.WriteLine($"Device S/N: {Device.SerialNumber}");
            Console.WriteLine($"Device variant: {Device.UnitVersion}");
            Console.WriteLine($"Firmware version: {Device.FirmwareVersion}");
            Console.WriteLine($"Protocol version: {Device.ProtocolVersion}");
            //Launch 
            Device.Start();
            //Wait
            while (_demoAscansReceived < 10) Thread.Sleep(50);
            //Stop
            Device.Stop();
            //Disconnect
            Device.Disconnect();
            //Unsubscribe
            Device.DataReceived -= DeviceDataReceivedDemo;
            Console.WriteLine($"Acquired 10 A-scans, maximum positive peak is {_maximumPositivePeak}");
        }

        //this delegate counts how many Ascans it has received from Device.DataReceived event and mimics some processing
        private static void DeviceDataReceivedDemo(AcquisitionData data)
        {

                _demoAscansReceived++;
                //what is the highest sample of ascan?
                                var maxAscanPositivePeak = int.MinValue;
                foreach (var sample in data.Vector)
                {
                    if (sample > maxAscanPositivePeak) maxAscanPositivePeak = sample;
                }
                //update absolute maximum of all 10 Ascans collected
                if (maxAscanPositivePeak > _maximumPositivePeak) _maximumPositivePeak = maxAscanPositivePeak;

        }

        //Most used Device commands are made accessible to user menu via this list.
        //All Actions are AcousticInspectionDevice methods except FullCycleDemo, which consists of several steps
        //Any method's exception  will be caught and displayed by  ProgramMenuLoop class code .
        //A caller should take care of exception handling in a real application.
        private static readonly List<MenuItem> DeviceCommands = new List<MenuItem>
        {
            new MenuItem{Description = "Get 10 A-Scans from IP", Action = FullCycleDemo, CharacterToInvoke = '1'},
            new MenuItem{Description = "Connect to the device at IP", Action = () => {Device.Connect(Console.ReadLine());}, CharacterToInvoke = '2'},
            new MenuItem{Description = "Start measurement(s)", Action = Device.Start, CharacterToInvoke = '3'},
            new MenuItem{Description = "Stop measurement(s)", Action = Device.Stop, CharacterToInvoke = '4'},
            new MenuItem{Description = "Disconnect", Action = Device.Disconnect, CharacterToInvoke = '5'},
        };


        //Most important A1560 Device API parameters are made accessible to user menu via this list.
        //Each action changes AcousticInspectionDevice property designated for the parameter in question.
        //Any property setting exception  will be caught and displayed by  ProgramMenuLoop class code .
        //User code should take care of exception handling in a real application.
        private static readonly List<MenuItem> DeviceParameters = new List<MenuItem>
        {
            new MenuItem
            {
                Description = "Samples count for each acquired vector, 1024 to 131072",
                Action = () => { Device.VectorLengthSamples = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 'l'
            },

            new MenuItem
            {
                Description = "ADC frequency, 1, 2, 5, 10, 25, 50, 100 MHz",
                Action = () => { Device.SamplingFrequencyMhz = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 'a'
            },

            new MenuItem
            {
                Description = "Triggering source (0-Manual, 1-Timer, 2- CTP msg, 3-Encoder inc, 4-Device chain slave, 5- TTL pulse)",
                Action = () => { Device.TriggeringMode = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 's'
            },

            new MenuItem
            {
                Description = "Microseconds between acquisitions when timer triggering source is used",
                Action = () => { Device.TriggeringIntervalUs = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 'm'
            },

            new MenuItem
            {
                Description = "Gain at the input, -20 to 80 dB",
                Action = () => { Device.GainDb = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 'g'
            },

            new MenuItem
            {
                Description = "Hi-Pass filter number at the input, 0 to 3",
                Action = () => { Device.InputFilterNumber = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 'h'
            },

            new MenuItem
            {
                Description = "Pulse voltage, 20, 100 or 200 Volts",
                Action = () => { Device.PulseVoltage = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 'v'
            },

            new MenuItem
            {
                Description = "Frequency of the pulse, kHz",
                Action = () => { Device.BurstFrequencyKhz = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 'f'
            },

            new MenuItem
            {
                Description = "Period of the pulse, nS",
                Action = () => { Device.BurstPeriodNs = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 'p'
            },


            new MenuItem
            {
                Description = "Duration of the pulse burst in half-cycles",
                Action = () => { Device.BurstLengthNumber = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 'd'
            },

            new MenuItem
            {
                Description = "Transducer type (0-Dual, 1-Single)",
                Action = () => { Device.TransducerType = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 't'
            },

            new MenuItem
            {
                Description = "Averaging factor",
                Action = () => { Device.AveragingFactor = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 'q'
            },

            new MenuItem
            {
                Description = "Averaging acquisition delay constant part, ns",
                Action = () => { Device.AveragingPeriodNs = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 'c'
            },

            new MenuItem
            {
                Description = "Averaging acquisition delay random part, ns",
                Action = () => { Device.AveragingRandomValueNs = int.Parse(Console.ReadLine()); },
                CharacterToInvoke = 'r'
            },


            new MenuItem
            {
                Description = "Store new device IP",
                Action = () => {Device.SetNewIp(Console.ReadLine());},
                CharacterToInvoke = 'i'
            },



        };

        //Displays the DeviceCommands and DeviceParameters as a text menu and executes and calls methods/property setters on a user's command
        //Handles (displays) all exceptions which may and will occur.
        private static readonly ProgramMenuLoop ProgramMenuLoop = new ProgramMenuLoop(DeviceCommands, DeviceParameters);


        public static void Main(string[] args)
        {
            //Device_DataReceived() will be called on each Device.DataReceived event
            //(i.e upon receive of every new A-Scan from the Device)
            Device.DataReceived += DeviceDataReceived; 
            ProgramMenuLoop.RunMainMenuLoop();
           //User pressed ESC in main menu, prepare to quit
           Device.DataReceived -= DeviceDataReceived;
           Device.Disconnect();
        }


        //Show some info about the A-Scan we've just acquired
        private static void DeviceDataReceived(AcquisitionData data)
        {

            var aScanInfo = $"Last ascan CTP: {data.Ctp[0]:X04}:{data.Ctp[1]:X04}:{data.Ctp[2]:X04}  Packet: {data.Id} Length: {data.Vector.Length} samples";
            lock (ProgramMenuLoop.ConsoleLock)
            {
                var consoleTop = Console.CursorTop;
                var consoleLeft = Console.CursorLeft;
                Console.CursorTop = Console.WindowTop + Console.WindowHeight - 1;
                Console.CursorLeft = 0;
                Console.Write(aScanInfo);
                Console.CursorLeft = consoleLeft;
                Console.CursorTop = consoleTop;
            }
        }
    }
}
