package presenter.algorithms.naiv;

import model.Line;
import model.Point;
import presenter.algorithms.Algorithm;
import presenter.algorithms.util.FastElementSelector;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;

/**
 * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
 *
 * @Author: Armin Wolf
 * @Email: a_wolf28@uni-muenster.de
 * @Date: 15.09.2017.
 */
public class NaivRepeatedMedianEstimator implements Algorithm{

    private LinkedList<Line> lines;
    private HashMap<String, ArrayList<Double>> slopesPerLine;
    private HashMap<String, ArrayList<Double>> interceptPerLine;
    private ArrayList<Double> xMedians, yMedians;
    private Double medianX;
    private Double medianY;

    public NaivRepeatedMedianEstimator(LinkedList<Line> lines) {
        this.lines = lines;
        slopesPerLine = new HashMap<>();
        interceptPerLine = new HashMap<>();
        xMedians = new ArrayList<>();
        yMedians = new ArrayList<>();
    }

    @Override
    public void run() {
        //init the List for the slopes
        for (int j=0;j<lines.size();j++) {
            Line leq = lines.get(j);
            if (slopesPerLine.get(leq.getId()) == null) {
                slopesPerLine.put(leq.getId(), new ArrayList<>());
            }
            if (interceptPerLine.get(leq.getId()) == null) {
                interceptPerLine.put(leq.getId(), new ArrayList<>());
            }
        }


        //calculate all slopes for each line
        Point ret;
        for (int i=0;i<lines.size();i++) {
            for (int j=i+1;j<lines.size();j++){
                ret = calculateSlope(lines.get(i), lines.get(j));
                slopesPerLine.get(lines.get(i).getId()).add(ret.getX());
                interceptPerLine.get(lines.get(i).getId()).add(ret.getY());
            }
        }

        //calculate median of slopes for each line
        for (String l : slopesPerLine.keySet()) {
            ArrayList<Double> list = slopesPerLine.get(l);
            int size = list.size() / 2;
            if (size > 0) {
                Double medianX = FastElementSelector.randomizedSelect(list, size);
                xMedians.add(medianX);
            }
        }

        //calculate median of slopes for each line
        for (String l : interceptPerLine.keySet()) {
            ArrayList<Double> list = interceptPerLine.get(l);
            int size = list.size() / 2;
            if (size > 0) {
                Double medianY = FastElementSelector.randomizedSelect(list, size);
                yMedians.add(medianY);
            }
        }

        medianX = FastElementSelector.randomizedSelect(xMedians, xMedians.size()/2);
        medianY = FastElementSelector.randomizedSelect(yMedians, yMedians.size()/2);
        System.out.printf("Naiv RM:  %6.2f * x + %6.2f \n", medianX, medianY);
    }

    @Override
    public void getResult() {

    }

    private Point calculateSlope(Line lineA, Line lineB){
        Double xi;
        Double xj;
        Double yi;
        Double yj;

        if (lineB.getM() > lineA.getM()){
            xi = lineA.getM();
            yi = lineA.getB();
            xj = lineB.getM();
            yj = lineB.getB();
        } else {
            xj = lineA.getM();
            yj = lineA.getB();
            xi = lineB.getM();
            yi = lineB.getB();
        }


        Double m = (yj - yi) / (xj -xi);
        Double b =  ((xj * yi) - (xi * yj)) / (xj - xi);
        return new Point(m,b);
    }

    public Double getM() {
        return medianX * -1;
    }

    public Double getB() {
        return medianY * -1;
    }
}
