
package edu.unl.consystlab.sudoku;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;

import javax.swing.JButton;
import javax.swing.JPanel;

import edu.unl.consystlab.gui.GameProgressBar;


public class VisualGrid extends JPanel implements Griddable, MouseListener, KeyListener
{
	private static Font FONT = new Font(null, Font.BOLD, 18);
	private final static Color BACKGROUND = Color.lightGray;
	private final static Color EMPTY = Color.white;
	private final static Color ERROR = Color.red;
	private final static Color LOCKED = Color.black;
	private final static Color UTRYING = new Color(0.1f,0.6f,0.1f);
	private final static Color STRYING = Color.blue;
	private final static Color SOLVED = Color.gray;
	private final static Color CERROR = Color.magenta;
	private final static Color SELECTED = Color.white;

	private Grid grid = null;
	private GameProgressBar progressBar;
	public JButton[][] jButtons;	

	private boolean updateLife = true;

	// the selected cell gets the keyboard input
	private int selectedCell = 0;

	private boolean showSelectedCell = true;

	public VisualGrid(Grid grid, GameProgressBar progressBar)
	{
		super();
		this.progressBar = progressBar;
		setGrid(grid);
		mkGui();
		calc();
		requestFocus();
	}

	public void repaint(Graphics g)
	{
		this.requestFocus();
		super.paint(g);
	}

	private void mkGui()
	{
		this.setLayout(new GridLayout(3, 3, 7, 7));

		this.setBackground(BACKGROUND);
		int row, col, n;
		jButtons = new JButton[Grid.SZ][Grid.SZ];
		for (int i = 0; i < 9; i++)
		{
			JPanel p = new JPanel();
			p.setLayout(new GridLayout(3, 3, 1, 1));
			p.setBackground(BACKGROUND);

			for (int j = 0; j < 9; j++)
			{
				row = 3 * (i / 3);
				col = 3 * (i % 3);
				row += j / 3;
				col += j % 3;
				String s = (j + 1) + "";
				JButton b = jButtons[row][col] = new JButton(s);
				//s = row+"/"+col;
				p.add(b);
				b.addMouseListener(this);
				b.addKeyListener(this);
				b.setToolTipText("Just click here to add or change the entry");
				b.setFont(FONT);
			}
			this.add(p);
		}
	} // mkGui

	public boolean isSelectedCell(int row, int col)
	{
		int t = row*Grid.SZ + col;
		if(t == selectedCell)
			return true;
		else
		    return false;
	} // isSelectedCell
	
	public void calc(int n)
	{
		calc(n/Grid.SZ, n%Grid.SZ);
	}
	
	public void calc(int row, int col)
	{
		if(updateLife==false)
			return;
			
		JButton c = jButtons[row][col];
		if(isSelectedCell(row,col) && showSelectedCell())
		{
			c.setBackground(SELECTED);
		}
		else
			c.setBackground(BACKGROUND);
		c.setForeground(stateToColor(row,col));
		if (getValue(row,col) == Grid.EMPTY)
			c.setText("");
		else
			c.setText("" + getValue(row,col));
		if(progressBar!=null)
			progressBar.setProgressPct(grid.getProgressPct());
	    this.repaint();	
	} // calc(row, col)
	
	public void calc()
	{
		//Debug.debug("VisualGrid.calc()");
		for (int i = 0; i < Grid.SZ; i++)
			for (int j = 0; j < Grid.SZ; j++)
				calc(i,j);
	} // calc();

	public Color stateToColor(int i, int j)
	{
		int val = getValue(i,j);
		if(val!=Grid.EMPTY)
		{
			if(grid.isOk(i,j,val)==false)
			{
				return ERROR;
			}
		}
		int state = getState(i,j);
		switch (state)
		{
			case Grid.EMPTY :
				return EMPTY;
			case Grid.LOCKED :
				return LOCKED;
			case Grid.SOLVED :
				return SOLVED;
			case Grid.UTRYING :
				return UTRYING;
			case Grid.STRYING :
				return STRYING;
		}
		return CERROR; // ???
	} // stateToColor();

	public void setGrid(Grid grid)
	{
		//Debug.debug("VisualGrid.setGrid()");
		this.grid = grid;
	}
	
	public Grid getGrid()
	{
		return grid;
	}

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
	 */
	public void mousePressed(MouseEvent e)
	{
		Object o = e.getSource();
		if (!(o instanceof JButton))
			return;
		JButton b = (JButton)o;
		pressJButton(b, (byte) - 1);
	}

	public void pressJButton(JButton b, byte val)
	{
		for (int row = 0; row < 9; row++)
		{
			for (int col = 0; col < 9; col++)
			{
				if (jButtons[row][col] == b)
				{
					press(row, col, val);
				}
			}
		}
	} // pressJButton()

