import theater.*;

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


public final class Board extends Stage {
    final static int NUMBER_OF_CHOICES = 5;
    final static int PLAYER_CHOICE = 1;
    final static int GAME = 2;
    final static int WAIT = 3;
    Space[][] board;
    Game game;
    boolean end;
    boolean started;
    Brain brainA;
    Brain brainB;
    ChoiceBrain[] choiceBrainsA;
    ChoiceBrain[] choiceBrainsB;
    int gameState;
    Thread pThread = null;
    ArrayList<Piece> toFront = new ArrayList<Piece>();
    int choiceA = -1;
    int choiceB = -1;
    ChoiceBrain chBrain = null;
    Space toSpace = null;
    boolean humanAct = false;
    Piece piece = null;

    public Board() {
        super(7, 8, 75);
        setBackground("board.gif");
        initChoiceBoard();

        if (pThread != null) {
            // pThread.stop();
            pThread = null;
        }

        Piece.lastTurn = null;
        gameState = PLAYER_CHOICE;
    }

    public Board(Board brett) {
        super(7, 8, 75);
        setBackground("board.gif");
        end = false;
        started = false;
        this.board = new Space[8][7];

        for (int r = 0; r < 8; r++) {
            for (int s = 0; s < 7; s++) {
                this.board[r][s] = new Space(brett.board[r][s]);
                this.board[r][s].setBrett(this);
            }
        }
    }

