import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.event.*;
import java.util.*;

public class TicTacToePanel extends JComponent
{
  private static int p1Score, p2Score;
  private Rectangle panelSize;
  private static TicTacToeFrame parentFrame = null;
  private int[] table = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  private int turn = 1;
  private int computerPlayer = 2;
  private int winningPlayer = 0;
  private BasicStroke basicStroke = new BasicStroke( 3.0F );

  /**
   * Konstruktori.
   * @param parent ikkuna johon tämä paneli laitetaan
   */
  public TicTacToePanel( JFrame parent )
  {
    addMouseListener( new java.awt.event.MouseAdapter()
    {
      public void mouseClicked( MouseEvent e )
      {
        panelMouseClicked( e );
      }
    } );
    addComponentListener( new java.awt.event.ComponentAdapter()
    {
      public void componentResized( ComponentEvent e )
      {
        panelResized( e );
      }
    } );

    parentFrame = ( TicTacToeFrame ) parent;
  }

  /**
   * Piirtometodi. Pelipaikkojen numerointi:
   * käyttäjä               ohjelma
   * 7 | 8 | 9             6 | 7 | 8
   * --+---+--             --+---+--
   * 4 | 5 | 6             3 | 4 | 5
   * --+---+--             --+---+--
   * 1 | 2 | 3             0 | 1 | 2
   * @param gfx piirtopinta
   */
  public void paintComponent( Graphics gfx )
  {
    int x = panelSize.width;
    int y = panelSize.height;
    int delta = 2 * y / 140; // pelipöydän viivanleveys

    int posXTable[] = {
        0, x / 3, x / 3 * 2,
        0, x / 3, x / 3 * 2,
        0, x / 3, x / 3 * 2 };
    int posYTable[] = {
        y / 3 * 2, y / 3 * 2, y / 3 * 2,
        y / 3, y / 3, y / 3,
        0, 0, 0 };

    Graphics2D g = ( Graphics2D ) gfx;
    g.setStroke( basicStroke );

    //pysty
    g.fillRect( posXTable[ 7 ], posYTable[ 7 ], delta, y );
    g.fillRect( posXTable[ 8 ], posYTable[ 8 ], delta, y );

    //vaaka
    g.fillRect( posXTable[ 3 ], posYTable[ 3 ], x, delta );
    g.fillRect( posXTable[ 0 ], posYTable[ 0 ], x, delta );

    for ( int i = 0; i < table.length; i++ )
    {
      if ( table[ i ] == 1 ) // o
      {
        g.setColor( Color.red );
        g.drawOval( posXTable[ i ] + 4 * delta, posYTable[ i ] + 4 * delta, x / 3 - 7 * delta, y / 3 - 7 * delta );
      }
      else if ( table[ i ] == 2 ) // x
      {
        g.setColor( Color.blue );
        g.drawLine( posXTable[ i ] + 4 * delta, posYTable[ i ] + 4 * delta,
                    posXTable[ i ] + 4 * delta + x / 3 - 7 * delta, posYTable[ i ] + 4 * delta + y / 3 - 7 * delta );
        g.drawLine( posXTable[ i ] + 4 * delta, posYTable[ i ] + 4 * delta + y / 3 - 7 * delta,
                    posXTable[ i ] + 4 * delta + x / 3 - 7 * delta, posYTable[ i ] + 4 * delta );
      }
    }
  }

  /**
   * Paneelin kokoa muutettiin.
   * @param e tarkempi kuvaus
   */
  void panelResized( ComponentEvent e )
  {
    panelSize = getBounds( panelSize );
  }

  /**
   * Aloittaa uuden pelin.
   */
  public void restartGame()
  {
    if ( winningPlayer == 1 )
      turn = 2;
    else
      turn = 1;
    for ( int i = 0; i < table.length; i++ )
      table[ i ] = 0;
    Toolkit.getDefaultToolkit().beep();
    winningPlayer = 0;
    repaint();
    parentFrame.write( "Pelaaja   " + p1Score + " : " + p2Score + "   Tietokone" );
  }

  /**
   * Nollaa pisteet.
   */
  public void resetGame()
  {
    p1Score = 0;
    p2Score = 0;
    restartGame();
  }

  /**
   * Aktivoituu hiiren klikkauksesta.
   * @param e tarkempi kuvaus
   */
  void panelMouseClicked( MouseEvent e )
  {
    selectTic( e.getX(), e.getY() );
  }

  /**
   * Laskee ruudun johon hiiren näpäytys osui.
   * @param x hiiren x-koordinaatti
   * @param y hiiren y-koordinaatti
   */
  void selectTic( int x, int y )
  {
    int pos = 0;

    if ( x < panelSize.width / 3 )
    {
      if ( y < panelSize.height / 3 )
        pos = 7;
      else if ( y < panelSize.height / 3 * 2 )
        pos = 4;
      else
        pos = 1;
    }
    else if ( x < panelSize.width / 3 * 2 )
    {
      if ( y < panelSize.height / 3 )
        pos = 8;
      else if ( y < panelSize.height / 3 * 2 )
        pos = 5;
      else
        pos = 2;
    }
    else
    {
      if ( y < panelSize.height / 3 )
        pos = 9;
      else if ( y < panelSize.height / 3 * 2 )
        pos = 6;
      else
        pos = 3;
    }

    markTurn( pos );
  }

