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

namespace Shlukova_Analyza_BP
{
    class Siluety
    {
        private double[,] prehlednaTabulka;
        private List<Silueta> seznam = new List<Silueta>();
        private int n;
        private double[][] maticeVzdalenosti;
        public List<Silueta> Seznam
        {
            get { return seznam; }
        }
        public Siluety(List<HashSet<int>> seznamShluku, double[][] maticeVzdalenosti)
        {
            this.maticeVzdalenosti = maticeVzdalenosti;
            seznam = new List<Silueta>(seznamShluku.Count);
            for (int i = 0; i < seznamShluku.Count; i++) // prochází shluky 
            {
                seznam.Add(new Silueta(i, seznamShluku, maticeVzdalenosti));
            }
            int n = 0;
            for (int i = 0; i < seznamShluku.Count; i++)
            {
                n += seznamShluku[i].Count;
            }
            this.n = n;
            prehlednaTabulka = new double[n, 4];
        }
        public double Prumer // průměrná hodnota průměrů jednotlivých siluet - asi není moc vypovídající
        {
            get
            {
                double x = 0;
                for (int i = 0; i < seznam.Count; i++)
                {
                    x += seznam[i].Prumer;
                }
                return x/seznam.Count;
            }
        }
        public double PrumerVsech
        {
            get
            {
                int celkem = 0;
                double x = 0;
                for (int i = 0; i < seznam.Count; i++)
                {
                    x += seznam[i].Prumer * seznam[i].N;
                    celkem += seznam[i].N;
                }
                return x / celkem;
            }
        }
        public double[,] PrehlednaTabulka
        {
            get
            {
                int pocitac = 0;
                for (int i = 0; i < seznam.Count; i++)
                {
                    for (int j = 0; j < seznam[i].N; j++)
                    {
                        prehlednaTabulka[pocitac, 0] = seznam[i].cislo + 1;
                        prehlednaTabulka[pocitac, 1] = seznam[i].soused[j] + 1;
                        prehlednaTabulka[pocitac, 2] = seznam[i].hodnoty[j];
                        prehlednaTabulka[pocitac, 3] = seznam[i].shluky[j] + 1;
                        pocitac++;
                    }
                }
                return prehlednaTabulka;
            }
        }
        public double[,] GrafovaTabulka
        {
            get
            {
                int mezera = n / 400;
                double[,] grafovaTabulka = new double[n + mezera * (seznam.Count - 1), seznam.Count];
                int pocitac = 0;
                for (int i = 0; i < seznam.Count; i++)
                {
                    for (int j = 0; j < seznam[i].N; j++)
                    {
                        grafovaTabulka[pocitac, seznam.Count - 1 - i] = seznam[i].hodnoty[j];
                        pocitac++;
                    }
                    if (i != seznam.Count - 1)
                    {
                        for (int j = 0; j < mezera; j++)
                        {
                            grafovaTabulka[pocitac, seznam.Count - 1 - i] = 0;
                            pocitac++;
                        }
                    }
                }
                return grafovaTabulka;
            }
        }
    }
    class Silueta
    {
        public int cislo; // číslo shluku
        public double[] hodnoty;
        public int[] shluky, soused;
        private double[][] maticeVzdalenosti;
        public int N
        {
            get
            {
                return hodnoty.GetLength(0);
            }
        }
        public Silueta(int cislo, List<HashSet<int>> seznamShluku, double[][] maticeVzdalenosti)
        {
            this.maticeVzdalenosti = maticeVzdalenosti;
            shluky = new int[seznamShluku[cislo].Count];
            hodnoty = new double[seznamShluku[cislo].Count];
            soused = new int[seznamShluku[cislo].Count];
            this.cislo = cislo;
            int pocitac = 0;
            foreach (int item in seznamShluku[cislo]) //prochází prvky shluku
            { 
                double[] pomoc = vypoctiB(seznamShluku, cislo, item);
                double b = pomoc[0];
                soused[pocitac] = (int)pomoc[1];
                shluky[pocitac] = item;
                double a = prumernaVzdalenost2(seznamShluku[cislo], item);
                hodnoty[pocitac] = (b - a) / Math.Max(a, b);
                pocitac++;
            }
            if (seznamShluku[cislo].Count == 1)
            {
                hodnoty[0] = 0;
            }
            else
            {
                Sorting();
            }
        }
        public double Prumer
        {
            get
            {
                if (N != 0)
                {
                    return hodnoty.Average();
                }
                return 0;
            }
        }
        private void Sorting()
        {
            int[] indexy = new int[hodnoty.Length];
            for (int i = 0; i < hodnoty.Length; i++)
            {
                indexy[i] = i;
            }
            Array.Sort(hodnoty, indexy);
            int[] shluky2 = new int[hodnoty.Length];
            int[] soused2 = new int[hodnoty.Length];
            for (int i = 0; i < hodnoty.Length; i++)
            {
                shluky2[indexy[i]] = shluky[i];
                soused2[indexy[i]] = soused[i];
            }
            soused = soused2;
            shluky = shluky2;
        }
        private double[] vypoctiB(List<HashSet<int>> seznamShluku, int cislo, int objekt)
        {
            double b, soused;
            if (cislo != 0)
            {
                b = prumernaVzdalenost(seznamShluku[0], objekt);
                soused = 0;
            }
            else
            {
                b = prumernaVzdalenost(seznamShluku[1], objekt);
                soused = 1;
            }
            double mezivypocet = 0;
            for (int i = 1; i < seznamShluku.Count; i++)
            {
                if (i != cislo)
                {
                    mezivypocet = prumernaVzdalenost(seznamShluku[i], objekt);
                    if (mezivypocet < b)
                    {
                        b = mezivypocet;
                        soused = i;
                    }
                }  
            }
            return new double[] { b, soused };
        }
        private double prumernaVzdalenost(HashSet<int> shluk, int objekt)
        {
            double suma = 0;
            foreach (int item in shluk)
            {
                suma += vzdalenost(objekt, item);
            }
            return suma / shluk.Count;
        }
        private double prumernaVzdalenost2(HashSet<int> shluk, int objekt)
        {
            double suma = 0;
            foreach (int item in shluk)
            {
                if (item != objekt)
                {
                    suma += vzdalenost(objekt, item);
                }
            }
            return suma / (shluk.Count - 1);
        }
        private double vzdalenost(int x, int y)
        {
            if (x > y)
            {
                return maticeVzdalenosti[x][y];
            }
            else
            {
                return maticeVzdalenosti[y][x];
            }
        }
    }
}
