package com.brackeen.javagamebook.tilegame.sprites;

import java.lang.reflect.Constructor;
import com.brackeen.javagamebook.graphics.*;

/**
    Klasa Creature dziedziczy po Sprite dodatkowo ma na nią wpływa 
    grawitacja oraz może zginąć. Posiada cztery obiekty
    Animation: przesuwanie w lewo, przesuwanie w prawo, śmierć w lewo
    i śmierć w prawo.
*/
public abstract class Creature extends Sprite {

    /**
        Czas przechodzenia ze stanu STATE_DYING do STATE_DEAD.
    */
    private static final int DIE_TIME = 1000;

    public static final int STATE_NORMAL = 0;
    public static final int STATE_DYING = 1;
    public static final int STATE_DEAD = 2;

    private Animation left;
    private Animation right;
    private Animation deadLeft;
    private Animation deadRight;
    private int state;
    private long stateTime;

    /**
        Tworzenie nowego obiektu Creature z określonymi obiektami Animations.
    */
    public Creature(Animation left, Animation right,
        Animation deadLeft, Animation deadRight)
    {
        super(right);
        this.left = left;
        this.right = right;
        this.deadLeft = deadLeft;
        this.deadRight = deadRight;
        state = STATE_NORMAL;
    }


    public Object clone() {
        // wykorzystanie refleksji w celu utworzenia właściwych klas pochodnych
        Constructor constructor = getClass().getConstructors()[0];
        try {
            return constructor.newInstance(new Object[] {
                (Animation)left.clone(),
                (Animation)right.clone(),
                (Animation)deadLeft.clone(),
                (Animation)deadRight.clone()
            });
        }
        catch (Exception ex) {
            // nie powinno się nigdy zdarzyć
            ex.printStackTrace();
            return null;
        }
    }

    /**
        Zwraca maksymalną prędkość tego obiektu.
    */
    public float getMaxSpeed() {
        return 0;
    }

    /**
        Budzenie stwora po jego pojawieniu się na ekranie. Normalnie
        obiekt zaczyna poruszać się w lewo.
    */
    public void wakeUp() {
        if (getState() == STATE_NORMAL && getVelocityX() == 0) {
            setVelocityX(-getMaxSpeed());
        }
    }

    /**
        Zwraca stan obiektu Creature. Może przyjmować wartości
        STATE_NORMAL, STATE_DYING lub STATE_DEAD.
    */
    public int getState() {
        return state;
    }

    /**
        Ustawia stan obiektu Creature na: STATE_NORMAL,
        STATE_DYING lub STATE_DEAD.
    */
    public void setState(int state) {
        if (this.state != state) {
            this.state = state;
            stateTime = 0;
            if (state == STATE_DYING) {
                setVelocityX(0);
                setVelocityY(0);
            }
        }
    }

    /**
        Sprawdza, czy stwór jest żywy.
    */
    public boolean isAlive() {
        return (state == STATE_NORMAL);
    }

    /**
        Sprawdza, czy stwór lata.
    */
    public boolean isFlying() {
        return false;
    }

    /**
        Wywoływana przed update() jeżeli stwór wszedł poziomo z kafelkiem.
    */
    public void collideHorizontal() {
        setVelocityX(-getVelocityX());
    }

    /**
        Wywoływana przed update() jeżeli stwór wszedł pionowo z kafelkiem.
    */
    public void collideVertical() {
        setVelocityY(0);
    }

    /**
        Aktualizuje animację stwora.
    */
    public void update(long elapsedTime) {
        // wybranie odpowiedniego obiektu Animation
        Animation newAnim = anim;
        if (getVelocityX() < 0) {
            newAnim = left;
        }
        else if (getVelocityX() > 0) {
            newAnim = right;
        }
        if (state == STATE_DYING && newAnim == left) {
            newAnim = deadLeft;
        }
        else if (state == STATE_DYING && newAnim == right) {
            newAnim = deadRight;
        }

        // aktualizacja obiektu Animation
        if (anim != newAnim) {
            anim = newAnim;
            anim.start();
        }
        else {
            anim.update(elapsedTime);
        }

        // zmiana na stan "martwy"
        stateTime += elapsedTime;
        if (state == STATE_DYING && stateTime >= DIE_TIME) {
            setState(STATE_DEAD);
        }
    }
}
