package presenter.algorithms.util;

import model.Interval;
import model.Line;
import model.Point;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;

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

    private LinkedList<Line> lines;
    private List<Line>[] subList;
    private TreeSet<Point> intersections;

    private Double xMinimum;
    private Double xMaximum;
    private Double yMinimum;
    private Double yMaximum;

    private Thread[] worker;

    public IntersectionComputer(LinkedList<Line> lines) {
        this.lines = lines;
        this.worker = new Thread[4];
        this.subList = new LinkedList[4];
        this.intersections = new TreeSet<>();

        xMinimum = Double.MAX_VALUE;
        xMaximum = Double.MIN_VALUE;
        yMinimum = Double.MAX_VALUE;
        yMaximum = Double.MIN_VALUE;
    }

    public ArrayList<Point> compute(){

        if (lines.size() >= 12) {
            worker[0] = new Thread(() -> {
                work(lines.subList(0, (lines.size() / 4)));
            });
            worker[0].start();
            worker[1] = new Thread(() -> {
                work(lines.subList((lines.size() / 4) + 1, (lines.size() / 2)));
            });
            worker[1].start();
            worker[2] = new Thread(() -> {
                work(lines.subList((lines.size() / 2) + 1, 3 * (lines.size() / 4)));
            });
            worker[2].start();
            worker[3] = new Thread(() -> {
                work(lines.subList(3 * (lines.size() / 4) + 1, (lines.size())));
            });
            worker[3].start();


            for (Thread t : worker) {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } else {
            work(lines);
        }

        return new ArrayList<>(intersections);

    }

    private synchronized void work(List<Line> lines){
        IntersectionCounter counter = new IntersectionCounter();
        counter.run(lines, new Interval(-99999, 99999));
        ArrayList<Point> points = counter.calculateIntersectionAbscissas();
        for (Point p :points){
            if (!isFound(p)){
                addIntersection(p);
                setRanges(p);
            }
        }

    }

    public synchronized LinkedList<Line> getLines() {
        return lines;
    }

    public synchronized void addIntersection(Point p){
        this.intersections.add(p);
    }

    public synchronized boolean isFound(Point p){
        if (intersections.contains(p)){
            return true;
        } else {
            return false;
        }
    }

    public synchronized void setRanges(Point point){
        xMaximum = (point.getX() > xMaximum) ? point.getX() : xMaximum;
        xMinimum = (point.getX() < xMinimum) ? point.getX() : xMinimum;
        yMaximum = (point.getY() > yMaximum) ? point.getY() : yMaximum;
        yMinimum = (point.getY() < yMinimum) ? point.getY() : yMinimum;
    }

    public Double getxMinimum() {
        return xMinimum;
    }

    public Double getxMaximum() {
        return xMaximum;
    }

    public Double getyMinimum() {
        return yMinimum;
    }

    public Double getyMaximum() {
        return yMaximum;
    }
}