    private void initChoiceBoard() {
        end = false;
        started = false;

        game = new Game(this);

        choiceBrainsA = new ChoiceBrain[NUMBER_OF_CHOICES];

        for (int i = 0; i < NUMBER_OF_CHOICES; i++) {
            choiceBrainsA[i] = new ChoiceBrain(true, i);
        }

        choiceBrainsB = new ChoiceBrain[NUMBER_OF_CHOICES];

        for (int i = 0; i < NUMBER_OF_CHOICES; i++) {
            choiceBrainsB[i] = new ChoiceBrain(false, i);
        }

        board = new Space[8][7];

        // erste Reihe
        board[0][0] = new Space(8, 'a', this, Direction.SOUTH, Direction.EAST);
        board[0][1] = new Space(8, 'b', this, Direction.WEST, Direction.SOUTH,
                Direction.EAST);
        board[0][2] = new Space(8, 'c', this, Direction.WEST, Direction.SOUTH,
                Direction.EAST);
        board[0][3] = new Space(8, 'd', this, Direction.WEST, Direction.SOUTH,
                Direction.EAST);
        board[0][4] = new Space(8, 'e', this, Direction.WEST, Direction.SOUTH,
                Direction.EAST);
        board[0][5] = new Space(8, 'f', this, Direction.WEST, Direction.SOUTH,
                Direction.EAST);
        board[0][6] = new Space(8, 'g', this, Direction.SOUTH);

        // zweite Reihe
        board[1][0] = new Space(7, 'a', this, Direction.NORTH, Direction.SOUTH);
        board[1][1] = new Space(7, 'b', this, Direction.NORTHWEST,
                Direction.SOUTH, Direction.EAST);
        board[1][2] = new Space(7, 'c', this, Direction.NORTH, Direction.WEST,
                Direction.SOUTH, Direction.EAST);
        board[1][3] = new Space(7, 'd', this, Direction.WEST, Direction.SOUTH,
                Direction.EAST);
        board[1][4] = new Space(7, 'e', this, Direction.NORTH, Direction.WEST,
                Direction.SOUTH, Direction.EAST);
        board[1][5] = new Space(7, 'f', this, Direction.WEST, Direction.SOUTH);
        board[1][6] = new Space(7, 'g', this, Direction.NORTH, Direction.SOUTH);

        // dritte Reihe
        board[2][0] = new Space(6, 'a', this, Direction.NORTH, Direction.SOUTH,
                Direction.EAST);
        board[2][1] = new Space(6, 'b', this, Direction.NORTH, Direction.WEST,
                Direction.SOUTH, Direction.EAST);
        board[2][2] = new Space(6, 'c', this, Direction.NORTHWEST,
                Direction.SOUTHWEST, Direction.SOUTHEAST, Direction.NORTHEAST);
        board[2][3] = new Space(6, 'd', this, Direction.WEST, Direction.EAST);
        board[2][4] = new Space(6, 'e', this, Direction.NORTHWEST,
                Direction.SOUTHWEST, Direction.SOUTHEAST, Direction.NORTHEAST);
        board[2][5] = new Space(6, 'f', this, Direction.NORTH, Direction.WEST,
                Direction.EAST);
        board[2][6] = new Space(6, 'g', this, Direction.NORTH, Direction.WEST,
                Direction.SOUTH);

        // vierte Reihe
        board[3][0] = new Space(5, 'a', this, Direction.SOUTHEAST,
                Direction.EAST, Direction.NORTHEAST);
        board[3][1] = new Space(5, 'b', this, Direction.EAST);
        board[3][2] = new Space(5, 'c', this, Direction.NORTH, Direction.SOUTH,
                Direction.EAST);
        board[3][3] = new Space(5, 'd', this, Direction.NORTH,
                Direction.NORTHWEST, Direction.WEST, Direction.SOUTHWEST,
                Direction.SOUTH, Direction.SOUTHEAST, Direction.EAST,
                Direction.NORTHEAST);
        board[3][4] = new Space(5, 'e', this, Direction.NORTH, Direction.SOUTH,
                Direction.EAST);
        board[3][5] = new Space(5, 'f', this, Direction.NORTH, Direction.WEST,
                Direction.SOUTH, Direction.EAST);
        board[3][6] = new Space(5, 'g', this, Direction.NORTH, Direction.SOUTH);

        // fuenfte Reihe
        board[4][0] = new Space(4, 'a', this, Direction.NORTH, Direction.SOUTH);
        board[4][1] = new Space(4, 'b', this, Direction.NORTH, Direction.WEST,
                Direction.SOUTH, Direction.EAST);
        board[4][2] = new Space(4, 'c', this, Direction.NORTH, Direction.WEST,
                Direction.SOUTH);
        board[4][3] = new Space(4, 'd', this, Direction.NORTH,
                Direction.NORTHWEST, Direction.WEST, Direction.SOUTHWEST,
                Direction.SOUTH, Direction.SOUTHEAST, Direction.EAST,
                Direction.NORTHEAST);
        board[4][4] = new Space(4, 'e', this, Direction.NORTH, Direction.WEST,
                Direction.SOUTH);
        board[4][5] = new Space(4, 'f', this, Direction.WEST);
        board[4][6] = new Space(4, 'g', this, Direction.NORTHWEST,
                Direction.WEST, Direction.SOUTHWEST);

        // sechste Reihe
        board[5][0] = new Space(3, 'a', this, Direction.NORTH, Direction.SOUTH,
                Direction.EAST);
        board[5][1] = new Space(3, 'b', this, Direction.WEST, Direction.SOUTH,
                Direction.EAST);
        board[5][2] = new Space(3, 'c', this, Direction.NORTHWEST,
                Direction.SOUTHWEST, Direction.SOUTHEAST, Direction.NORTHEAST);
        board[5][3] = new Space(3, 'd', this, Direction.WEST, Direction.EAST);
        board[5][4] = new Space(3, 'e', this, Direction.NORTHWEST,
                Direction.SOUTHWEST, Direction.SOUTHEAST, Direction.NORTHEAST);
        board[5][5] = new Space(3, 'f', this, Direction.NORTH, Direction.WEST,
                Direction.SOUTH, Direction.EAST);
        board[5][6] = new Space(3, 'g', this, Direction.NORTH, Direction.WEST,
                Direction.SOUTH);

        // siebte Reihe
        board[6][0] = new Space(2, 'a', this, Direction.NORTH, Direction.SOUTH);
        board[6][1] = new Space(2, 'b', this, Direction.NORTH, Direction.EAST);
        board[6][2] = new Space(2, 'c', this, Direction.NORTH, Direction.WEST,
                Direction.SOUTH, Direction.EAST);
        board[6][3] = new Space(2, 'd', this, Direction.NORTH, Direction.WEST,
                Direction.EAST);
        board[6][4] = new Space(2, 'e', this, Direction.NORTH, Direction.WEST,
                Direction.SOUTH, Direction.EAST);
        board[6][5] = new Space(2, 'f', this, Direction.NORTH, Direction.WEST,
                Direction.SOUTHEAST);
        board[6][6] = new Space(2, 'g', this, Direction.NORTH, Direction.SOUTH);

        // achte Reihe
        board[7][0] = new Space(1, 'a', this, Direction.NORTH);
        board[7][1] = new Space(1, 'b', this, Direction.NORTH, Direction.WEST,
                Direction.EAST);
        board[7][2] = new Space(1, 'c', this, Direction.NORTH, Direction.WEST,
                Direction.EAST);
        board[7][3] = new Space(1, 'd', this, Direction.NORTH, Direction.WEST,
                Direction.EAST);
        board[7][4] = new Space(1, 'e', this, Direction.NORTH, Direction.WEST,
                Direction.EAST);
        board[7][5] = new Space(1, 'f', this, Direction.NORTH, Direction.WEST,
                Direction.EAST);
        board[7][6] = new Space(1, 'g', this, Direction.NORTH, Direction.WEST);

        // besetzen
        board[0][1].setFigur(new Numskull(false));
        board[0][2].setFigur(new Numskull(false));
        // board[0][3].setFigur(brainB = new Brain(false));
        board[0][4].setFigur(new Numskull(false));
        board[0][5].setFigur(new Numskull(false));
        board[1][0].setFigur(new Ninny(false));
        board[1][1].setFigur(new Ninny(false));
        board[1][2].setFigur(new Ninny(false));
        board[1][3].setFigur(new Ninny(false));
        board[1][4].setFigur(new Ninny(false));
        board[1][5].setFigur(new Ninny(false));
        board[1][6].setFigur(new Ninny(false));

        board[6][0].setFigur(new Ninny(true));
        board[6][1].setFigur(new Ninny(true));
        board[6][2].setFigur(new Ninny(true));
        board[6][3].setFigur(new Ninny(true));
        board[6][4].setFigur(new Ninny(true));
        board[6][5].setFigur(new Ninny(true));
        board[6][6].setFigur(new Ninny(true));
        board[7][1].setFigur(new Numskull(true));
        board[7][2].setFigur(new Numskull(true));
        // board[7][3].setFigur(brainA = new Brain(true));
        board[7][4].setFigur(new Numskull(true));
        board[7][5].setFigur(new Numskull(true));

        for (int i = 1; i <= NUMBER_OF_CHOICES; i++) {
            board[3][i].setFigur(choiceBrainsB[i - 1]);
            board[4][i].setFigur(choiceBrainsA[i - 1]);
        }

        add(new CurrentSpace(), 3, 7);
        add(new PossibleSpace(), 3, 0);
    }

