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 System.IO;

using Generator;
using USB_Comunication;

namespace USB_interface
{
    public partial class MyGenForm : Form
    {
        private USB_Device USBdev = null;

        // konstruktor
        public MyGenForm()
        {
            // inicializace komponent
            InitializeComponent();

            // vytvoreni generatoru funkci
            waveform = new GenWaveform();

            // vypocet moznych frekvencnich kroku
            waveform.OtherTones = ToneTable.GenerateTones((uint)sample, 256, 1024, out waveform.SineTones);
            waveform.Tones = waveform.SineTones;
            //waveform.Tones = ToneTable.GenerateTones((uint)sample, 256, 1024);
            

            // inicializace generatoru
            textBox4.Text = mult.ToString();

            this.trackBar2.Maximum = waveform.Tones.Count - 1;

            this.trackBar1.Value = 0;
            this.trackBar2.Value = 0;
            this.trackBar3.Value = 0;

            this.label14.Visible = false;

            this.trackBar1_Scroll(null, null);
            this.trackBar2_Scroll(null, null);
            this.trackBar3_Scroll(null, null);

            this.button8.Enabled = false;

            // udalost reakce na enter v textboxech
            textBox12.KeyDown += textBox12_kd;
            textBox4.KeyDown += textBox4_kd;
        }

        // uprava UI pri pripojeni k USB generatoru
        public void DeviceOpened(object sender, string name)
        {
            this.textBox2.Text = name;
            this.label13.Visible = false;
            this.label14.Visible = true;

            this.USBdev = (sender as USB_Device);

            this.textBox8.Text = USBdev.frequency.ToString();
            this.textBox9.Text = USBdev.bitDepth.ToString();
            this.textBox10.Text = USBdev.devider.ToString();
            this.textBox11.Text = USBdev.memorySize.ToString();

            // zmena frekvencnich kroku dle moznosti generatoru
            waveform.Tones.Clear();
            waveform.OtherTones = ToneTable.GenerateTones(USBdev.frequency, USBdev.memorySize, USBdev.devider, out waveform.SineTones);

            if(self == SelectedForm.sine)
            {
                waveform.Tones = waveform.SineTones;
            }
            else
            {
                waveform.Tones = waveform.OtherTones;
            }

            this.trackBar2.Maximum = waveform.Tones.Count - 1;
 
            this.sample = USBdev.frequency;
            this.button8.Enabled = true;

            this.panel3.Refresh();
        }

        // uprava UI při odpojení generatoru
        public void DeviceClosed(object sender, object o)
        {
            this.button8.Enabled = false;
            this.textBox2.Text = "";
            this.label13.Visible = true;
            this.label14.Visible = false;
        }

        // zmena velikosti panelu pri zmene velikosti hlavniho okna
        public void SetSize(Size s)
        {
            this.Size = s;
            ReDraw();
        }
 
