package com.brackeen.javagamebook.bsp2D;

import com.brackeen.javagamebook.math3D.*;
import com.brackeen.javagamebook.game.GameObjectManager;

/**
    Klasa BSPTreeTraverer odpowiada za przeglądanie węzłów dwuwymiarowego
    drzewa BSP albo w porządku poprzeczny, albo w kolejności rysowania
    (od przodu do tyłu). Odwiedzone wielokąty otrzymują odpowiednie sygnały
    za pośrednictwem obiektów klasy BSPTreeTraverseListener.
*/
public class BSPTreeTraverser {

    private boolean traversing;
    private float x;
    private float z;
    private GameObjectManager objectManager;
    private BSPTreeTraverseListener listener;

    /**
        Tworzy nowy obiekt BSPTreeTraverser bez obiektu klasy
        BSPTreeTraverseListener.
    */
    public BSPTreeTraverser() {
        this(null);
    }


    /**
        Tworzy nowy obiekt BSPTreeTraverser ze wskazanym obiektem
        klasy BSPTreeTraverseListener.
    */
    public BSPTreeTraverser(BSPTreeTraverseListener listener) {
        setListener(listener);
    }


    /**
        Ustawia obiekt BSPTreeTraverseListener wykorzystywany podczas
        przeglądania drzewa.
    */
    public void setListener(BSPTreeTraverseListener listener) {
        this.listener = listener;
    }


    /**
        Ustawia obiekt GameObjectManager. Jeśli podczas przeglądania
        obiekt GameObjectManager jest różny od null, wówczas wywoływana
        jest metoda markVisible() menadżera, która określa widoczne
        części drzewa.
    */
    public void setGameObjectManager(
        GameObjectManager objectManager)
    {
        this.objectManager = objectManager;
    }


    /**
        Przegląda węzły drzewa w kolejności rysowania reprezentowanych przez
        nie wielokątów (od przodu do tyłu) dla określonej pozycji kamery.
    */
    public void traverse(BSPTree tree, Vector3D viewLocation) {
        x = viewLocation.x;
        z = viewLocation.z;
        traversing = true;
        traverseDrawOrder(tree.getRoot());
    }


    /**
        Przegląda węzły drzewa w porządku poprzecznym.
    */
    public void traverse(BSPTree tree) {
        traversing = true;
        traverseInOrder(tree.getRoot());
    }


    /**
        Przegląda węzeł w kolejności rysowania (od przodu do tyłu) dla
        określonej pozycji kamery.
    */
    private void traverseDrawOrder(BSPTree.Node node) {
        if (traversing && node != null) {
            if (node instanceof BSPTree.Leaf) {
                // brak podziałów, tylko obsługa wielokątów
                visitNode(node);
            }
            else if (node.partition.getSideThin(x,z) != BSPLine.BACK) {
                traverseDrawOrder(node.front);
                visitNode(node);
                traverseDrawOrder(node.back);
            }
            else {
                traverseDrawOrder(node.back);
                visitNode(node);
                traverseDrawOrder(node.front);
            }
        }
    }



    /**
        Przegląda węzeł w porządku poprzecznym.
    */
    private void traverseInOrder(BSPTree.Node node) {
        if (traversing && node != null) {
            traverseInOrder(node.front);
            visitNode(node);
            traverseInOrder(node.back);
        }
    }


    /**
        Odwiedza węzeł drzewa BSP. Metoda visitPolygon() interfejsu
        BSPTreeTraverseListener jest wywoływana dla każdego wielokąta
        w danym węźle.
    */
    private void visitNode(BSPTree.Node node) {
        if (!traversing || node.polygons == null) {
            return;
        }

        boolean isBack = false;
        if (node instanceof BSPTree.Leaf) {
            BSPTree.Leaf leaf = (BSPTree.Leaf)node;
            isBack = leaf.isBack;
            // oznacza ograniczenia tego liścia jako obszar widoczny
            // w menadżerze obiektów występujących w grze.
            if (objectManager != null && leaf.bounds != null) {
                objectManager.markVisible(leaf.bounds);
            }
        }

        // odwiedza każdy wielokąt
        for (int i=0; traversing && i<node.polygons.size(); i++) {
            BSPPolygon poly = (BSPPolygon)node.polygons.get(i);
            traversing = listener.visitPolygon(poly, isBack);
        }
    }


}