    private void initBoard() {
        end = false;
        started = false;

        game = new Game(this, choiceA, choiceB);

        board[0][3].setFigur(brainB = new Brain(false));
        board[7][3].setFigur(brainA = new Brain(true));

        for (int i = 1; i <= NUMBER_OF_CHOICES; i++) {
            board[3][i].setFigur(null);
            board[4][i].setFigur(null);
        }
    }

    public Game getGame() {
        return this.game;
    }

    public Space getFeld(int reihe, char spalte) {
        if ((reihe < 1) || (reihe > 8) || (spalte < 'a') || (spalte > 'g')) {
            return null;
        }

        return board[8 - reihe][spalte - 'a'];
    }

    public Space getFeld(int reihe, int spalte) {
        try {
            return board[reihe][spalte];
        } catch (ArrayIndexOutOfBoundsException exc) {
            return null;
        }
    }

    public Space[][] getBrettArray() {
        return board;
    }

    public void fuehreSpielzugAus(Turn zug) {
        if (zug == null) {
            return;
        }

        Space vonFeld = board[8 - zug.getVonReihe()][zug.getVonSpalte() - 'a'];
        Space nachFeld = board[8 - zug.getNachReihe()][zug.getNachSpalte() -
            'a'];
        Piece figur = vonFeld.getFigur();
        vonFeld.entferneFigur();

        if (nachFeld.hatFigur()) {
            nachFeld.entferneFigur();
        }

        if (this.checkNinnyUmwandlung(figur, nachFeld)) {
            figur = new Numskull(figur.isA());
        }

        nachFeld.setFigur(figur);
    }

