package litebrite.paintingbased;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;

public class LiteBriteBoard extends JComponent
{
  // Measures how round the blocks of the board are
  private static final double ARC_FACTOR = 0.5;
  private int numberOfRows;
  private int numberOfColumns;
  private Color currentColor;
  private List<Color> colorList;

  public LiteBriteBoard(int numberOfRows, int numberOfColumns)
  {
    // Check that we have valid arguments
    if (numberOfColumns < 1 || numberOfColumns < 1)
    {
      throw new IllegalArgumentException("The number of rows and columns must be greater than 0.");
    }

    // Remember the size of the board and set the default color
    this.numberOfRows = numberOfRows;
    this.numberOfColumns = numberOfColumns;
    currentColor = Color.white;

    // Fill the board with the default color
    colorList = new ArrayList<Color>(numberOfRows * numberOfColumns);
    for (int i = 0; i < numberOfColumns * numberOfRows; i++)
    {
      colorList.add(currentColor);
    }

    // Make a mouse adapter to handle mouse presses and drags
    MouseAdapter mouseAdapter = new MouseAdapter()
    {
      @Override
      public void mousePressed(MouseEvent e)
      {
        colorBlockAt(e.getX(), e.getY());
      }

      @Override
      public void mouseDragged(MouseEvent e)
      {
        colorBlockAt(e.getX(), e.getY());
      }

      private void colorBlockAt(int x, int y)
      {
        // Compute the size of the blocks
        double blockWidth =
                getWidth() / (double) LiteBriteBoard.this.numberOfColumns;
        double blockHeight =
                getHeight() / (double) LiteBriteBoard.this.numberOfRows;

        // Find the block that was clicked
        int column = (int) (x / blockWidth);
        int row = (int) (y / blockHeight);

        // Check that the results are valid
        if (row >= LiteBriteBoard.this.numberOfRows
            || column >= LiteBriteBoard.this.numberOfColumns)
        {
          return;
        }

        // Set that block's color
        colorList.set(row * LiteBriteBoard.this.numberOfColumns + column,
                      currentColor);

        // Update the screen
        repaint();
      }
    };

    addMouseListener(mouseAdapter);
    addMouseMotionListener(mouseAdapter);
  }

  public int getNumberOfColumns()
  {
    return numberOfColumns;
  }

  public int getNumberOfRows()
  {
    return numberOfRows;
  }

  public Color getCurrentColor()
  {
    return currentColor;
  }

  public void setCurrentColor(Color currentColor)
  {
    this.currentColor = currentColor;
  }

  @Override
  protected void paintComponent(Graphics g)
  {
    // Let UI delegate paint first for background filling
    super.paintComponent(g);

    // Compute the size of the blocks
    double blockWidth = getWidth() / (double) numberOfColumns;
    double blockHeight = getHeight() / (double) numberOfRows;

    // Paint each block
    for (int row = 0; row < numberOfRows; row++)
    {
      for (int column = 0; column < numberOfColumns; column++)
      {
        g.setColor(colorList.get(row * numberOfColumns + column));
        g.fillRoundRect((int) (column * blockWidth),
                        (int) (row * blockHeight),
                        (int) Math.ceil(blockWidth),
                        (int) Math.ceil(blockHeight),
                        (int) Math.ceil(blockWidth * ARC_FACTOR),
                        (int) Math.ceil(blockHeight * ARC_FACTOR));

        g.setColor(Color.black);
        g.drawRoundRect((int) (column * blockWidth),
                        (int) (row * blockHeight),
                        (int) Math.ceil(blockWidth),
                        (int) Math.ceil(blockHeight),
                        (int) Math.ceil(blockWidth * ARC_FACTOR),
                        (int) Math.ceil(blockHeight * ARC_FACTOR));
      }
    }
  }
}
