package com.brackeen.javagamebook.math3D;

import java.util.List;
import java.util.ArrayList;


/**
    PolygonGroup reprezentuje grupę wielokątów z obiektem klasy
    MovingTransform3D. Obiekty PolygonGroup mogą także zawierać
    inne obiekty PolygonGroup.
*/
public class PolygonGroup implements Transformable {

    private String name;
    private String filename;
    private List objects;
    private MovingTransform3D transform;
    private int iteratorIndex;


    /**
        Tworzy nowy pusty obiekt PolygonGroup.
    */
    public PolygonGroup() {
        this("unnamed");
    }


    /**
        Tworzy nowy pusty obiekt PolygonGroup z określoną nazwą.
    */
    public PolygonGroup(String name) {
        setName(name);
        objects = new ArrayList();
        transform = new MovingTransform3D();
        iteratorIndex = 0;
    }


    /**
        Zwraca MovingTransform3D dla tego obiektu PolygonGroup.
    */
    public MovingTransform3D getTransform() {
        return transform;
    }


    /**
        Zwraca nazwę tego obiektu PolygonGroup.
    */
    public String getName() {
        return name;
    }


    /**
        Ustawia nazwę tego obiektu PolygonGroup.
    */
    public void setName(String name) {
        this.name = name;
    }


    /**
        Zwraca nazwę pliku dla tego obiektu PolygonGroup.
    */
    public String getFilename() {
        return filename;
    }


    public boolean isEmpty() {
        return objects.isEmpty();
    }


    /**
        Ustawia nazwę pliku dla tego obiektu PolygonGroup.
    */
    public void setFilename(String filename) {
        this.filename = filename;
    }


    /**
        Dodaje wielokąt do tej grupy.
    */
    public void addPolygon(Polygon3D o) {
        objects.add(o);
    }


    /**
        Dodaje obiekt PolygonGroup do tej grupy.
    */
    public void addPolygonGroup(PolygonGroup p) {
        objects.add(p);
    }


    /**
        Kopiuje tę grupę wielokątów. Obiekty Polygon3D są współdzielone
        przez tę grupę i kopię grupy; Obiekty Transform3D są kopiowane.
    */
    public Object clone() {
        PolygonGroup group = new PolygonGroup(name);
        group.setFilename(filename);
        for (int i=0; i<objects.size(); i++) {
            Object obj = objects.get(i);
            if (obj instanceof Polygon3D) {
                group.addPolygon((Polygon3D)obj);
            }
            else {
                PolygonGroup grp = (PolygonGroup)obj;
                group.addPolygonGroup((PolygonGroup)grp.clone());
            }
        }
        group.transform = (MovingTransform3D)transform.clone();
        return group;
    }


    /**
        Zwraca należący do tej grupy obiekt PolygonGroup o podanej
        nazwie lub null, jeśli grupa o takiej nazwie nie istnieje.
    */
    public PolygonGroup getGroup(String name) {
        // sprawdza nazwę tej grupy
        if (this.name != null && this.name.equals(name)) {
            return this;
        }
        for (int i=0; i<objects.size(); i++) {
            Object obj = objects.get(i);
            if (obj instanceof PolygonGroup) {
                PolygonGroup subgroup =
                    ((PolygonGroup)obj).getGroup(name);
                if (subgroup != null) {
                    return subgroup;
                }
            }
        }

        // grupa o podanej nazwie nie została znaleziona
        return null;
    }


    /**
        Zeruje iterator wielokątów dla tej grupy.
        @see #hasNext
        @see #nextPolygon
    */

    public void resetIterator() {
        iteratorIndex = 0;
        for (int i=0; i<objects.size(); i++) {
            Object obj = objects.get(i);
            if (obj instanceof PolygonGroup) {
                ((PolygonGroup)obj).resetIterator();
            }
        }
    }


    /**
        Sprawdza, czy istnieje kolejny wielokąt w bieżącej
        iteracji.
        @see #resetIterator
        @see #nextPolygon
    */

    public boolean hasNext() {
        return (iteratorIndex < objects.size());
    }


    /**
        Zwraca kolejny wielokąt z bieżącej iteracji.
        @see #resetIterator
        @see #hasNext
    */

    public Polygon3D nextPolygon() {
        Object obj = objects.get(iteratorIndex);

        if (obj instanceof PolygonGroup) {
            PolygonGroup group = (PolygonGroup)obj;
            Polygon3D poly = group.nextPolygon();
            if (!group.hasNext()) {
                iteratorIndex++;
            }
            return poly;
        }
        else {
            iteratorIndex++;
            return (Polygon3D)obj;
        }
    }


    /**
        Zwraca kolejny wielokąt z bieżącej iteracji, wykonuje operacje
        ruchu (MovingTransform3D) i zapisuje go w zmiennej cache.
    */
    public void nextPolygonTransformed(Polygon3D cache) {
        Object obj = objects.get(iteratorIndex);

        if (obj instanceof PolygonGroup) {
            PolygonGroup group = (PolygonGroup)obj;
            group.nextPolygonTransformed(cache);
            if (!group.hasNext()) {
                iteratorIndex++;
            }
        }
        else {
            iteratorIndex++;
            cache.setTo((Polygon3D)obj);
        }

        cache.add(transform);
    }


    /**
        Aktualizuje obiekty MovingTransform3D tej grupy i wszystkich
        podgrup.
    */
    public void update(long elapsedTime) {
        transform.update(elapsedTime);
        for (int i=0; i<objects.size(); i++) {
            Object obj = objects.get(i);
            if (obj instanceof PolygonGroup) {
                PolygonGroup group = (PolygonGroup)obj;
                group.update(elapsedTime);
            }
        }
    }

    // z interfejsu Transformable

    public void add(Vector3D u) {
        transform.getLocation().add(u);
    }

    public void subtract(Vector3D u) {
        transform.getLocation().subtract(u);
    }

    public void add(Transform3D xform) {
        addRotation(xform);
        add(xform.getLocation());
    }

    public void subtract(Transform3D xform) {
        subtract(xform.getLocation());
        subtractRotation(xform);
    }

    public void addRotation(Transform3D xform) {
        transform.rotateAngleX(xform.getAngleX());
        transform.rotateAngleY(xform.getAngleY());
        transform.rotateAngleZ(xform.getAngleZ());
    }

    public void subtractRotation(Transform3D xform) {
        transform.rotateAngleX(-xform.getAngleX());
        transform.rotateAngleY(-xform.getAngleY());
        transform.rotateAngleZ(-xform.getAngleZ());
    }

}