    boolean checkNinnyUmwandlung(Piece figur, Space feld) {
        if (!(figur.getClass() == Ninny.class)) {
            return false;
        }

        boolean figurA = figur.isA();
        int reihe = feld.getReihe();
        char spalte = feld.getSpalte();

        if (figurA) {
            return (reihe == 8) &&
            ((spalte == 'b') || (spalte == 'c') || (spalte == 'e') ||
            (spalte == 'f'));
        } else {
            return (reihe == 1) &&
            ((spalte == 'b') || (spalte == 'c') || (spalte == 'e') ||
            (spalte == 'f'));
        }
    }

    void setFront(Piece piece) {
        toFront.add(piece);
        piece.setZCoordinate(1);
    }

    public void run() {
        switch (gameState) {
        case PLAYER_CHOICE:
            handlePlayerChoice();

            break;

        case GAME:
        case WAIT:
            handleGame();

            break;
        }
    }

    private void handlePlayerChoice() {
        for (Piece piece : toFront) {
            int x = piece.getColumn();
            int y = piece.getRow();
            piece.setLocation(x, y);
            piece.setZCoordinate(0);
        }

        toFront.clear();
    }

    private void handleGame() {
        if (end) {
            Piece.lastTurn = null;
            Performance.getPerformance().stop();

            return;
        }

        if (!started) {
            started = true;

            Player currentPlayer = this.game.getAktuellerSpieler();

            for (Piece p : getPieces(this.game.getAktuellerSpieler().isSpielerA())) {
                if (currentPlayer.isHuman()) {
                    p.setNormalFinger();
                } else {
                    p.setDenken();
                }
            }
        }

        for (Piece piece : toFront) {
            int x = piece.getColumn();
            int y = piece.getRow();
            piece.setLocation(x, y);
            piece.setZCoordinate(0);
        }

        toFront.clear();
        this.paint();
        checkBothPrograms();
    }

    public ArrayList<Piece> getPieces(boolean isA) {
        ArrayList<Piece> list = new ArrayList<Piece>();

        for (int r = 0; r < 8; r++) {
            for (int s = 0; s < 7; s++) {
                Piece p = board[r][s].getFigur();

                if ((p != null) && (p.isA() == isA)) {
                    list.add(p);
                }
            }
        }

        return list;
    }

    private void checkBothPrograms() {
        Player currentPlayer = game.getAktuellerSpieler();
        Player otherPlayer = game.getOtherPlayer();

        if (!currentPlayer.isHuman() && !otherPlayer.isHuman()) {
            if (currentPlayer.isSpielerA()) {
                brainA.actProgram();
            } else {
                brainB.actProgram();
            }
        }
    }

    void setPlayerAChoice(int staerke) {
        choiceA = staerke;

        if (choiceB != -1) {
            startGame();
        }
    }

    void setPlayerBChoice(int staerke) {
        choiceB = staerke;

        if (choiceA != -1) {
            startGame();
        }
    }

    void startGame() {
        this.remove(this.getComponents(ChoiceBrain.class));
        this.remove(this.getComponents(CurrentSpace.class));
        this.remove(this.getComponents(PossibleSpace.class));
        initBoard();
        gameState = GAME;
    }

    void setHuman(boolean state) {
        humanAct = true;
    }

    public void mousePressed(MouseInfo info) {
        if (gameState == PLAYER_CHOICE) {
            choiceMousePressed(info);
        } else if (humanAct) {
            humanMousePressed(info);
        }
    }