        // vzorkovaci frekvence
        private double sample = 100000;
        // aktualni frekvence
        private double freq = 1000;
        // nasobic
        private double mult = 1;
        // amplituda
        private int amplitude = 127;
        // napetovi offset
        private int offset = 0;
        // generator signalu a frekvencnich kroku
        private GenWaveform waveform = null;

        
        // vykresleni panelu simulace signalu
        private void panel3_Paint(object sender, PaintEventArgs e)
        {
            //if (USBdev == null) return;

            int WinHeight = this.panel3.Height / 2 - 2;
            double ratio = WinHeight / (double)trackBar1.Maximum * 1.98;
            var p = sender as Panel;
            var g = e.Graphics;

            Point[] points = new Point[4];

            points[0] = new Point(0, 0);
            points[1] = new Point(0, p.Height);
            points[2] = new Point(p.Width, p.Height);
            points[3] = new Point(p.Width, 0);

            Brush brush = new SolidBrush(Color.White);

            g.FillPolygon(brush, points);

            Point[] pnt = new Point[2000];
            Point[] pntda = new Point[2 * pnt.Length];

			for (int i = 0; i < pnt.Length; i++)
			{
				pnt[i].X = 15 + (int)(i * waveform.Tones[trackBar2.Value].Devider * mult);
                pnt[i].Y = WinHeight + (int)(-1 * (offset + (waveform.Sequence[i % waveform.Sequence.Count] - trackBar1.Maximum / 2)) * ratio);
			}

            int j = 0;
            for (int i = 0; i < pnt.Length; i++)
            {
                j = 2 * i;
                pntda[j].X = 15 + (int)(i * waveform.Tones[trackBar2.Value].Devider * mult);
                pntda[j].Y = WinHeight + (int)(-1 * (offset + (waveform.Sequence[i % waveform.Sequence.Count] - trackBar1.Maximum / 2)) * ratio);

                pntda[j + 1].X = 15 + (int)(i * waveform.Tones[trackBar2.Value].Devider * mult);
                pntda[j + 1].Y = WinHeight + (int)(-1 * (offset + (waveform.Sequence[(i + 1) % waveform.Sequence.Count] - trackBar1.Maximum / 2)) * ratio);
            }

            if (mult > 1)
            {
                for (int i = 0; i < pnt.Length; i++)
                {
                    g.DrawLine(Pens.Silver, new Point((int)(15 + mult * i), 0), new Point((int)(15 + mult * i), 2 * WinHeight));
                }
            }

            g.DrawLine(Pens.Silver, new Point((int)(sample / freq * mult * waveform.Tones[trackBar2.Value].Multipler) + 15, 0), new Point((int)(sample / freq * mult * waveform.Tones[trackBar2.Value].Multipler) + 15, this.Width));
            g.DrawLine(Pens.Silver, new Point(15, 0), new Point(15, this.Width));
            g.DrawLine(Pens.Silver, new Point(0, WinHeight), new Point(pnt.Length, WinHeight));
            g.DrawLine(Pens.Red, new Point(15, WinHeight), new Point((int)(sample / freq * mult * waveform.Tones[trackBar2.Value].Multipler) + 15, WinHeight));

            if (AproxBoxChecked) g.DrawLines(Pens.Black, pnt);

            if (DABoxChecked) g.DrawLines(Pens.Green, pntda);
        }

        // vykresleni panelu pro uzivatelsky definovany signal
        private void panel5_Paint(object sender, PaintEventArgs e)
        {
            setSizes();

            e.Graphics.DrawLine(Pens.Green, 0, Middle + DrawPoint.PointSize.Height / 2, panel5.Width, Middle + DrawPoint.PointSize.Height / 2);

            e.Graphics.DrawLine(Pens.Silver, 0, Middle / 2 + DrawPoint.PointSize.Height / 2, panel5.Width, Middle / 2 + DrawPoint.PointSize.Height / 2);
            e.Graphics.DrawLine(Pens.Silver, 0, Middle + Middle / 2 + DrawPoint.PointSize.Height / 2, panel5.Width, Middle + Middle / 2 + DrawPoint.PointSize.Height / 2);

            e.Graphics.DrawLine(Pens.Silver, 0, Middle / 4 + DrawPoint.PointSize.Height / 2, panel5.Width, Middle / 4 + DrawPoint.PointSize.Height / 2);
            e.Graphics.DrawLine(Pens.Silver, 0, Middle + Middle / 4 + DrawPoint.PointSize.Height / 2, panel5.Width, Middle + Middle / 4 + DrawPoint.PointSize.Height / 2);

            e.Graphics.DrawLine(Pens.Silver, 0, Middle * 3 / 4 + DrawPoint.PointSize.Height / 2, panel5.Width, Middle * 3 / 4 + DrawPoint.PointSize.Height / 2);
            e.Graphics.DrawLine(Pens.Silver, 0, Middle + Middle * 3 / 4 + DrawPoint.PointSize.Height / 2, panel5.Width, Middle + Middle * 3 / 4 + DrawPoint.PointSize.Height / 2);


            if (DPoints.Count < 4 || DrawPoint.Down)
                return;

            // change of points positions
            int m = (int)Math.Round((double)panel5.Width / DPoints.Count);

            for (int i = 0; i < DPoints.Count; i++)
            {
                DPoints[i].Top = (int)Math.Round(Middle + PPoints[i] * Middle * (-1));
                DPoints[i].Left = m * i;
            }

            // draw lines between points
            Point[] pts = new Point[DPoints.Count + 1];
            for (int i = 0; i < DPoints.Count; i++)
            {
                pts[i] = new Point(DPoints[i].Left + 4, DPoints[i].Top + 4);
            }

            pts[DPoints.Count] = new Point(this.panel5.Width + 4, DPoints[0].Top + 4);
            e.Graphics.DrawLines(Pens.Red, pts);

        }


