/* Useful imports */

import edu.neu.ccs.*;
import edu.neu.ccs.gui.*;
import edu.neu.ccs.codec.*;
import edu.neu.ccs.console.*;
import edu.neu.ccs.filter.*;
import edu.neu.ccs.jpf.*;
import edu.neu.ccs.parser.*;
import edu.neu.ccs.pedagogy.*;
import edu.neu.ccs.quick.*;
import edu.neu.ccs.util.*;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.font.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.border.*;
import java.io.*;
import java.util.*;
import java.math.*;
import java.beans.*;
import java.lang.reflect.*;
import java.net.URL;
import java.util.regex.*;
import java.text.ParseException;


public class SudokuBlock
    extends BufferedPanel
{
    private static final Color background = Colors.bisque;
    
    private static final Color selectborder = Colors.red;
    
    private static final int boxsize = 64;

    private static final int gapsize = 2;

    private static final int cellsize = boxsize + 4 * gapsize;
    
    
    private static final int largefontsize = boxsize;
    
    private static final Font largefont =
        new Font("serif", Font.BOLD, largefontsize);
    
    
    private static TextPaintable[] digits = 
        new TextPaintable[10];
    
    static {
        // find the height of a typical digit in the large font
        TextPaintable p =
            new TextPaintable
                ("0", largefont, Colors.black, TextBounds.TIGHT,
                 TextAnchor.CENTER_BASELINE, 0, 0);
        
        Rectangle2D textbounds = p.getBounds2D();
        
        int digitheight = (int) textbounds.getHeight();
        
        // find the location of the center baseline for all digits
        int x = cellsize / 2;
        int y = (cellsize + digitheight) / 2;
        
        // create digits 1-9 
        // omit digit 0 since it will never be used
        for (int i = 1; i <= 9; i++) {
            digits[i] =
                new TextPaintable
                    (i + "", largefont, Colors.black, TextBounds.TIGHT,
                     TextAnchor.CENTER_BASELINE, x, y);
        }
    }
    
    
    private static final int smallfontsize = (boxsize / 3);
    
    private static final Font smallfont =
        new Font("serif", Font.BOLD, smallfontsize);
    
    private static TextPaintable[] hintdigits = 
        new TextPaintable[10];
    
    static {
        // find the height of a typical digit in the small font
        TextPaintable p =
            new TextPaintable
                ("0", smallfont, Colors.black, TextBounds.TIGHT,
                 TextAnchor.CENTER_BASELINE, 0, 0);
        
        Rectangle2D textbounds = p.getBounds2D();
        
        int digitheight = (int) textbounds.getHeight();
        
        int[] xpos = {
            cellsize / 5,
            cellsize / 2,
            cellsize - (cellsize / 5)
        };
        
        int[] ypos = {
            3 * gapsize + digitheight,
            (cellsize + digitheight) / 2,
            cellsize - (3 * gapsize)
        };
        
        // create digits 1-9 
        // omit digit 0 since it will never be used
        for (int i = 1; i <= 9; i++) {
            int row = (i - 1) / 3;
            int col = (i - 1) % 3;
            
            int x = xpos[col];
            int y = ypos[row];
            
            hintdigits[i] =
                new TextPaintable
                    (i + "", smallfont, Colors.black, TextBounds.TIGHT,
                     TextAnchor.CENTER_BASELINE, x, y);
        }
    }
    
    
    private static final Rectangle2D border =
        new Rectangle2D.Double(1, 1, cellsize - 2, cellsize - 2);
    
    private static final Stroke stroke=
        new BasicStroke(2);
    
    
    private SudokuModel model = null;
    
    private SudokuTable table = null;
    
    private int row;
    
    private int col;
   
    private boolean selected = false;
    
    private DisplayPanel inner = null;
    
    private KeyActionAdapter keyadapter = null;
    
    private MouseActionAdapter mouseadapter = null;
    
    
    // The key action object for key pressed events
    private KeyAction keyPressed = new KeyAction() {
        public void keyActionPerformed(KeyEvent kevt) {
            handleKeyPressed(kevt);
        }
    };
    
    
    
    // The mouse action object for mouse pressed events
    private MouseAction mousePressed = new MouseAction() {
        public void mouseActionPerformed(MouseEvent mevt) {
            handleMousePressed(mevt);
        }
    };
    
    
    
    public SudokuBlock() {
        super(cellsize, cellsize, background);
        
        initialize();
    }
    
    
    public SudokuBlock
        (SudokuModel model,
         SudokuTable table,
         int row,
         int col)
    {
        super(cellsize, cellsize, background);
        
        this.model = model;
        this.table = table;
        this.row = row;
        this.col = col;
        
        initialize();
    }
    
    
    private void initialize() {
        inner = getInnerPanel();
        
        keyadapter = getKeyActionAdapter();
        initializeKeys();
        
        mouseadapter = getMouseActionAdapter();
        initializeMouse();
    }
    
    
    // The key action method for key pressed events
    private void handleKeyPressed(KeyEvent kevt) {
        int code  = kevt.getKeyCode();
        int digit = 0;
        
        switch(code) {
            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:
            {
                digit = code - KeyEvent.VK_0;
                break;
            }
            
            case KeyEvent.VK_0:
            case KeyEvent.VK_SPACE:
                digit = 0;
                break;
                
            default:
                return;
        }
        
        paintDigit(digit);
        
        if (model != null) {
            model.setCellFromView(row, col, digit);
            model.updateHints();
        }
    }
    
    
    // Initialize key listening for the inner panel
    private void initializeKeys() {
        // enable the inner panel to gain the focus of attention
        inner.setFocusable(true);
        
        // attach the pressed action object to the key adapter
        keyadapter.addKeyPressedAction(keyPressed);
    }
    
    
    // Set or reset key listening to the inner panel
    public void resetKeyFocus() {
        inner.requestFocusInWindow();
    }
    
    
    // Initialize mouse listening'
    private void initializeMouse() {
        mouseadapter.addMousePressedAction(mousePressed);
    }
    
    
    // The mouse action method for mouse pressed events
    private void handleMousePressed(MouseEvent mevt) {
        if (table != null)
            table.unselectBlocks();
        
        select();
    }
    
    
    public void setSelect(boolean select) {
        if (select)
            resetKeyFocus();
        
        if (selected != select) {
            selected = select;
            repaintBlock();
        }
    }
    
    
    public void select() {
        setSelect(true);
    }
    
    
    public void unselect() {
        setSelect(false);
    }
    
    
    public boolean isSelected() {
        return selected;
    }
    
    
    public void clearBlock() {
        paintDigit(0);
    }
    
    
    public void paintDigit(int digit) {
        clearPanel();
        
        Graphics2D g = getBufferGraphics();
        
        if ((digit >= 1) && (digit <= 9))
            digits[digit].paint(g);
        
        repaintBlock();
    }
    
    
    public void paintHint(SudokuHint hint) {
        clearPanel();
        
        Graphics2D g = getBufferGraphics();
        
        for (int digit = 1; digit <= 9; digit++)
            if (hint.getHint(digit))
                hintdigits[digit].paint(g);
        
        repaintBlock();
    }
    
    
    public void repaintBlock() {
        paintSelectState();
        repaint();
    }
    
    
    private void paintSelectState() {
        Color color = selected ? selectborder : background;
        
        Graphics2D g = getBufferGraphics();
        
        g.setPaint(color);
        g.setStroke(stroke);
        g.draw(border);
    }
    
    
    public static void main(String[] args) {
        SudokuBlock block1 = new SudokuBlock();
        SudokuBlock block2 = new SudokuBlock();
        
        SudokuHint hint = new SudokuHint();
        
        hint.setAll(true);
        
        block1.paintHint(hint);
        block2.paintHint(hint);
        
        Object[] stuff = { block1, block2 };
        
        TablePanel panel = new TablePanel(stuff, HORIZONTAL, 5, 5, CENTER);
        
        panel.setBackground(Colors.black);
        
        panel.frame("Two Sudoku Blocks");
    }
    
}

