import theater.*;

import java.util.List;


/**
 * Die Klasse stellt eine Repraesentation des Mädchens dar.
 *
 * @author Dietrich Boles (Universitaet Oldenburg)
 * @version 1.0 (29.09.2009)
 *
 */
public class Maedchen extends Actor {
    private static TheaterImage lebendImage = new TheaterImage("maedchen.gif");
    private static TheaterImage totImage = new TheaterImage("grab.gif");
    private boolean added; // internes Flag

    // Default-Konstruktor (muss implementiert werden!)
    public Maedchen() {
        super(); // nicht loeschen!

        setImage(lebendImage); // anfangs lebt das Mädchen
        setZCoordinate(3); // Mädchen ist immer im Vordergrund
        added = false;
    }

    // Copy-Konstruktor (muss implementiert werden!)
    public Maedchen(Maedchen actor) {
        super(actor); // nicht loeschen!

        added = false;
    }

    @Invisible
    public void addedToStage(Stage stage) {
        added = true;
    }

    @Description("<html>Das Mädchen dreht sich um 90 Grad nach rechts.</html>")
    public void rechtsUm() {
        Performance.getPerformance().lock();

        try {
            setRotation((getRotation() + 90) % 360);
        } finally {
            Performance.getPerformance().unlock();
        }
    }

    @Description("<html>Das Mädchen dreht sich um 90 Grad nach links.</html>")
    public void linksUm() {
        Performance.getPerformance().lock();
        Performance.getPerformance().freeze();

        try {
            rechtsUm();
            rechtsUm();
            rechtsUm();
        } finally {
            Performance.getPerformance().unfreeze();
            Performance.getPerformance().unlock();
        }
    }

    @Description("<html>Das Mädchen bewegt sich in seiner aktuellen Blickrichtung " +
    "nach vorne.<br>Fehler, wenn sich vor dem Mädchen ein Geist befindet.</html>")
    public void vor() {
        Performance.getPerformance().lock();

        try {
            Position vorne = getVorne();
            setLocation(vorne.col, vorne.row);
        } finally {
            Performance.getPerformance().unlock();
        }
    }

    @Description("<html>Das Mädchen versucht, einen sich unmittelbar vor " +
    "ihm befindlichen Geist zu vertreiben.<br>" +
    "Fehler, wenn sich gar kein Geist vor dem Mädchen befindet.</html>")
    public void geistVertreiben() {
        Performance.getPerformance().lock();

        try {
            if (!vorneGeist()) {
                sterben();
            }

            Position vorne = getVorne();
            Geist geist = (Geist) getStage()
                                      .getComponentsAt(vorne.col, vorne.row,
                    Geist.class).get(0);
            geist.vertreiben();
        } finally {
            Performance.getPerformance().unlock();
        }
    }

    @Description("Liefert genau dann true, wenn sich vor dem Mädchen ein Geist befindet.")
    public boolean vorneGeist() {
        Performance.getPerformance().lock();

        try {
            Position vorne = getVorne();

            return getStage().getComponentsAt(vorne.col, vorne.row, Geist.class)
                       .size() > 0;
        } finally {
            Performance.getPerformance().unlock();
        }
    }

    @Description("Liefert genau dann true, wenn sich das Mädchen auf einem Kürbis befindet.")
    public boolean kuerbisDa() {
        Performance.getPerformance().lock();

        try {
            return getStage()
                       .getComponentsAt(getColumn(), getRow(), Kuerbis.class)
                       .size() > 0;
        } finally {
            Performance.getPerformance().unlock();
        }
    }

    @Invisible
    public void setLocation(int col, int row) {
        Performance.getPerformance().lock();
        Performance.getPerformance().freeze();

        try {
            if (col < 0) {
                col = 0;
            } else if (col >= getStage().getNumberOfColumns()) {
                col = getStage().getNumberOfColumns() - 1;
            }

            if (row < 0) {
                row = 0;
            } else if (row >= getStage().getNumberOfRows()) {
                row = getStage().getNumberOfRows() - 1;
            }

            super.setLocation(col, row);

            if (!added) { // Mädchen wird im Territorium platziert

                return;
            }

            // ab hier: Mädchen wird verschoben

            // evtl. muss die Kürbisgröße angepasst werden
            List<Component> kuerbisse = getStage().getComponents(Kuerbis.class);

            for (Component kuerbis : kuerbisse) {
                ((Kuerbis) kuerbis).checkKuerbisSize(col, row);
            }

            // alle Geister unscihtbar machen
            List<Component> geister = getStage().getComponents(Geist.class);

            for (Component geist : geister) {
                geist.setVisible(false);
            }

            // befindet sich auf dem neuen Feld ein Geist?
            geister = getStage().getComponentsAt(col, row, Geist.class);

            if (geister.size() > 0) {
                sterben((Geist) geister.get(0));
            } else {
                if (getImage() != lebendImage) {
                    setImage(lebendImage);
                }
            }
        } finally {
            Performance.getPerformance().unlock();
            Performance.getPerformance().unfreeze();
        }
    }

    // Mädchen ist auf einen Geist gelaufen
    @Invisible
    private void sterben(Geist geist) {
        Performance.getPerformance().lock();

        try {
            Performance.getPerformance().playSound("lachen.wav");
            geist.setVisible(true);
            setImage(totImage);
            throw new RuntimeException(
                "Der Geist hat das Mädchen (und dein Programm) zu Tode erschreckt!");
        } finally {
            Performance.getPerformance().unlock();
        }
    }

    // Mädchen hat versucht, einen Geist zu vertreiben, obwohl keiner da war
    @Invisible
    private void sterben() {
        Performance.getPerformance().lock();

        try {
            // alle Geister sichtbar machen und lachen lassen
            for (Component geist : getStage().getComponents(Geist.class)) {
                Performance.getPerformance().playSound("lachen.wav");
                ((Geist) geist).setVisible(true);
            }

            setImage(totImage);
            throw new RuntimeException(
                "Vor dem Mädchen befindet sich kein Geist");
        } finally {
            Performance.getPerformance().unlock();
        }
    }

    // Umrechnung der Spalte im Torus
    @Invisible
    private int calcColumn(int col) {
        col = col % getStage().getNumberOfColumns();

        if (col < 0) {
            col = getStage().getNumberOfColumns() + col;
        }

        return col;
    }

    // Umrechnung der Reihe im Torus
    @Invisible
    private int calcRow(int row) {
        row = row % getStage().getNumberOfRows();

        if (row < 0) {
            row = getStage().getNumberOfRows() + row;
        }

        return row;
    }

    // Berechnung des Feldes vor dem Mädchen (im Torus)
    @Invisible
    private Position getVorne() {
        int col = getColumn();
        int row = getRow();

        switch (getRotation()) {
        case 0: // Ost
            return new Position(calcColumn(col + 1), row);

        case 270: // Nord
            return new Position(col, calcRow(row - 1));

        case 180: // West
            return new Position(calcColumn(col - 1), row);

        default: //90 = Sued

            return new Position(col, calcRow(row + 1));
        }
    }
}


class Position {
    int col;
    int row;

    Position(int c, int r) {
        col = c;
        row = r;
    }
}
