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

namespace Generator
{
    public class ToneTable
    {
        public ToneTable(double freq, uint samples, uint devider, uint multipler)
        {
            this.Freq = freq;
            this.SamplesPerT = samples;
            this.Devider = devider;
            this.Multipler = multipler;
        }

        public override string ToString()
        {
            return "f: " + Freq + "Hz, samples: " + SamplesPerT + ", devider: " + Devider + ", muliplpler: " + Multipler;
        }

        public static List<ToneTable> GenerateTones(uint sampleRate, uint memoryCount, uint maxfDev, out List<ToneTable> sineTones)
        {
            // tony, ktere v bufferu obsahuji vzdy pouze jednu periodu
            List<ToneTable> tones = new List<ToneTable>();
            // tony pro funkci sinus, mohou obsahovat vice period do bufferu
            sineTones = new List<ToneTable>();

            uint j = 4;     // minimalni pocet vzorku na periodu
            uint dev = 1;   // delitel samplovaci frekvence
            
            // prvni cyklus pro delitele
            for (uint f = 1; dev <= maxfDev; f++)
            {
                
                // druhy cyklus pro jednotlive vzorky
                for (; j <= memoryCount; j += f)
                {
                    // pridani tonu
                    tones.Insert(0, new ToneTable((double)sampleRate / j, j, dev, 1));
                    sineTones.Insert(0, new ToneTable((double)sampleRate / j, j, dev, 1));
                    // pokud je delitel 1, tj. pro vyzsi frekvence pridani tonu s vice periodami v bufferu
                    if (dev == 1)
                    {
                        double j2 = j;
                        uint mult = 2;
                        
                        while ((j2 / mult) >= 4)
                        {
                            sineTones.Insert(0, new ToneTable((double)sampleRate / j * mult, j, dev, mult));
                            mult++;
                        }
                    }
                }
                // dalsi krok s vetsim delitelem
                sampleRate /= 2;
                dev *= 2;
                j /= 2;
            }

            // serazeni sinusovych frekvenci
            sineTones.Sort((x, y) => x.Freq.CompareTo(y.Freq));

            // odstraneni duplicitnich frekvenci
            for(int i = 1; i < sineTones.Count; i++)
            {
                if (Math.Abs(sineTones[i - 1].Freq - sineTones[i].Freq) < 0.05)
                {
                    if (sineTones[i - 1].Multipler <= sineTones[i].Multipler)
                    {
                        sineTones.RemoveAt(i--);
                    }
                    else
                    {
                        sineTones.RemoveAt(--i);
                    }
                }
            }
            return tones;
        }

        // frekvence
        public double Freq { get; private set; }
        // pocet vzorku v bufferu
        public uint SamplesPerT { get; private set; }
        // delitel
        public uint Devider { get; private set; }
        // pocet period v bufferu
        public uint Multipler { get; private set; }
    }
}
