package org.ts.ddcs.Tool;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

@SuppressWarnings("all")
public class IForest {

    private final static double POINT_DIVIDING_LINE_PATH_VALUE = 0.51; //离群点分界线路径常量值
    private final static int TREES=100;//孤立森林的树数量
    private final static int MAX_SAMPLES=256;//林的样本数量
    private final static int MAX_TREE_HIGH=8;//树的最大层高
    private List<ITree> iTrees=new ArrayList<>();
    private int maxSamples;
    private int numTrees;
    private int treeHigh;


    IForest(){
        this.numTrees = IForest.TREES;
        this.maxSamples = IForest.MAX_SAMPLES;
        this.treeHigh = IForest.MAX_TREE_HIGH;
    }

    IForest(int trees,int maxSamples,int treeHigh){
        this.numTrees = trees;
        this.maxSamples = maxSamples;
        this.treeHigh = treeHigh;
    }

    public void  train(List<Double> datas) throws  NullPointerException {
        if (null == datas){
            throw new NullPointerException("训练数据不能为空！");
        }
        int subSampleSize = Math.min(this.maxSamples* this.numTrees, datas.size()) / this.numTrees;
        int maxLength = (int) Math.ceil(bottomChanging(subSampleSize, 2));
        maxLength = Math.min(maxLength,this.treeHigh);

        for (int i = 0; i < this.numTrees; i++) {
            List<Double> subSample = getSubSample(datas, i, subSampleSize);
            ITree tree= growTree(subSample, maxLength, 0);
            iTrees.add(tree);
        }
    }

    private ITree growTree(List<Double> subDatas, int maxLength, int currentLength) {
        if (currentLength >= maxLength) {
            return new ITreeLeaf(subDatas.size());
        }
        if (subDatas.size()<=1){
            return new ITreeLeaf(subDatas.size());
        }
        Random random = new Random();
        int dataLen = subDatas.size();
        int randomPoint = random.nextInt(dataLen);
        //分割点
        double splitPoint = subDatas.get(randomPoint);
        List<Double> rightList = new ArrayList<Double>();
        List<Double> leftList = new ArrayList<Double>();
        for (int i = 0; i < randomPoint; i++) {
            if (subDatas.get(i) >= splitPoint) {
                rightList.add(subDatas.get(i));
            } else {
                leftList.add(subDatas.get(i));
            }
        }

        for (int i = randomPoint; i < dataLen; i++) {
            if (subDatas.get(i) >= splitPoint) {
                rightList.add(subDatas.get(i));
            } else {
                leftList.add(subDatas.get(i));
            }
        }
        return new ITreeBranch(growTree(leftList, maxLength, currentLength + 1),
                growTree(rightList, maxLength, currentLength + 1),
                splitPoint);
    }

    public int predict(double x) throws IllegalArgumentException {
        if ( iTrees == null ||  iTrees.size() == 0 ) {
            throw new IllegalArgumentException("必须先调用训练方法！");
        }
        double sum = 0;
        for (int i = 0; i < iTrees.size(); i++) {
            double len =pathLengh(x, iTrees.get(i), 0);
            //System.out.println("path len "+len);
            sum +=len;
        }
        double ave =cost(this.maxSamples);
        double exponent = -(sum / iTrees.size()) / cost(this.maxSamples);
        //得分阈值
        double score = Math.pow(2, exponent);
        //System.out.println("predict value "+ x + "   score "+score);
        if (score > POINT_DIVIDING_LINE_PATH_VALUE) {
            return -1;
        } else {
            return 1;
        }
    }

    double pathLengh(double x, ITree tree, int path_length) {
        String simpleName = tree.getClass().getSimpleName();
        if (simpleName.equals("ITreeLeaf")) {
            ITreeLeaf leaf = (ITreeLeaf) tree;
            int size = leaf.getSize();
            return path_length + cost(size);
        }
        ITreeBranch iTreeBranch = (ITreeBranch) tree;
        double splitValue = iTreeBranch.getSplitValue();
        if (x < splitValue) {
            ITree left = iTreeBranch.getLeft();
            return pathLengh(x, left, path_length + 1);
        } else {
            ITree right = iTreeBranch.getRight();
            return pathLengh(x, right, path_length + 1);
        }
    }


    private List<Double> getSubSample(List<Double> datas, int index, int subSampleCount) {
        List<Double> subSample = new LinkedList<>();
        for (int i = index * subSampleCount; i < datas.size() && i < (index + 1) * subSampleCount; i++) {
            subSample.add(datas.get(i));
        }
        return subSample;
    }

    double getHi(int i) {
        double constantValue = 0.5772156649;
        return Math.log(i) + constantValue;
    }

    double cost(int n) {
        double hi = getHi(n - 1);
        if (n <= 1) {
            return 1.0;
        }
        double cost = 2 * hi - 2 * (n - 1) / n;
        return cost;
    }



    private double bottomChanging(int x, int bottom) {
        double log = Math.log10(x) / Math.log10(bottom);
        return log;
    }

}
