using System;
using System.Collections.Generic;
using System.Text;

namespace Intemi
{
    public class MDkNNConfig : IConfiguration
    {
        bool mDistanceWeight = false;
        int mCount = 8;
        public bool distanceWeight
        {
            get { return mDistanceWeight; }
            set { mDistanceWeight = value; }
        }
        public int count
        {
            get { return mCount; }
            set { mCount = value; }
        }
        public void Configure(ConfigBuilder confBuilder)
        {
            confBuilder.DeclareInput("Dataset",
                new Type[] { typeof(IDataTable) }, false);
            confBuilder.DeclareOutput("MissingDataFiller", null,
                new Type[] { typeof(IDataSetTransformer) });
            confBuilder.DeclareOutput("DataSet", "FilledDataTable", 
                new Type[] { typeof(IDataTable) });
        }
        public object Clone()
        {
            MDkNNConfig c = new MDkNNConfig();
            return c;
        }
    }

    [Machine("MDkNN", typeof(MDkNNConfig))]
    public class MDkNN : IMachine, IDataSetTransformer
    {
        public class entryDistance
        {
            public int mEntry;
            public float mDistance;
            public float mWeight;
            public entryDistance(int entry, float distance, float weight)
            {
                mEntry = entry;
                mDistance = distance;
                mWeight = weight;
            }
        }
        IMachineBase modelBase;
        FeatureStat[] fs;
        List<entryDistance> distList = new List<entryDistance>();
        int instanceCount;

        bool distanceWeight;
        float[,] values;
        IDataTable dataTable;
        IDataTable filledDataTable;

        public IDataSet FilledDataTable
        {
            get { return filledDataTable; }
        }
        public void SetMachineBase(IMachineBase _modelBase)
        {
            modelBase = _modelBase;
        }
        public void Run(ref bool shouldTerminate)
        {
            IDataTable dt = modelBase.GetInput("Dataset") as IDataTable;
            fs = dt.FeatureStatistics;
            filledDataTable = (IDataTable)Transform(dt);
        }
        
        public void fillDistanceTableFor(int jj, int ii)
        {
            distList.Clear();
            for (int j = 0; j < dataTable.InstanceCount; j++)
            {
                float dist = 0;
                if (j != jj)
                {
                    for (int i = 0 ; i < dataTable.FeaturesInfo.Count; i++)
                    {
                        if (i != ii)
                        {
                            dist += ((values[j,i] - values[jj, i]) *
                                     (values[j,i] - values[jj, i]));
                        }
                    }
                    distList.Add(new entryDistance(j, dist, distanceWeight==false?1:1/dist));
                }
            }
            distList.Sort(delegate(entryDistance e1, entryDistance e2) { return e1.mDistance.CompareTo(e2.mDistance); });
        }
        public IDataSet Transform(IDataSet dataSet)
        {
            dataTable = dataSet as IDataTable;
            distanceWeight = ((MDkNNConfig)modelBase.ConfBase.Configuration).distanceWeight;
            instanceCount = ((MDkNNConfig)modelBase.ConfBase.Configuration).count;
            IDataTableBuilder dtb = (IDataTableBuilder)dataTable.GetBuilder();
            dtb.CloneFrom(dataTable);
            
            values = dtb.Values;
            bool[,] m = dtb.Missing;

            for (int j = 0; j < dataTable.InstanceCount; j++)
            {
                for (int i = 0; i < dataTable.FeaturesInfo.Count; i++)
                {
                    if (m[j, i])
                    {
                        // Tutaj brak danych, obliczanie:
                        fillDistanceTableFor(j, i);
                        // Obliczanie sredniej/sredniej wazonej
                        float mean = 0;
                        Console.WriteLine("The following " + instanceCount + "instances are closed");
                        for (int k = 0; k < instanceCount; k++)
                        {
                            Console.WriteLine("distList[" + k + "] = " + distList[k].mEntry);
                            mean += values[distList[k].mEntry, i] * distList[k].mWeight;
                        }
                        mean /= instanceCount;
                        values[j, i] = mean;
                    }
                }
            }

            /* Debuging */
            for (int j = 0; j < dataTable.InstanceCount; j++)
            {
                for (int i = 0; i < dataTable.FeaturesInfo.Count; i++)
                {
                    Console.Write(values[j, i] + " ");
                }
                Console.WriteLine();
            }
            for (int i = 0; i < dataTable.FeaturesInfo.Count; i++)
            {
                Console.Write(fs[i].mean + " ");
            }
            /**/
            return dtb.Build();
        }
    }
}