        // tlacitko aktualizovat
        private void button2_Click(object sender, EventArgs e)
        {
            ReDraw();
        }

        // TRACKBAR OF FREQUENCY
        private void trackBar2_Scroll(object sender, EventArgs e)
        {
            textBox1.Text = Math.Round(waveform.Tones[trackBar2.Value].Freq, 2).ToString();
            generate(trackBar2.Value, trackBar1.Value, (int)sample);
        }

        // decrement
        private void button1_Click(object sender, EventArgs e)
        {
            if (trackBar2.Value > 0)
            {
                trackBar2.Value--;
                trackBar2_Scroll(null, null);
            }
        }

        // increment
        private void button3_Click(object sender, EventArgs e)
        {
            if (trackBar2.Value < trackBar2.Maximum)
            {
                trackBar2.Value++;
                trackBar2_Scroll(null, null);
            }
        }

        // TRACKBAR OF AMPLITUDE
        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            textBox6.Text = ((double)trackBar1.Value / 1000).ToString();
            amplitude = trackBar1.Value;
            generate(trackBar2.Value, trackBar1.Value, (int)sample);
        }

        // decrement
        private void button5_Click(object sender, EventArgs e)
        {
            if (trackBar1.Value > 0)
            {
                trackBar1.Value--;
                trackBar1_Scroll(null, null);
            }
        }

        // increment
        private void button4_Click(object sender, EventArgs e)
        {
            if (trackBar1.Value < trackBar1.Maximum)
            {
                trackBar1.Value++;
                trackBar1_Scroll(null, null);
            }
        }

        // TRACKBAR OF OFFSET
        private void trackBar3_Scroll(object sender, EventArgs e)
        {
            textBox7.Text = ((double)trackBar3.Value / 1000).ToString();
            offset = trackBar3.Value;
            ReDraw();
        }

        // decrement
        private void button7_Click(object sender, EventArgs e)
        {
            if (trackBar3.Value > 0)
            {
                trackBar3.Value--;
                trackBar3_Scroll(null, null);
            }
        }

        // increment
        private void button6_Click(object sender, EventArgs e)
        {
            if (trackBar3.Value < trackBar3.Maximum)
            {
                trackBar3.Value++;
                trackBar3_Scroll(null, null);
            }

        }

        // WAVEFORM CHANGES
        public void radioButton_CheckedChanged(object sender, SelectedForm sf)
        {
            self = sf;
            double val = double.Parse(textBox1.Text); 


            if (lastf == SelectedForm.sine && self != SelectedForm.sine)
            {
                waveform.Tones = waveform.OtherTones;
            }

            if (lastf != SelectedForm.sine && self == SelectedForm.sine)
            {
                waveform.Tones = waveform.SineTones;
            }

            trackBar2.Maximum = waveform.Tones.Count - 1;
            FindFreqValue(val);


            if(self == SelectedForm.user)
            {
                panel3.Visible = false;
                panel5.Visible = true;
            }
            else
            {
                panel3.Visible = true;
                panel5.Visible = false;
                generate(trackBar2.Value, trackBar1.Value, (int)sample);
            }

            lastf = self;
        }


        // start generating
        /*
            bufferlength    - uint16
            multiplier      - uint16
            buffer          - int16 value(0 - 4095)
        */
        private void button8_Click(object sender, EventArgs e)
        {
            USBdev.SendToneBuffer(waveform.Tones[trackBar2.Value].Devider, (uint)waveform.Sequence.Count, (uint)trackBar3.Value + 2047, waveform.Sequence.ToArray());
        }