  /**
   * Merkkaa kierroksella tehdyn siirron.
   * @param pos ruudun numero johon merkki laitettiin
   */
  void markTurn( int pos )
  {
    if ( pos <= table.length && pos > 0 )
      if ( table[ pos - 1 ] == 0 && winningPlayer == 0 )
      {
        table[ pos - 1 ] = turn;
        if ( turn == 1 )
          turn = 2;
        else
          turn = 1;
        repaint();
      }
      else
        Toolkit.getDefaultToolkit().beep();

    if ( winningPlayer == 0 )
      if ( ( table[ 0 ] == table[ 1 ] && table[ 0 ] == table[ 2 ] && table[ 0 ] == 1 ) ||
           ( table[ 3 ] == table[ 4 ] && table[ 3 ] == table[ 5 ] && table[ 3 ] == 1 ) ||
           ( table[ 6 ] == table[ 7 ] && table[ 6 ] == table[ 8 ] && table[ 6 ] == 1 ) ||
           ( table[ 0 ] == table[ 3 ] && table[ 0 ] == table[ 6 ] && table[ 0 ] == 1 ) ||
           ( table[ 1 ] == table[ 4 ] && table[ 1 ] == table[ 7 ] && table[ 1 ] == 1 ) ||
           ( table[ 2 ] == table[ 5 ] && table[ 2 ] == table[ 8 ] && table[ 2 ] == 1 ) ||
           ( table[ 0 ] == table[ 4 ] && table[ 0 ] == table[ 8 ] && table[ 0 ] == 1 ) ||
           ( table[ 2 ] == table[ 4 ] && table[ 2 ] == table[ 6 ] && table[ 2 ] == 1 ) )
      {
        winningPlayer = 1;
        parentFrame.write( "Voittaja on O" );
        p1Score++;
        turn = -1;
      }
      else
      if ( ( table[ 0 ] == table[ 1 ] && table[ 0 ] == table[ 2 ] && table[ 0 ] == 2 ) ||
           ( table[ 3 ] == table[ 4 ] && table[ 3 ] == table[ 5 ] && table[ 3 ] == 2 ) ||
           ( table[ 6 ] == table[ 7 ] && table[ 6 ] == table[ 8 ] && table[ 6 ] == 2 ) ||
           ( table[ 0 ] == table[ 3 ] && table[ 0 ] == table[ 6 ] && table[ 0 ] == 2 ) ||
           ( table[ 1 ] == table[ 4 ] && table[ 1 ] == table[ 7 ] && table[ 1 ] == 2 ) ||
           ( table[ 2 ] == table[ 5 ] && table[ 2 ] == table[ 8 ] && table[ 2 ] == 2 ) ||
           ( table[ 0 ] == table[ 4 ] && table[ 0 ] == table[ 8 ] && table[ 0 ] == 2 ) ||
           ( table[ 2 ] == table[ 4 ] && table[ 2 ] == table[ 6 ] && table[ 2 ] == 2 ) )
      {
        winningPlayer = 2;
        parentFrame.write( "Voittaja on X" );
        p2Score++;
        turn = -1;
      }
      else
      {
        boolean theEnd = true;
        for ( int i = 0; i < table.length; i++ )
        {
          if ( table[ i ] == 0 )
            theEnd = false;
        }

        if ( theEnd == true )
        {
          parentFrame.write( "Tasapeli" );
          turn = -1;
        }
      }

    // luo tietokonepelaajan, erittäin huono tapa pelata
    if ( computerPlayer == turn )
    {
      ComputerPlayerThread r = new ComputerPlayerThread();
      Thread t = new Thread( r );
      t.setDaemon( true );
      t.start();
    }

  }

  /**
   * Tietokoneen pelisäie.
   */
  public class ComputerPlayerThread implements Runnable
  {
       int[][] pos = { {0,1,2}, {0,2,1}, {1,2,0},
                       {3,4,5}, {4,5,3}, {3,5,4},
                       {6,7,8}, {6,8,7}, {7,8,6},
                       {0,3,6}, {0,6,3}, {3,6,0},
                       {1,4,7}, {1,7,4}, {4,7,1},
                       {2,5,8}, {2,8,5}, {5,8,2},
                       {0,4,8}, {0,8,4}, {4,8,0},
                       {2,4,6}, {2,6,4}, {4,6,2} };

    public void run()
    {
      int nextMove = -1;

      // oma rivi
      for ( int i = 0; i < 24; i++ )
      {
        if ( table[ pos[ i ][ 0 ] ] == 2 && table[ pos[ i ][ 1 ] ] == 2 && table[ pos[ i ][ 2 ] ] == 0 )
        {
          nextMove = pos[ i ][ 2 ];
          break;
        }
      }

      // vastustaja
      if ( nextMove < 0 )
      {
        for ( int i = 0; i < 24; i++ )
        {
          if ( table[ pos[ i ][ 0 ] ] == 1 && table[ pos[ i ][ 1 ] ] == 1 && table[ pos[ i ][ 2 ] ] == 0 )
          {
            nextMove = pos[ i ][ 2 ];
            break;
          }
        }
      }

      // satunnainen liikku
      if ( nextMove < 0 )
      {
        do
        {
          nextMove = ( int ) ( Math.random() * 8.1 );
        }
        while ( table[ nextMove ] != 0 );
      }

      // tietokone nukkuu neljännessekunnin ja merkkaa liikkunsa vasta sen jälkeen.
      // tämä siksi että näyttäisi siltä niinkuin kone ajattelisi.
      try
      {
        Thread.sleep( 250 );
      }
      catch ( InterruptedException e )
      {}

      markTurn( nextMove + 1 );
    }

  }

}
