package com.brackeen.javagamebook.game;

import java.awt.Rectangle;
import java.awt.Graphics2D;
import java.util.*;
import com.brackeen.javagamebook.game.GameObjectRenderer;

/**
    Klasa SimpleGameObjectManager jest implementacją interfejsu
    GameObjectManager, która utrzymuje wszystkie obiekty na liście
    i nie wykonuje żadnych operacji wykrywania kolizji.
*/
public class SimpleGameObjectManager implements GameObjectManager {

    private List allObjects;
    private List visibleObjects;
    private List spawnedObjects;
    private GameObject player;

    /**
        Tworzy nowy obiekt klasy SimpleGameObjectManager.
    */
    public SimpleGameObjectManager() {
        allObjects = new ArrayList();
        visibleObjects = new ArrayList();
        spawnedObjects = new ArrayList();
        player = null;
    }


    /**
        Oznacza wszystkie obiekty jako potencjalnie widoczne
        (czyli takie, które powinny być rysowane).
    */
    public void markAllVisible() {
        for (int i=0; i<allObjects.size(); i++) {
            GameObject object = (GameObject)allObjects.get(i);
            if (!visibleObjects.contains(object)) {
                visibleObjects.add(object);
            }
        }
    }


    /**
        Oznacza wszystkie obiekty w danym dwuwymiarowym obszarze
        jako potencjalnie widoczne (czyli takie, które powinny być
        rysowane).
    */
    public void markVisible(Rectangle bounds) {
        for (int i=0; i<allObjects.size(); i++) {
            GameObject object = (GameObject)allObjects.get(i);
            if (bounds.contains(object.getX(), object.getZ()) &&
                !visibleObjects.contains(object))
            {
                visibleObjects.add(object);
            }
        }
    }

    public Iterator iterator() {
        return allObjects.iterator();
    }

    /**
        Dodaje obiekt klasy GameObject do tego menadżera.
    */
    public void add(GameObject object) {
        if (object != null) {
            allObjects.add(object);
        }
    }


    /**
        Dodaje obiekt klasy GameObject do tego menadżera,
        określając go jako obiekt gracza. Aktualny obiekt
        gracza (jeśli taki istnieje) nie jest usuwany.
    */
    public void addPlayer(GameObject player) {
        this.player = player;
        if (player != null) {
            player.notifyVisible(true);
            allObjects.add(0, player);
        }
    }


    /**
        Zwraca obiekt oznaczony jako obiekt gracza lub wartość
        null, jeśli nie wyznaczono żadnego obiektu gracza.
    */
    public GameObject getPlayer() {
        return player;
    }


    /**
        Usuwa wskazany GameObject z tego menadżera.
    */
    public void remove(GameObject object) {
        allObjects.remove(object);
        visibleObjects.remove(object);
    }


    /**
        Aktualizuje wszystkie obiektu w oparciu o czas jaki upłynął
        od ostatniej aktualizacji.
    */
    public void update(long elapsedTime) {
        for (int i=0; i<allObjects.size(); i++) {
            GameObject object = (GameObject)allObjects.get(i);

            // przenieś obiekt
            object.update(player, elapsedTime);

            // śledź wszystkie nowe obiekty (dodawane za chwilę)
            List spawns = object.getSpawns();
            if (spawns != null) {
                spawnedObjects.addAll(spawns);
            }

            // usuń zniszczone obiekty
            if (object.isDestroyed()) {
                allObjects.remove(i);
                visibleObjects.remove(object);
                i--;
            }
        }

        // dodaj nowe obiekty
        if (spawnedObjects.size() > 0) {
            for (int i=0; i<spawnedObjects.size(); i++) {
                add((GameObject)spawnedObjects.get(i));
            }
            spawnedObjects.clear();
        }
    }


    /**
        Rysuje wszystkie widoczne obiekty po czym oznacza wszystkie
        obiekty jako niewidoczne.
    */
    public void draw(Graphics2D g, GameObjectRenderer r) {
        Iterator i = visibleObjects.iterator();
        while (i.hasNext()) {
            GameObject object = (GameObject)i.next();
            boolean visible = r.draw(g, object);
            // poinformuj odpowiednie obiekty, że są widoczne w tej
            // klatce
            object.getListener().notifyVisible(object, visible);
        }
        visibleObjects.clear();
    }
}