	public void press(int row, int col, byte val)
	{
		// If it's locked, do not change neither select it:
		if(getState(row,col)==Grid.LOCKED)
			return;
			
		// if the cell is not in the 'selected' state,
		// select it but do not change it (Karl)
		if(!isSelectedCell(row,col))
		{	
			setSelectedCell(row,col);
			return;
		}

		// Use the button to set/increment the value
		if (val < 0) // just increment
		{
			val = (byte)(getValue(row,col) + 1);
			if (val > 9)
				val = Grid.EMPTY;
		}
		setSelectedCell(row,col);
		setValue(row,col,val);
		if(val==Grid.EMPTY)
			setState(row,col,Grid.EMPTY);
		else
			setState(row,col,Grid.UTRYING);

		//repainter.repaint();
		//this.repaint();
	} // press();

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
	 */
	public void mouseClicked(MouseEvent arg0)
	{
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
	 */
	public void mouseReleased(MouseEvent arg0)
	{
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
	 */
	public void mouseEntered(MouseEvent arg0)
	{
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
	 */
	public void mouseExited(MouseEvent arg0)
	{
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
	 */
	public void keyTyped(KeyEvent arg0)
	{
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
	 */
	public void keyPressed(KeyEvent event)
	{
		// aha!
		if(selectedCell<0 || selectedCell>=(Grid.SZ*Grid.SZ))
			selectedCell=0;
		
		int row = selectedCell/Grid.SZ;
		int col = selectedCell%Grid.SZ;
		
		// let's see
		int key = event.getKeyCode();
		switch(key)
		{
			case KeyEvent.VK_0: case KeyEvent.VK_1: 
			case KeyEvent.VK_2: case KeyEvent.VK_3: 
			case KeyEvent.VK_4: case KeyEvent.VK_5: 
			case KeyEvent.VK_6: case KeyEvent.VK_7: 
			case KeyEvent.VK_8: case KeyEvent.VK_9: 
			case KeyEvent.VK_SPACE:
			case KeyEvent.VK_BACK_SPACE:
			case KeyEvent.VK_DELETE:
			
				// calc value from key
				byte direction=0, val=getValue(row,col);
				if(key==KeyEvent.VK_SPACE)
				{
					direction = +1;
					val = Grid.EMPTY;
				}
				if(key==KeyEvent.VK_CLEAR ||
					key==KeyEvent.VK_BACK_SPACE||
					key==KeyEvent.VK_DELETE)
				{
					direction = -1;
					val = Grid.EMPTY;
				}
				if(key>=KeyEvent.VK_0 && key<=KeyEvent.VK_9)
				{
					val = (byte)(key - KeyEvent.VK_0);
					direction = +1;
				}
				
				// set value & state
				setValue(row,col,val);
				if(val==Grid.EMPTY)
					setState(row,col,Grid.EMPTY);
				else
					setState(row,col,Grid.UTRYING);
				
				// if ok, advance 'cursor'
				if(isOk(row,col))
				{
					setSelectedCell(getSelectedCell()+
							direction);
				}
				break;
			case KeyEvent.VK_UP:
				row--;
				if(row<0) row = Grid.SZ-1;
				setSelectedCell(row,col);
				break;
			case KeyEvent.VK_DOWN:
				row++;
				if(row>=Grid.SZ) row = 0;
				setSelectedCell(row,col);
				break;
			case KeyEvent.VK_LEFT:
				col--;
				if(col<0) col = Grid.SZ-1;
				setSelectedCell(row,col);
				break;
			case KeyEvent.VK_RIGHT:
				col++;
				if(col>=Grid.SZ) col = 0;
				setSelectedCell(row,col);
				break;
			default:
				// beep()
		}
	} //keyPressed

	/* (non-Javadoc)
	 * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
	 */
	public void keyReleased(KeyEvent arg0)
	{
		// TODO Auto-generated method stub

	}

	/**
	 * @return
	 */
	public boolean isUpdateLife()
	{
		return updateLife;
	}

	/**
	 * @param b
	 */
	public void setUpdateLife(boolean b)
	{
		updateLife = b;
	}

	// implement Griddable interface
	public void clear()
	{ grid.clear(); calc(); }
	
	public void clear(int row, int col) 
	{ grid.clear(row,col); calc(row,col); }
	
	public void lock(int row, int col, byte val)
	{ grid.lock(row,col,val); calc(row,col);}			
	
	public boolean isUsed(int row, int col, byte val)
	{ return grid.isUsed(row,col,val); 	}
	
	public boolean isOk(int row, int col)
	{ return grid.isOk(row,col); 	}
	
	public int getEntropy(int row, int col)
	{ return grid.getEntropy(row,col); }
	
	public void setValue(int row, int col, byte val)
	{ grid.setValue(row,col,val); calc(row,col);	}
	
	public void setState(int row, int col, byte state)
	{ grid.setState(row,col,state); calc(row,col);}
	
	public byte getValue(int row, int col)
	{ return grid.getValue(row,col); }
	
	public byte getState(int row, int col)
	{ return grid.getState(row,col); }
	
	public int getProgressPct()
	{ return grid.getProgressPct(); }
	/**
	 * @return
	 */
	public int getSelectedCell()
	{
		return selectedCell;
	}

	/**
	 * @param i
	 */
	public void setSelectedCell(int newCell)
	{
		if(newCell<0 || newCell>=(Grid.SZ*Grid.SZ))
			newCell=0;
		int oldCell = selectedCell;
		selectedCell = newCell;
		calc(oldCell);
		calc(newCell);
	}
	
	public void setSelectedCell(int i, int j)
	{
		setSelectedCell(i*Grid.SZ+j);
		setShowSelectedCell(true);
	}

	/**
	 * @return
	 */
	public boolean isShowSelectedCell()
	{
		return showSelectedCell;
	}
	
	public boolean showSelectedCell()
	{
		return isShowSelectedCell();
	}

	/**
	 * @param b
	 */
	public void setShowSelectedCell(boolean b)
	{
		showSelectedCell = b;
		calc(getSelectedCell());
	}
	
	public void lock()
	{
		grid.lock();
		this.calc();
	}
	
	public void lockToggle()
	{
		grid.lockToggle();
		this.calc();
	}
	
	public boolean save(String name, BufferedWriter bw)
	{
		boolean res = grid.save(name, bw);
		this.calc();
		return res;
	}
	
	public boolean load(String name, BufferedReader br)
	{
		boolean res = grid.load(name, br);
		this.calc();
		return res;
	}
}