    private void choiceMousePressed(MouseInfo info) {
        List<Component> chBrains = getComponentsAt(info.getColumn(),
                info.getRow(), ChoiceBrain.class);

        if ((chBrains.size() == 0) ||
                ((info.getRow() == 0) && (info.getColumn() == 3)) ||
                ((info.getRow() == 7) && (info.getColumn() == 3))) {
            return;
        }

        chBrain = (ChoiceBrain) chBrains.get(0);
    }

    private void humanMousePressed(MouseInfo info) {
        List<Component> pieces = getComponentsAt(info.getColumn(),
                info.getRow(), Piece.class);

        if (pieces.size() == 0) {
            return;
        }

        piece = (Piece) pieces.get(0);

        Player currentPlayer = game.getAktuellerSpieler();

        if (currentPlayer.isSpielerA() != piece.isA()) {
            piece = null;

            return;
        }
    }

    public void mouseDragged(MouseInfo info) {
        if (gameState == PLAYER_CHOICE) {
            choiceMouseDragged(info);
        } else if (humanAct) {
            humanMouseDragged(info);
        }
    }

    private void choiceMouseDragged(MouseInfo info) {
        if (chBrain == null) {
            return;
        }

        if (!chBrain.isScaled()) {
            setFront(chBrain);
            chBrain.setScaled(true);
            chBrain.scale();
        }

        toSpace = getFeld(info.getRow(), info.getColumn());

        if (toSpace != null) {
            chBrain.setLocation(info.getColumn(), info.getRow());
        }
    }

    private void humanMouseDragged(MouseInfo info) {
        if (piece == null) {
            return;
        }

        Player currentPlayer = game.getAktuellerSpieler();
        Rules rules = game.getRegeln();
        Space space = piece.getSpace();
        Space[] zugFelder = rules.getZugFelder(space, currentPlayer);

        if (!piece.isScaled()) {
            setFront(piece);
            piece.setScaled(true);
            piece.scale();
        }

        space.markCurrent();

        for (Space feld : zugFelder) {
            feld.markPossible();
        }

        toSpace = getFeld(info.getRow(), info.getColumn());

        if (toSpace != null) {
            piece.setLocation(info.getColumn(), info.getRow());
        }

        if (piece.in(zugFelder, toSpace) || (toSpace == space)) {
            piece.setNormalFinger();
            piece.scale();
        } else {
            piece.setSadFinger();
            piece.scale();
        }
    }

    public void mouseReleased(MouseInfo info) {
        if (gameState == PLAYER_CHOICE) {
            choiceMouseReleased(info);
        } else if (humanAct) {
            humanMouseReleased(info);
        }
    }

    private void choiceMouseReleased(MouseInfo info) {
        if (chBrain == null) {
            return;
        }

        chBrain.setScaled(false);

        if (chBrain.isA() && (toSpace != null) && (toSpace.getReihe() == 1) &&
                (toSpace.getSpalte() == 'd')) {
            setPlayerAChoice(chBrain.getStaerke());
        } else if (!chBrain.isA() && (toSpace != null) &&
                (toSpace.getReihe() == 8) && (toSpace.getSpalte() == 'd')) {
            setPlayerBChoice(chBrain.getStaerke());
        } else {
            chBrain.setLocation(chBrain.getSpace().getWorldColumn(),
                chBrain.getSpace().getWorldRow());
        }

        chBrain.reScale();
        chBrain = null;
        toSpace = null;
    }

    private void humanMouseReleased(MouseInfo info) {
        if (piece == null) {
            return;
        }

        Player currentPlayer = game.getAktuellerSpieler();
        Rules rules = game.getRegeln();
        Space space = piece.getSpace();
        Space[] zugFelder = rules.getZugFelder(space, currentPlayer);

        piece.reScale();
        piece.setNormalFinger();
        piece.setScaled(false);

        for (Space feld : zugFelder) {
            feld.unmarkPossible();
        }

        space.unmarkCurrent();

        if (piece.in(zugFelder, toSpace)) {
            Piece.lastTurn = new Turn(space, toSpace);
            fuehreSpielzugAus(Piece.lastTurn);
            piece.checkEnd(currentPlayer);
        } else {
            piece.setLocation(space.getWorldColumn(), space.getWorldRow());
        }
    }
}
