package com.horstmann.flip;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.Timer;

import edu.lhup.ai.IPiece;
import edu.lhup.ai.IPlayer;

public class FlipPanel extends JComponent
{
    public FlipPanel(FlipBoard board, IPlayer player)
    {
        this.board = board;
        this.player = player;
        
        int h = playerRow() / 2;
        images[0][h] = new ImageIcon(getClass().getResource("eye.png"));
        images[0][1 - h] = new ImageIcon(getClass().getResource("hal.png"));
        for (int i = 1; i <= 6; i++)
            for (int j = 0; j < 2; j++)
            images[i][j] = new ImageIcon(getClass().getResource("gnome-dice-" + i + (j == 0 ? "" : "-gray") + ".png"));

        IPiece[][] pieces = board.getBoard();
        
        for (int i = 0; i < pieces.length; i++)
            for (int j = 0; j < pieces[i].length; j++)
            {
                Visual visual = new Visual();                
                visual.dx = (int)(10 * Math.random());
                visual.dy = (int)(10 * Math.random());
                visual.x = j + (i % 2 == 0 ? 0 : pieces[i - 1].length);
                visual.y = i / 2;
                visuals.put(pieces[i][j], visual);
            }
              
        setPreferredSize(new Dimension(600, 500));
        
        addMouseListener(new MouseAdapter()
        {
           public void mousePressed(MouseEvent event)
           {
               if (!waitingForInput) return;
               Die die = (Die) findPiece(event.getPoint());
               if (die == null) return;
               Visual visual = visuals.get(die);
               if (visual.gray && visual.y == playerRow()) return; // can't flip an unflippable
               if (visual.y == 1)
               {                   
                   if (visual.gray) return;
                   visual.gray = true;                   
                   move += die;
                   repaint();
               }
               else
               {                    
                   move += (visual.y == playerRow() ? "f" : "p" ) + die;
                   setInputString(move);
                   move = "";
               }
           }
        });
        
    }
    
    private int playerRow()
    {
        return board.getPlayers()[0] == player ? 0 : 2;
    }
    
    private IPiece findPiece(Point p)
    {
        for (IPiece piece : visuals.keySet())
            if (getBounds(piece).contains(p))
                return piece;
        return null;
    }

    public void updateBoard()
    {
        IPiece[][] pieces = board.getBoard();
        
        int maxlength = 0;
        for (int i = 0; i < pieces.length; i++)
            maxlength += pieces[i].length;        
        boolean[][] occupied = new boolean[3][maxlength];
        
        List<IPiece> movedPieces = new ArrayList<IPiece>();
        for (int i = 0; i < pieces.length; i++)
            for (int j = 0; j < pieces[i].length; j++)
            {
                Visual visual = visuals.get(pieces[i][j]);
                int y = i / 2;
                if (y == visual.y) // piece has not moved
                {
                    occupied[y][visual.x] = true;
                }
                else
                {
                    visual.y = y; // remember desired y
                    movedPieces.add(pieces[i][j]);
                }
                visual.gray = i % 2 == 1;
            }               
        
        for (IPiece piece : movedPieces)
        {
            Visual visual = visuals.get(piece);
            int x = 0;
            while (occupied[visual.y][x]) x++;
            visual.x = x;
            occupied[visual.y][x] = true;
        }
        repaint();
    }
    
    public void paintComponent(Graphics g)
    {
        Graphics2D g2 = (Graphics2D) g;
        
        int count = 0;
        IPiece[][] pieces = board.getBoard();
        for (int i = 0; i < pieces.length; i++)
            for (int j = 0; j < pieces[i].length; j++)
            {
                IPiece piece = pieces[i][j];
                Visual visual = visuals.get(piece);
                g2.drawImage(getImage(piece), 100 + visual.x * 100 + visual.dx, visual.y * 100 + visual.dy, null);
                count++;
            }
        for (int h = 0; h < 2; h++)
            g2.drawImage(images[0][h].getImage(), 10, 10 + 200 * h, null);
    }
    
    private Image getImage(IPiece piece)
    {
        Visual visual = visuals.get(piece);
        return images[((Die) piece).getValue()][visual.gray ? 1 : 0].getImage(); 
    }
    
    private Rectangle getBounds(IPiece piece)
    {
        Visual visual = visuals.get(piece);
        Image image = getImage(piece);
        return new Rectangle(100 + visual.x * 100 + visual.dx, visual.y * 100 + visual.dy, image.getWidth(null), image.getHeight(null));        
    }
    
    public synchronized void setWaitingForInput(boolean waitingForInput)
    {
        this.waitingForInput = waitingForInput;
        move = "";
        inputString = null;
    }
    
    public synchronized String getInputString() throws InterruptedException
    {
        setWaitingForInput(true);
        while (inputString == null)
            wait();
        return inputString;
    }
    
    public synchronized void setInputString(String inputString)
    {
        this.inputString = inputString;
        notifyAll();
    }    
   
    private FlipBoard board;
    private IPlayer player;
    private Map<IPiece, Visual> visuals = new HashMap<IPiece, Visual>();
    private ImageIcon[][] images = new ImageIcon[7][2];
    private boolean waitingForInput;
    private String move = "";
    private String inputString;
    
    private static class Visual
    {
        public int x;
        public int y;
        public int dx;
        public int dy;
        public boolean gray;
        
        public String toString() { return "(x=" + x + ",y=" + y + (gray ? "*" : "") + ")"; }
    }
}
