package vypocetObalky;

import java.util.ArrayList;

public class VypocetObalkyNove {

    /**
     * Vypocita horni obalku bodu
     */
    public static ArrayList<Primka> horniObalka(ArrayList<Souradnice> body) {
        if (body == null) {
            throw new NullPointerException("body");
        }
        if (body.size() < 2) {
            throw new IllegalArgumentException("body musi byt nejmene 2");
        }

        ArrayList<Primka> obalka = new ArrayList<Primka>();


        //1. vezmi vsechny body ze serazeneho grafu --parametr fce
        //2. uloz prvni bod do promenne vychoziBod
        //3. nasledujici bod uloz do promenne soucasnyBod
        //4. pomoci fce primkaZBodu vypocti rovnici primky - souradnice bodu A, souradnice bodu B, smernici a posunuti
        //5. pomoci fce vsechnyBodyPodPrimkou zjisti zda se pod primkou nachazeji vsechny body
        //6. pokud predchozi podminka plati tak uloz hodnoty do promenne obalka (coz je pole primek)
        //7. posun se na bod, ktery byl konecny pro ulozenou primku --- startovni bod pro cyklus 
        //   bude tedy bod jenz byl ulozen jako soucasny  
        //8. opakuj cyklus pro vsechny body
        //TENTO POSTUP JE PODODNY I PRO VYPOCET DOLNI OBALKY 
        //- zmena je jen u kontrolni fce - v tomto pripade jsou kontrolovany body jenz lezi nad primkou 

        Souradnice vychoziBod = body.get(0);
        for (int i = 1; i < body.size(); i++) {
            Souradnice soucasnyBod = body.get(i);
            Primka novaPrimka = primkaZBodu(vychoziBod, soucasnyBod);
            if (vsechnyBodyPodPrimkou(novaPrimka, body)) {
                obalka.add(novaPrimka);
                vychoziBod = soucasnyBod;
            }
        }

        //pridat primku k poslednimu bodu

        Souradnice posledniBod = body.get(body.size() - 1);
        Primka novaPrimka = primkaZBodu(vychoziBod, posledniBod);
        if (!novaPrimka.getK().toString().equals("NaN")) {
            obalka.add(novaPrimka);
        }

        //vykresli graf spolu s primkami
        Vykresleni.VykresliGraf(obalka, body);

        return obalka;
    }

    /**
     * Vypocita spodni obalku bodu
     */
    public static ArrayList<Primka> dolniObalka(ArrayList<Souradnice> body) {
        if (body == null) {
            throw new NullPointerException("body");
        }
        if (body.size() < 2) {
            throw new IllegalArgumentException("body musi byt nejmene 2");
        }

        ArrayList<Primka> obalka = new ArrayList<Primka>();

        Souradnice vychoziBod = body.get(0);
        for (int i = 1; i < (body.size() - 1); i++) {
            Souradnice soucasnyBod = body.get(i);
            Primka novaPrimka = primkaZBodu(vychoziBod, soucasnyBod);
            if (vsechnyBodyNadPrimkou(novaPrimka, body)) {
                obalka.add(novaPrimka);
                vychoziBod = soucasnyBod;
            }
        }

        //pridat primku k poslednimu bodu
        Souradnice posledniBod = body.get(body.size() - 1);
        Primka novaPrimka = primkaZBodu(vychoziBod, posledniBod);
        obalka.add(novaPrimka);

        //vykresli graf spolu s primkami
        Vykresleni.VykresliGraf(obalka, body); 

        return obalka;
    }

    /**
     * Overi, ze vsechny body jsou pod primkou
     */
    private static boolean vsechnyBodyPodPrimkou(Primka primka, ArrayList<Souradnice> body) {
        if (primka == null || body == null) {
            throw new NullPointerException("primka or body je null");
        }

        //body jsou pod primkou prave kdyz kazdy bod ma y souradnici mensi nebo rovnu vypoctene
        //ze smernicove rovnice primky y = kx + q
        for (Souradnice souradnice : body) {
            Double bodX = souradnice.getSouradniceX();
            Double bodY = souradnice.getSouradniceY();

            if (bodX == null || bodY == null) {
                throw new NullPointerException("bodX nebo bodY nektere ze souradnic je null");
            }

            Double k = primka.getK();
            Double q = primka.getQ();

            Double primkaY = (k * bodX) + q;

            if (bodY > primkaY) {
                return false;
            }
        }

        return true;
    }

    /**
     * Overi, ze vsechny body jsou pod primkou
     */
    private static boolean vsechnyBodyNadPrimkou(Primka primka, ArrayList<Souradnice> body) {
        if (primka == null || body == null) {
            throw new NullPointerException("primka or body je null");
        }

        //body jsou nad primkou prave kdyz kazdy bod ma y souradnici vetsu nebo rovnu vypoctene
        //ze smernicove rovnice primky y = kx + q
        for (Souradnice souradnice : body) {
            Double bodX = souradnice.getSouradniceX();
            Double bodY = souradnice.getSouradniceY();

            if (bodX == null || bodY == null) {
                throw new NullPointerException("bodX nebo bodY nektere ze souradnic je null");
            }

            Double k = primka.getK();
            Double q = primka.getQ();

            Double primkaY = (k * bodX) + q;

            if (bodY < primkaY) {
                return false;
            }
        }

        return true;
    }

    /**
     * Vypocita primku z 2 bodu
     */
    private static Primka primkaZBodu(Souradnice a, Souradnice b) {
        if (a == null || b == null) {
            throw new NullPointerException("a or b je null");
        }

        //k = dY / dX
        Double deltaX = b.getSouradniceX() - a.getSouradniceX();
        Double deltaY = b.getSouradniceY() - a.getSouradniceY();

        Double k = deltaY / deltaX;

        //q = Y - k*X
        Double q = a.getSouradniceY() - k * a.getSouradniceX();

        Primka primka = new Primka(k, q, a, b);
        return primka;
    }
}
