/*
 * (C) 2004 - Geotechnical Software Services
 * 
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation; either 
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this program; if not, write to the Free 
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 
 * MA  02111-1307, USA.
 */
import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;
import java.awt.*;

import javax.swing.*;

import no.geosoft.cc.geometry.Geometry;
import no.geosoft.cc.graphics.*;



/**
 * G demo program. Demonstrates:
 *
 * <ul>
 * <li>A sample interactive game application
 * <li>Custom interaction
 * <li>Dynamic graphics highlighting
 * </ul>
 * 
 * @author <a href="mailto:info@geosoft.no">GeoSoft</a>
 */   
public class Demo13 extends JFrame
  implements GInteraction
{
  private Reversi  reversi_;
  
  
  public Demo13 (int boardSize)
  {
    super ("G Graphics Library - Demo 13");
    setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
    
    // Create the GUI
    JPanel topLevel = new JPanel();
    topLevel.setLayout (new BorderLayout());
    getContentPane().add (topLevel);        

    // Create the graphic canvas
    GWindow window = new GWindow (new Color (220, 220, 220));
    topLevel.add (window.getCanvas(), BorderLayout.CENTER);    

    // Create scene
    GScene scene = new GScene (window);
    double w0[] = {0.0,              0.0,             0.0};
    double w1[] = {boardSize + 2.0,  0.0,             0.0};
    double w2[] = {0.0,              boardSize + 2.0, 0.0};
    scene.setWorldExtent (w0, w1, w2);

    // Create the Reversi game and graphics representation
    reversi_ = new Reversi (boardSize);
    GObject reversiBoard = new ReversiBoard();
    scene.add (reversiBoard);
    
    pack();
    setSize (new Dimension (500, 500));
    setVisible (true);

    // Make sure plot can be scrolled
    window.startInteraction (this);
  }


  
  public void event (GScene scene, int event, int x, int y)
  {
    if (scene == null) return;

    GObject interaction = scene.find ("interaction");
    if (interaction == null) {
      interaction = new GObject ("interaction");
      scene.add (interaction);
    }

    interaction.removeSegments();

    double[] w = scene.getTransformer().deviceToWorld (x, y);

    int i = (int) w[1] - 1;
    int j = (int) w[0] - 1;

    if (i < 0 || i >= reversi_.getSize()|| j < 0 || j >= reversi_.getSize()) 
      return;

    switch (event) {
      case GWindow.MOTION     :
        if (reversi_.isLegalMove (i, j)) {
          GSegment highlight = new GSegment();
          GStyle highlightStyle  = new GStyle();
          highlightStyle.setBackgroundColor (new Color (1.0f, 1.0f, 1.0f, 0.7f));
          highlight.setStyle (highlightStyle);
          interaction.addSegment (highlight);

          highlight.setGeometryXy (new double[] {j + 1.0, i + 1.0,
                                                 j + 2.0, i + 1.0,
                                                 j + 2.0, i + 2.0,
                                                 j + 1.0, i + 2.0,
                                                 j + 1.0, i + 1.0});
        }
        break;
        
      case GWindow.BUTTON1_UP :
        if (reversi_.isLegalMove (i, j)) {
          reversi_.move (i, j);
          interaction.removeSegments();
          scene.redraw();
        }
    }
    
    scene.refresh();    
  }


  class ReversiBoard extends GObject
  {
    private GSegment    board_;
    private GSegment[]  grid_;
    private List        pieces_; // og GSegment
    private GStyle[]    pieceStyle_;
    
    

    public ReversiBoard()
    {
      board_ = new GSegment();
      GStyle boardStyle = new GStyle();
      boardStyle.setBackgroundColor (new Color (0, 200, 0));
      board_.setStyle (boardStyle);
      addSegment (board_);

      GStyle gridStyle = new GStyle();
      gridStyle.setForegroundColor (new Color (0, 0, 0));
      gridStyle.setLineWidth (2);
      grid_ = new GSegment[(reversi_.getSize() + 1) * 2];
      
      for (int i = 0; i < grid_.length; i++) {
        grid_[i] = new GSegment();
        grid_[i].setStyle (gridStyle);
        addSegment (grid_[i]);
      }

      pieceStyle_ = new GStyle[2];
      pieceStyle_[0] = new GStyle();
      pieceStyle_[0].setForegroundColor (new Color (255, 255, 255));
      pieceStyle_[0].setBackgroundColor (new Color (255, 255, 255));

      pieceStyle_[1] = new GStyle();
      pieceStyle_[1].setForegroundColor (new Color (0, 0, 0));
      pieceStyle_[1].setBackgroundColor (new Color (0, 0, 0));

      pieces_ = new ArrayList();
    }

    
    
    public void draw()
    {
      int size = reversi_.getSize();
      
      // Board
      board_.setGeometryXy (new double[] {1.0, 1.0,
                                          size + 1.0, 1.0,
                                          size + 1.0, size + 1.0,
                                          1.0, size + 1.0,
                                          1.0, 1.0});

      // Grid lines
      for (int i = 0; i <= size; i++) {
        grid_[i*2 + 0].setGeometry (1.0, i + 1.0, size + 1.0, i + 1.0);
        grid_[i*2 + 1].setGeometry (i + 1.0, 1.0, i + 1.0, size + 1.0);
      }

      // Pieces
      int[] state = reversi_.getState();
      int j = 0;
      for (int i = 0; i < state.length; i++) {
        if (state[i] != 0) {
          double y = i / size + 1.5;
          double x = i % size + 1.5;

          int[] xy = getTransformer().worldToDevice (x, y);

          GSegment piece;
          if (j < pieces_.size())
            piece = (GSegment) pieces_.get (j);
          else {
            piece = new GSegment();
            pieces_.add (piece);
            addSegment (piece);
          }

          j++;
          
          piece.setStyle (pieceStyle_[state[i] - 1]);
          piece.setGeometry (Geometry.createCircle (xy[0], xy[1], 15));
        }
      }
    }
  }
  


  class Reversi
  {
    private int    size_;
    private int[]  state_;
    private int    player_;

    
    public Reversi (int size)
    {
      size_ = size;
      
      state_ = new int[size_ * size_];
      for (int i = 0; i < state_.length; i++)
        state_[i] = 0;

      state_[(size_ / 2 - 1) * size_ + size_ / 2 - 1] = 1;
      state_[(size_ / 2 - 1) * size_ + size_ / 2]     = 2;
      state_[(size_ / 2)     * size_ + size_ / 2 - 1] = 2;
      state_[(size_ / 2)     * size_ + size_ / 2]     = 1;

      player_ = 1;
    }


    public int getSize()
    {
      return size_;
    }
    

    public int[] getState()
    {
      return state_;
    }
    
    
    public boolean isLegalMove (int i, int j)
    {
      return state_[i*size_+j] == 0 && (win (i, j, player_)).length > 0;
    }

    
    public void move (int i, int j)
    {
      int[] win = win (i, j, player_);
      for (int s = 0; s < win.length; s++)
        state_[win[s]] = player_;
      state_[i*size_ + j] = player_;

      player_ = player_ == 1 ? 2 : 1;
    }

        
    private int[] win (int i, int j, int color)
    {
      List win = new ArrayList();
      win.addAll (win (i, j, color, +1,  0));
      win.addAll (win (i, j, color, +1, +1));
      win.addAll (win (i, j, color,  0, +1));
      win.addAll (win (i, j, color, -1, +1));
      win.addAll (win (i, j, color, -1,  0));
      win.addAll (win (i, j, color, -1, -1));
      win.addAll (win (i, j, color,  0, -1));
      win.addAll (win (i, j, color, +1, -1));

      int[] a = new int[win.size()];
      int s = 0;
      for (Iterator t = win.iterator(); t.hasNext(); s++)
        a[s] = ((Integer) t.next()).intValue();

      return a;
    }
    

    private List win (int i, int j, int color, int dx, int dy)
    {
      List win = new ArrayList();
      
      while (true) {
        i += dx;
        j += dy;
        
        if (i < 0 || i == size_ || j < 0 || j == size_ ||
            state_[i*size_ + j] == 0)
          return new ArrayList();

        else if (state_[i*size_ + j] == color)
          return win;
        else if (state_[i*size_ + j] != color)
          win.add (new Integer (i*size_ + j));          
      }
    }
  }
  

  
  public static void main (String[] args)
  {
    int boardSize = 8;
    new Demo13 (boardSize);
  }
}