package org.ggp.base.apps.kiosk.games;

import java.awt.Color;
import java.awt.Graphics;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.ggp.base.apps.kiosk.templates.CommonGraphics;
import org.ggp.base.apps.kiosk.templates.GameCanvas_SimpleGrid;
import org.ggp.base.util.statemachine.MachineState;


public class BlokboxSimpleCanvas extends GameCanvas_SimpleGrid {
    private static final long serialVersionUID = 1L;

    @Override
    public String getGameName() { return "Blokbox Simple"; }
    @Override
    protected String getGameKey() { return "blokbox_simple"; }

    protected Set<String> getLegalMovesForCell(int xCell, int yCell) {
        if(selectedPiece == -1) return new HashSet<String>();
        return gameStateHasLegalMovesMatching("\\( place " + selectedPiece + " " + xCell + " " + yCell + " \\)");
    }

    // ========================================================================
    @Override
    protected int getGridHeight() { return 20; }
    @Override
    protected int getGridWidth() { return 20; }

    protected Set<String> getFactsAboutCell(int xCell, int yCell) {
        Set<String> theFacts = gameStateHasFactsMatching("\\( cell " + xCell + " " + yCell + " (.*) \\)");
        if(xCell >= 15 || yCell >= 15) {
            int nPiece = pieceGrid[xCell-1][yCell-1];
            theFacts.addAll(gameStateHasFactsMatching("\\( owns " + myRole + " " + nPiece + " \\)"));
        }
        return theFacts;
    }

