using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Generator
{
    public class GenWaveform
    {
        public GenWaveform()
        {
            Sequence = new List<UInt16>();
        }


        public void GenerateSine(int index, int amplitude, int sampleFreq, int max_ampl)
        {
            Sequence.Clear();
            for (int i = 0; i < Tones[index].SamplesPerT; i++)
            {
                Sequence.Add((UInt16)Math.Round(amplitude / 2 * Math.Sin(2 * Math.PI * i * Tones[index].Freq / sampleFreq * Tones[index].Devider) + max_ampl / 2));
            }
        }

        public void GenerateSawUp(int index, int amplitude, int sampleFreq, int max_ampl)
        {
            Sequence.Clear();
            for (int i = 0; i < Tones[index].SamplesPerT; i++)
            {
                Sequence.Add((ushort)(amplitude * i / Tones[index].SamplesPerT - amplitude / 2 + max_ampl / 2));
            }
        }

        public void GenerateSawDown(int index, int amplitude, int sampleFreq, int max_ampl)
        {
            Sequence.Clear();
            for (int i = 0; i < Tones[index].SamplesPerT; i++)
            {
                Sequence.Add((ushort)(amplitude / 2 - amplitude * i / Tones[index].SamplesPerT + max_ampl / 2));
            }
        }

        public void GenerateTriangle(int index, int amplitude, int sampleFreq, int max_ampl)
        {
            GenerateUserDefined(index, amplitude, sampleFreq, max_ampl, new double[] {0.0, 1.0, 0.0, -1.0, 0.0});
            /*
            Sequence.Clear();

            int i = 0;
            int j = 0;

            for (; i < Tones[index].SamplesPerT / 4; i++)
            {
                Sequence.Add((UInt16)(max_ampl / 2 + amplitude * j * 2 / Tones[index].SamplesPerT));
                j++;
            }
            j = 0;
            for (; i < Tones[index].SamplesPerT * 3 / 4; i++)
            {
                Sequence.Add((UInt16)(max_ampl / 2 +  amplitude / 2 - amplitude * j * 2 / Tones[index].SamplesPerT));
                j++;
            }
            j = 0;
            for (; i < Tones[index].SamplesPerT; i++)
            {
                Sequence.Add((UInt16)(max_ampl / 2 + amplitude * j * 2 / Tones[index].SamplesPerT - amplitude / 2));
                j++;
            }*/
        }

        public void GenerateRectangle(int index, int amplitude, int sampleFreq, int max_ampl)
        {
            Sequence.Clear();

            int i = 0;

            for (; i < Tones[index].SamplesPerT / 2; i++)
            {
                Sequence.Add((UInt16)(max_ampl / 2 + amplitude / 2));
            }
            for (; i < Tones[index].SamplesPerT; i++)
            {
                Sequence.Add((UInt16)(max_ampl / 2 - amplitude / 2));
            }
        }

        public void GenerateUserDefined(int index, int amplitude, int sampleFreq, int max_ampl, double[] points)
        {
            // vypocet vstupni a vystupni delky
            int outlen = (int)Tones[index].SamplesPerT;
            int inlen = points.Length;

            
            // vystup ma vetrsi pocet vzorku
            if (outlen >= inlen)
            {   
                // vypocet interpolace krivky
                double[] tmp = interpolate(points, outlen);

                // odstraneni starych dat
                Sequence.Clear();

                // vypocteni hodnot vzorku
                foreach (double d in tmp)
                {
                    Sequence.Add((ushort)(d * (amplitude / 2.0) + (double)max_ampl / 2));
                }
                
            }
            // vystup ma stejny pocet vzorku jako vstup
            else if (outlen == inlen - 1)
            {
                // odstraneni starych dat
                Sequence.Clear();

                for (int i = 0; i < inlen - 1; i++)
                {
                    Sequence.Add((ushort)Math.Round(points[i] * (amplitude / 2.0) + (double)amplitude / 2));
                }
            }
            // vystup ma mensi pocet vzorku nez 
            else
            {
                // chyba, udelej si min bodu...
                throw new Exception("This pattern has too many samples for required frequency.");
            }
        }


        public void GeneratePWM(int index, int amplitude, int sampleFreq)
        {
        }


        public static double[] interpolate(double[] inPoints, int outCount)
        {
            // vystupni pole
            double[] outPoints = new double[outCount];
            double first, second, inPart;

            // double pro vlastni vypocet
            double d_in = 1.0 / (inPoints.Length - 1);
            double d_out = 1.0 / outCount;
            // nahrazeno obracenou hodnotou, pro pocitani cyklu, kvuli chybe zaokrouhlovani double
            int _in = outCount; 
            int _out = (inPoints.Length - 1);

            int j = 0;

            // cyklus pro vstupni pole
            for (int i = 0; i < inPoints.Length - 1; i++)
            {
                // nstaveni bodu
                first = inPoints[i];
                second = inPoints[i + 1];

                // prvni cast vstupni krivky
                inPart = (i + 1) * _in;

                // cyklus pro vystupni vzorky v 1 useku vstupni krivky
                while ((_out * j) < inPart)
                {
                    // body jsou "stejne vysoko"
                    if (first == second)
                    {
                        outPoints[j] = first;
                    }
                    // krivka stoupa
                    else
                    {
                        outPoints[j] = first + ((second - first) * ((j * d_out) - (i * d_in)) / d_in);
                    }
                    j++;

                }
            }
            return outPoints;
        }


        public List<ToneTable> Tones;
        public List<ToneTable> OtherTones = new List<ToneTable>();
        public List<ToneTable> SineTones = new List<ToneTable>();
        public List<UInt16> Sequence { get; private set; }
    }
}
