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

namespace Intemi
{
    public enum StdStyle { Std, Normalize01, Normalize11 }
    public class StdConfig : IConfiguration
    {
        StdStyle stdStyle = StdStyle.Std;
        bool onlyOrderedFeatures = true;

        public StdStyle StdStyle
        {
            get { return stdStyle; }
            set { stdStyle = value; }
        }
        public bool OnlyOrderedFeatures
        {
            get { return onlyOrderedFeatures; }
            set { onlyOrderedFeatures = value; }
        }

        public void Configure(ConfigBuilder confBuilder)
        {
            confBuilder.DeclareInput("Dataset",
                new Type[] { typeof(IDataTable) });
            confBuilder.DeclareOutput("Standardizer", null,
                new Type[] { typeof(IDataSetTransformer) });
            confBuilder.DeclareOutput("Dataset", "StandardizedDataTable",
                new Type[] { typeof(IDataTable) });
        }
        public object Clone() 
        {
            StdConfig c = new StdConfig();
            c.onlyOrderedFeatures = onlyOrderedFeatures;
            return c; 
        }
    }

    [Machine("Standardization",typeof(StdConfig))]
    public class Std : IMachine, IDataSetTransformer
    {
        IMachineBase machineBase;
        FeatureStat[] fs;
        IDataTable standardizedDataTable;

        public IDataTable StandardizedDataTable
        {
            get { return standardizedDataTable; }
        }

        public void SetMachineBase(IMachineBase _machineBase)
        {
            machineBase = _machineBase;
        }
        public void Run(ref bool shouldTerminate)
        {
            IDataTable dt = machineBase.GetInput("Dataset") as IDataTable;
            fs = dt.FeatureStatistics;
            standardizedDataTable = (IDataTable)Transform(dt);
        }

        public IDataSet Transform(IDataSet dataSet)
        {
            IDataTable dataTable = dataSet as IDataTable;
            bool onlyOrdered = ((StdConfig)machineBase.ConfBase.Configuration).OnlyOrderedFeatures;
            StdStyle stdStyle = ((StdConfig)machineBase.ConfBase.Configuration).StdStyle;
            IDataTableBuilder dtb = (IDataTableBuilder)dataTable.GetBuilder();
            dtb.CloneFrom(dataTable);
            IFeaturesInfo fi = dataTable.FeaturesInfo;

            for (int i = 0; i < dataTable.FeaturesInfo.Count; i++)
                if (!onlyOrdered || fi[i].Ordered)
                    dtb.SetOrdered(i, true);

            float[,] v = dtb.Values;
            bool[,] m = dtb.Missing;

            for (int i = 0; i < dataTable.InstanceCount; i++)
            {
                for (int j = 0; j < dataTable.FeaturesInfo.Count; j++)
                    if (!m[i, j] && (!onlyOrdered || fi[j].Ordered))
                    {
                        double _m, _s;
                        switch (stdStyle)
                        {
                            case StdStyle.Std:
                                _m = fs[j].mean;
                                _s = fs[j].standardDev;
                                break;
                            case StdStyle.Normalize01:
                                _m = fs[j].minValue;
                                _s = fs[j].maxValue - fs[j].minValue;
                                break;
                            case StdStyle.Normalize11:
                                _m = fs[j].mean;
                                _s = (fs[j].maxValue - fs[j].minValue) / 2.0;
                                break;
                            default:
                                _m = 0; _s = 0;
                                break;
                        }
                        v[i, j] = (float)((double)v[i, j] - _m);
                        if (_s != 0)
                            v[i, j] = (float)((double)v[i, j] / _s);
                    }
            }            
            return dtb.Build();
        }
    }
}