    private int[][] pieceGrid = new int[][] {
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 8, 8, 8},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21,21, 0, 8, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0,18, 0,18},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0,18,18,18},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 6, 0, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  6, 6, 0, 5, 5},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0,20, 0, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0,20,20,20, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0,20, 0, 7},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,10, 0, 0, 7},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0,10, 0, 0, 7},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 1, 1, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 1, 1, 0},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14,14, 0, 0,15},
        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 2, 0,15},
        { 0, 0, 0,12,12,12, 0,13,13, 0,11,11,11, 0,14,14, 0, 2, 0,15},
        { 0, 0, 0,12, 0, 0,17, 0,13,13, 0,11, 0, 4, 0, 0, 2, 2, 0,15},
        { 0, 0, 0,12, 0, 0,17,17, 0,13, 0,11, 0, 0,19,19, 0, 0, 0,15},
        { 0, 0, 0, 0,16,16, 0,17, 0, 0, 0, 0, 0,19,19, 0, 3, 0, 0, 0},
        { 0, 0, 0,16,16,16, 0,17, 0, 9, 9, 9, 9, 0,19, 0, 3, 3, 3, 3}
    };

    protected void renderCellBackground(Graphics g, int xCell, int yCell) {
        int width = g.getClipBounds().width;
        int height = g.getClipBounds().height;

        if ((xCell == 15 && yCell <= 15) || (yCell == 15 && xCell <= 15)) {
            if (xCell == 15) width /= 2;
            if (yCell == 15) height /= 2;
            width++;
            height++;

            int xStart = 0, yStart = 0;
            if (xCell == 15 || yCell == 0) xStart++;
            if (yCell == 15 || xCell == 0) yStart++;
            if (xCell == 15 && yCell == 15)
            { xStart--; yStart--; width++; height++; }

            g.setColor(Color.black);
            g.fillRect(xStart, yStart, width, height);
            g.fillRect(xStart, yStart, width, height);
            return;
        }

        if (xCell <= 14 && yCell <= 14) {
            CommonGraphics.drawCellBorder(g);
            return;
        }

        g.setColor(Color.black);
        if (xCell == 16 && yCell == 2) CommonGraphics.fillWithString(g, "B", 2.0);
        if (xCell == 17 && yCell == 2) CommonGraphics.fillWithString(g, "L", 2.0);
        if (xCell == 18 && yCell == 2) CommonGraphics.fillWithString(g, "O", 2.0);
        if (xCell == 19 && yCell == 2) CommonGraphics.fillWithString(g, "K", 2.0);
        if (xCell == 20 && yCell == 2) CommonGraphics.fillWithString(g, "S", 2.0);
    }

    protected void renderCellContent(Graphics g, String theFact) {
        String[] cellFacts = theFact.split(" ");
        if(cellFacts[1].equals("owns")) {
            if (myRole.toString().contains("orange"))
                g.setColor(Color.orange);
            else g.setColor(Color.magenta);

            int width = g.getClipBounds().width;
            int height = g.getClipBounds().height;
            g.fillRect(0, 0, width, height);
            g.setColor(Color.black);
            g.drawRect(0, 0, width, height);
        } else {
            String cellPlayer = cellFacts[4];
            if (cellPlayer.equals("orange")) {
                g.setColor(Color.ORANGE);
            } else if (cellPlayer.equals("purple")) {
                g.setColor(Color.MAGENTA);
            }
            int width = g.getClipBounds().width;
            int height = g.getClipBounds().height;
            g.fillRect(0, 0, width, height);
            g.setColor(Color.black);
            g.drawRect(0, 0, width, height);
        }
    }

    protected void renderMoveSelectionForCell(Graphics g, int xCell, int yCell, String theMove) {
        if (selectedPiece == pieceGrid[xCell-1][yCell-1]) {
            CommonGraphics.drawSelectionBox(g);
            return;
        }

        if (selectedRow == yCell && selectedColumn == xCell) {
            CommonGraphics.drawSelectionBox(g);
            return;
        }
    }


    //////////////////////////////////////////////////////////////////////
    // We need to re-implement part of the FancyGrid architecture, because
    // we need certain parts but need to change other parts (specifically,
    // we want FancyGrid behavior within the 14x14 game grid, but not when
    // the player is interacting with the pieces sidebar).

    protected void renderCellContent(Graphics g, Set<String> theFacts){
        if(!theFacts.isEmpty()) {
            if(theFacts.size() > 1) {
                System.err.println("More than one fact for a cell? Unexpected!");
            }

            String theFact = theFacts.iterator().next();
            renderCellContent(g, theFact);
        }
    }

    protected int selectedPiece = -1;

    private int selectedRow = -1;
    private int selectedColumn = -1;
    private String currentSelectedMove;
    private Iterator<String> possibleSelectedMoves = null;
    @Override
    protected final void handleClickOnCell(int xCell, int yCell, int xWithin, int yWithin) {
        if(xCell > 20 || yCell > 20) return;

        if(xCell > 15 || yCell > 15) {
            int nPiece = pieceGrid[xCell-1][yCell-1];
            selectedPiece = -1;
            if (nPiece > 0 && !getCachedFactsAboutCell(xCell, yCell).isEmpty())
                selectedPiece = nPiece;
            selectedRow = -1;
            selectedColumn = -1;
            currentSelectedMove = "";
            possibleSelectedMoves = null;
            submitWorkingMove(null);
            factsCache.clear();
            return;
        }
        if(selectedPiece == -1) return;

        if(selectedRow != yCell || selectedColumn != xCell || !possibleSelectedMoves.hasNext()) {
            SortedSet<String> theMoves = new TreeSet<String>(getLegalMovesForCell(xCell, yCell));
            if(theMoves.isEmpty())
                return;
            possibleSelectedMoves = theMoves.iterator();
        }
        selectedRow = yCell;
        selectedColumn = xCell;

        currentSelectedMove = possibleSelectedMoves.next();
        submitWorkingMove(stringToMove(currentSelectedMove));
    }

    // Cache all of the facts about cells that we compute, since they should not
    // change unless the game state changes.
    private Map<Integer, Set<String>> factsCache = new HashMap<Integer, Set<String>>();
    protected Set<String> getCachedFactsAboutCell(int xCell, int yCell) {
        int cellHash = xCell*getGridHeight()*2 + yCell;
        Set<String> cachedFacts = factsCache.get(cellHash);
        if(cachedFacts != null)
            return cachedFacts;

        Set<String> realFacts = getFactsAboutCell(xCell, yCell);
        factsCache.put(cellHash, realFacts);
        return realFacts;
    }

    // When the game state changes, clear our cache of known facts.
    @Override
    public void updateGameState(MachineState gameState) {
        factsCache.clear();
        super.updateGameState(gameState);
    }

    @Override
    protected final void renderCell(Graphics g, int xCell, int yCell) {
        renderCellBackground(g, xCell, yCell);
        renderCellContent(g, getCachedFactsAboutCell(xCell, yCell));
        renderMoveSelectionForCell(g, xCell, yCell, currentSelectedMove);
    }

    @Override
    public final void clearMoveSelection() {
        submitWorkingMove(null);

        selectedPiece = -1;
        possibleSelectedMoves = null;
        currentSelectedMove = "";
        selectedColumn = -1;
        selectedRow = -1;

        repaint();
    }
}