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


namespace Intemi.InTrees
{
    /// <summary>
    /// Algorytem TDIDT budowy drzew decyzyjnych
    /// </summary>
    public class TDIDT3
    {
        /// <summary>
        /// Kryterium wyboru testu.
        /// </summary>
        private INodeTestSelector nodeTestSelector;
        /// <summary>
        /// Kryterium stopu.
        /// </summary>
        private IStoppingCriterion stoppingCriterium;
        /// <summary>
        /// Generator testów.
        /// </summary>
        private INodeTestGenerator nodeTestGenerator;
        /// <summary>
        /// Wybór domyślnej etykiety.
        /// </summary>
        private ITargetSelector targetSelector;

        public INodeTestSelector TestSelector
        {
            get { return nodeTestSelector; }
            set { nodeTestSelector = value; }
        }

        public IStoppingCriterion StoppingCriterium
        {
            get { return stoppingCriterium; }
            set { stoppingCriterium = value; }
        }

        public INodeTestGenerator NodeTestGenerator
        {
            get { return nodeTestGenerator; }
            set { nodeTestGenerator = value; }
        }

        public ITargetSelector TargetSelector
        {
            get { return targetSelector; }
            set { targetSelector = value; }
        }

        public TDIDT3(INodeTestGenerator nodeTestGenerator, INodeTestSelector nodeTestSelector, IStoppingCriterion stoppingCriterium, ITargetSelector targetSelector, ref bool shouldTerminate)
        {
            this.nodeTestGenerator = nodeTestGenerator;
            this.nodeTestSelector = nodeTestSelector;
            this.stoppingCriterium = stoppingCriterium;
            this.targetSelector = targetSelector;
        }

        public DecisionTree Run(IDataTable dataTable, ref bool shouldTerminate)
        {
            if (dataTable.InstanceCount <= 0)
                throw new ExceptionEmptyData();
            Node<DTNode> root = BuildTree(dataTable, targetSelector.SelectTarget(dataTable, 0, ref shouldTerminate), null, ref shouldTerminate);
            return (new DecisionTree(root));
        }

        public Node<DTNode> BuildTree(IDataTable dataTable, int defaultTarget, IClassificationRule rule, ref bool shouldTerminate)
        {
            Node<DTNode> node = null;
            int target;
            INodeTest nodeTest=null;
            bool isLeaf;

            List<INodeTest> nodeTests = NodeTestGenerator.GenerateTests(dataTable, ref shouldTerminate);

            if (!shouldTerminate)
            {
                target = targetSelector.SelectTarget(dataTable, defaultTarget, ref shouldTerminate);
                if (stoppingCriterium.Stop(dataTable, nodeTests, ref shouldTerminate))
                    isLeaf = true; // tworzony jest liść
                else
                {
                    nodeTest = nodeTestSelector.SelectTest(nodeTests, dataTable, ref shouldTerminate);
                    isLeaf = (nodeTest.TestRate == 0);
                }

                node = CreateNode(dataTable, nodeTest, target, rule, isLeaf, ref shouldTerminate);
                //nodeTests.Remove(nodeTest);  

                if (!isLeaf)
                {
                    IDataTable[] splitData = nodeTest.SplitData(dataTable, ref shouldTerminate);
                    for (int i = 0; !shouldTerminate && i < nodeTest.SplitCount(); i++)
                    {
                        if (splitData[i].InstanceCount > 0)
                            node.Children.Add(BuildTree(splitData[i], target, nodeTest.GetRule(i), ref shouldTerminate));
                    }
                }
            }
            else if (shouldTerminate)
                node = null;

            return (node);
        }

        private Node<DTNode> CreateNode(IDataTable dataTable, INodeTest nodeTest, int target, IClassificationRule rule, bool isLeaf, ref bool shouldTerminate)
        {
            if (isLeaf)
                nodeTest = null;
            DTNode dtNode = new DTNode(dataTable.FeaturesInfo, rule, nodeTest, target, isLeaf);
            dtNode.DataRecalculate(dataTable, ref shouldTerminate);

            return (new Node<DTNode>(dtNode));
        }
    }
}