        // prekresleni panelu
        public void ReDraw()
        {
            try
            {
                freq = double.Parse(textBox1.Text);
                mult = int.Parse(textBox4.Text);
                this.Refresh();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Špatně zadaný vstup.");
            }
            textBox3.Text = waveform.Tones[trackBar2.Value].SamplesPerT.ToString();
            textBox5.Text = waveform.Tones[trackBar2.Value].Devider.ToString();
            textBox14.Text = waveform.Tones[trackBar2.Value].Multipler.ToString();
        }


        private void generate(int index, int amplitude, int sampleFreq)
        {
            switch(self)
            {
                case SelectedForm.sine:
                    waveform.GenerateSine(index, amplitude, sampleFreq, trackBar1.Maximum);
                    break;
                case SelectedForm.sawUp:
                    waveform.GenerateSawUp(index, amplitude, sampleFreq, trackBar1.Maximum);
                    break;
                case SelectedForm.sawDown:
                    waveform.GenerateSawDown(index, amplitude, sampleFreq, trackBar1.Maximum);
                    break;
                case SelectedForm.triangle:
                    waveform.GenerateTriangle(index, amplitude, sampleFreq, trackBar1.Maximum);
                    break;
                case SelectedForm.rectangle:
                    waveform.GenerateRectangle(index, amplitude, sampleFreq, trackBar1.Maximum);
                    break;
                case SelectedForm.user:
                    try
                    {
                        waveform.GenerateUserDefined(index, amplitude, sampleFreq, trackBar1.Maximum, PPoints.ToArray());
                    }
                    catch(Exception e)
                    {
                        MessageBox.Show(e.Message, "Error");
                        trackBar2.Value = 0;
                        trackBar2_Scroll(this, null);
                    }
                    break;
            }
            ReDraw();
        }


        public enum SelectedForm
        {
            sine, triangle, sawUp, sawDown, rectangle, user
        }
        
        private static SelectedForm self = SelectedForm.sine;
        private static SelectedForm lastf = SelectedForm.sine;

        public void ShowCheckedChanged(object sender, bool[] e)
        {
            AproxBoxChecked = e[0];
            DABoxChecked = e[1];
            ReDraw();
        }

        public void ModeCheckedChanged(object sender, bool[] e)
        {
            this.MirrorMode1 = e[0];
            this.MirrorMode2 = e[1];
        }

        private bool DABoxChecked = false;
        private bool AproxBoxChecked = false;


        private List<DrawPoint> DPoints = new List<DrawPoint>();
        private List<double> PPoints = new List<double>();

        public int Middle = 0;
        public int Max = 0;


        // vytvoreni bodu v panelu pro uzivatelsky definovany signal
        public void CreatePoints(object sender, int n)
        {
            setSizes();

            foreach (DrawPoint p in DPoints)
            {
                p.Dispose();
            }

            DPoints.Clear();
            PPoints.Clear();

            DrawPoint tmp;
            int m = (int)Math.Round((double)panel5.Width / n);

            for (int i = 0; i < n; i++)
            {
                tmp = new DrawPoint();
                tmp.Cursor = System.Windows.Forms.Cursors.Cross;
                tmp.Location = new System.Drawing.Point(i * m, Middle);
                tmp.Name = "point" + i.ToString();
                tmp.Size = DrawPoint.PointSize;
                tmp.TabIndex = 0;
                tmp.Text = "point" + i.ToString();
                tmp.UseVisualStyleBackColor = true;
                tmp.Parent = panel5;
                tmp.MyForm = this;

                DPoints.Add(tmp);
                PPoints.Add(0.0);
            }
            // posledni bod pridan pro vypocet == prvnimu bodu
            PPoints.Add(0.0);

            panel5.Refresh();
        }

        // nastaveni pomernnych velikosti panelu pro vypocet okamzite amplitudy bodu
        private void setSizes()
        {
            Max = panel5.Height - DrawPoint.PointSize.Height - 6;
            if ((Max % 2) == 1)
                Max--;
            Middle = Max / 2;
        }

        public bool MirrorMode1 = false;
        public bool MirrorMode2 = false;

