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

namespace Intemi.InTrees.Implements
{
    /// <summary>
    /// Generator testów.
    /// Klasa generuje testy dzielące zbiór danych zgodnie z wartościomi danej cechy.
    /// </summary>
    public class MultiSplitTestGenerator : INodeTestGenerator
    {

        /// <summary>
        /// Zwraca listę testów
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public List<INodeTest> GenerateTests(IDataTable data, ref bool shouldTerminate)
        {
            return TestGenerator(data, null, false, ref shouldTerminate);
        }

        public INodeTest GenerateBestTest(IDataTable data, INodeTestSelector testSelector, ref bool shouldTerminate)
        {
            List<INodeTest> nodeTests = TestGenerator(data, testSelector, true, ref shouldTerminate);
            INodeTest test;
            if (nodeTests.Count > 0)
            {
                test = TestGenerator(data, testSelector, true, ref shouldTerminate)[0];
            }
            else
            {
                test = null;
            }

            return test;
        }

        private List<INodeTest> TestGenerator(IDataTable data, INodeTestSelector testSelector, bool bestTestOnly, ref bool shouldTerminate)
        {
            int interval = 2;

            List<INodeTest> nodeTests = new List<INodeTest>();
            NodeTest nodeTest;

            IFeaturesInfo features = data.FeaturesInfo;
            IFeature feature;
            IVectorEnumerator fe = (IVectorEnumerator)data.FeatureEnumerator();

            IOneFeatureData tt = (data as ITargets).Targets;
            IVectorEnumerator t = (IVectorEnumerator)tt.FeatureEnumerator();
            t.GoToInstance(0);
            float[] targets = t.Vector;
            float[] vector;

            for (int i = 0; !shouldTerminate && i < features.Count; i++)
            {
                feature = features[i];
                if (feature.Ordered)
                {
                    #region ContTestGen
                    fe.GoToInstance(i);
                    vector = fe.Vector;

                    List<float> splitPoints = new List<float>();
                    Array.Sort(vector, targets);
                    float targetOld = targets[0];
                    //splitPoints.Add(vector[0]);
                    int lastSplit = 0;
                    for (int j = 1; !shouldTerminate && j < targets.Length; j++)
                    {
                        if ((targets[j] != targetOld) && ((j - lastSplit) >= interval))
                        {
                            if (!splitPoints.Contains(vector[j]))
                            {
                                splitPoints.Add(vector[j]);
                                lastSplit = j;
                            }
                            lastSplit = j;
                        }
                        targetOld = targets[j];
                    }


                    for (int j = 0; !shouldTerminate && j < splitPoints.Count - 1; j++)
                    {

                        nodeTest = new NodeTest();
                        nodeTest.AddRule(new ClassificationRule(ContFeatAtom.LT(feature, splitPoints[j])));
                        nodeTest.AddRule(new ClassificationRule(ContFeatAtom.GEQ(feature, splitPoints[j])));
                        if (bestTestOnly) 
                        {
                            nodeTest.TestRate = testSelector.RateTest(nodeTest, data, ref shouldTerminate);
                            if (nodeTests.Count > 0)
                            {
                                if (nodeTest.TestRate > nodeTests[0].TestRate)
                                {
                                    nodeTests[0] = nodeTest;
                                }
                            }
                            else 
                            {
                                nodeTests.Add(nodeTest);
                            }
                        }
                        else
                        {
                            nodeTests.Add(nodeTest);
                        }
                    }
                    #endregion
                }
                else
                {
                    #region DiscrTesGen
                    nodeTest = new NodeTest();
                    for (int j = 0; !shouldTerminate && j < feature.NrValues; j++)
                    {
                        nodeTest.AddRule(new ClassificationRule(new DiscrFeatAtom(feature, new int[] { j })));
                    }

                    if (bestTestOnly)
                    {
                        nodeTest.TestRate = testSelector.RateTest(nodeTest, data, ref shouldTerminate);
                        if (nodeTests.Count > 0)
                        {
                            if (nodeTest.TestRate > nodeTests[0].TestRate)
                            {
                                nodeTests[0] = nodeTest;
                            }
                        }
                        else
                        {
                            nodeTests.Add(nodeTest);
                        }
                    }
                    else
                    {
                        nodeTests.Add(nodeTest);
                    }
                    #endregion
                }
            }

            if (shouldTerminate)
                nodeTests = new List<INodeTest>();

            return (nodeTests);
        }
    }
}
