package com.brackeen.javagamebook.bsp2D;

import java.util.List;
import java.util.ArrayList;
import com.brackeen.javagamebook.math3D.*;

/**
    Klasa BSPTreeBuilderWithPortals buduje drzewo BSP tree
    i dodaje do liści drzewa liście portali (przejść).

    Uwaga: Portale nie zostały zoptymalizowane. Na przykład,
    sąsiadujące, współliniowe portale nie są łączone,  
    a "bezużyteczne" przejścia nie są usuwane.
*/
public class BSPTreeBuilderWithPortals extends BSPTreeBuilder {


    /**
        Buduje drzewo BSP i dodaje liście portali.
    */
    public BSPTree build(List polygons) {
        super.build(polygons);
        findPortalsOfLeaves(currentTree.getRoot());
        return currentTree;
    }


    /**
        Odnajduje wśród liści podanego węzła wszystkie portale. 
    */
    protected void findPortalsOfLeaves(BSPTree.Node node) {
        if (node instanceof BSPTree.Leaf) {
            findPortals((BSPTree.Leaf)node);
        }
        else {
            findPortalsOfLeaves(node.front);
            findPortalsOfLeaves(node.back);
        }
    }


    /**
        Odnajduje wśród liści podanego węzła wszystkie portale. 
    */
    protected void findPortals(BSPTree.Leaf leaf) {
        ArrayList lines = new ArrayList();
        leaf.portals = new ArrayList();
        for (int i=0; i<leaf.polygons.size(); i++) {
            Polygon3D poly = (Polygon3D)leaf.polygons.get(i);
            for (int j=0; j<poly.getNumVertices(); j++) {
                int next = (j+1) % poly.getNumVertices();
                Vector3D v1 = poly.getVertex(j);
                Vector3D v2 = poly.getVertex(next);
                BSPLine line = new BSPLine(v1.x, v1.z, v2.x, v2.z);

                // test, by ustalić, czy wiersz był już sprawdzany
                boolean checked = false;
                for (int k=0; !checked && k<lines.size(); k++) {
                    if (line.equalsIgnoreOrder(
                        (BSPLine)lines.get(k)))
                    {
                        checked = true;
                    }
                }

                // utwórz portal
                if (!checked) {
                    lines.add(line);
                    Portal portal = createPortal(line);
                    if (portal != null) {
                        leaf.portals.add(portal);
                    }
                }
            }
        }
        ((ArrayList)leaf.portals).trimToSize();
    }


    /**
        Tworzy portal dla wybranego odcinka linii. Zwraca wartość
        null, jeśli nie można było utworzyć portalu (jeśli linia jest
        zwartą ścianą lub też nie udało się znaleźć linii).
    */
    protected Portal createPortal(BSPLine line) {
        BSPTree.Node node = currentTree.getCollinearNode(line);
        if (node != null && node.polygons != null) {
            for (int i=0; i<node.polygons.size(); i++) {
                BSPPolygon poly = (BSPPolygon)node.polygons.get(i);
                if (poly.isSolidWall() &&
                    line.equalsIgnoreOrder(poly.getLine()))
                {
                    // ściana nie do przejścia
                    return null;
                }
            }
        }

        BSPTree.Leaf frontLeaf = currentTree.getFrontLeaf(line);
        BSPTree.Leaf backLeaf = currentTree.getBackLeaf(line);
        if (frontLeaf != null && backLeaf != null &&
            frontLeaf != backLeaf && frontLeaf.bounds != null &&
            backLeaf.bounds != null)
        {
            return new Portal(line, frontLeaf, backLeaf);
        }
        else {
            return null;
        }

    }
}