        // prepocet signalu pri zmene polohy bodu
        public void PointPosChanged(DrawPoint actPoint)
        {
            if(MirrorMode1)
            {
                int index = DPoints.IndexOf(actPoint);
                int indexToChange = DPoints.Count - 1 - index;

                DPoints[indexToChange].Top = Max - DPoints[index].Top;
            }
            else if(MirrorMode2)
            {
                int index = DPoints.IndexOf(actPoint);
                int indexToChange = ((index > DPoints.Count / 2) ? (index - DPoints.Count / 2) : (index + DPoints.Count / 2)) % DPoints.Count;

                DPoints[indexToChange].Top = Max - DPoints[index].Top;
            }

            // nacteni pozic bodu
            for(int i = 0; i < DPoints.Count; i++)
            {
                // prepocteni polohy bodu do intervali -1 ... 1
                PPoints[i] = ((double)(DPoints[i].Top - Middle) * (-1) / Middle);
            }
            PPoints[PPoints.Count - 1] = PPoints[0];
            // volani fce pro vygenerovani bufferu
            try
            {
                waveform.GenerateUserDefined(trackBar2.Value, trackBar1.Value, (int)sample, trackBar1.Maximum, PPoints.ToArray());
            }
            catch(Exception e)
            {
                MessageBox.Show(e.Message, "Error");
                trackBar2.Value = 0;
                trackBar2_Scroll(this, null);
            }
        }

        public void SetUserPoints(List<double> points)
        {
            CreatePoints(this, points.Count);

            for(int i = 0; i < points.Count; i++)
            {
                DPoints[i].Top = ((int)Math.Round(points[i] * (-1) * Middle) + Middle);
            }

            PointPosChanged(null);
        }

        public void LoadUserSig(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "Text file (*.txt)|*.txt|All files (*.*)|*.*";
            ofd.ShowDialog();

            if (ofd == null || ofd.FileName == null || ofd.FileName == "" || !File.Exists(ofd.FileName))
                return;

            StreamReader sr = new StreamReader(ofd.FileName);

            List<double> points = new List<double>();
            bool end = false;

            do
            {
                try
                {
                    points.Add(double.Parse(sr.ReadLine()));
                }
                catch
                {
                    end = true;
                }
            } while (!end);

            if(points.Count < 4)
            {
                MessageBox.Show("This file contain no wave samples.", "Error");
            }

            SetUserPoints(points);

            panel5.Refresh();
        }

        public void SaveUserSig(object sender, EventArgs e)
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Filter = "Text file (*.txt)|*.txt";
            sfd.ShowDialog();
            
            StreamWriter sw = new StreamWriter(sfd.FileName);

            for (int i = 0; i < PPoints.Count - 1; i++)
            {
                sw.WriteLine(PPoints[i]);
            }

            sw.Flush();
            sw.Close();
        }

        private void textBox1_Click(object sender, EventArgs e)
        {
            this.textBox1.Visible = false;
            this.textBox12.Visible = true;
            this.button9.Visible = true;
        }

        private void button9_Click(object sender, EventArgs e)
        {

            double val = 0;

            try
            {
                val = double.Parse(textBox12.Text);
            }
            catch
            {
                MessageBox.Show("Enter valid number.", "Error");
                return;
            }
            if (val <= 0 || val > waveform.Tones[waveform.Tones.Count - 1].Freq)
            {
                MessageBox.Show("Inserted number must be greater than zero and less than maximal frequency (" + waveform.Tones[waveform.Tones.Count - 1].Freq + "Hz).", "Error");
                return;
            }

            FindFreqValue(val);
        }

        private void FindFreqValue(double val)
        {
            int i = 1;

            while (val > waveform.Tones[i++].Freq) ;

            if (Math.Abs(val - waveform.Tones[i - 1].Freq) < Math.Abs(val - waveform.Tones[i - 2].Freq))
            {
                trackBar2.Value = i - 1;
            }
            else
            {
                trackBar2.Value = i - 2;
            }

            trackBar2_Scroll(null, null);

            this.textBox1.Visible = true;
            this.textBox12.Visible = false;
            this.button9.Visible = false;
        }

        private void textBox12_kd(object sender, KeyEventArgs e)
        {
            if(e.KeyCode == Keys.Enter)
            {
                button9_Click(sender, null);
            }
        }

        private void textBox4_kd(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                ReDraw();
            }
        }


    }
}
