/*
 * (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.awt.*;

import javax.swing.*;

import no.geosoft.cc.graphics.*;



/**
 * G demo program. Demonstrates:
 *
 * <ul>
 * <li>A sample game application
 * <li>Graphics animation
 * <li>GObject reparenting
 * </ul>
 * 
 * @author <a href="mailto:info@geosoft.no">GeoSoft</a>
 */   
public class Demo14 extends JFrame
{
  private TowersOfHanoi  towersOfHanoi_;
  private GWindow        window_;
  private Peg[]          pegs_;
  private int            nDiscs_;
  private JButton        startButton_;
  
  
  
  public Demo14 (int nDiscs)
  {
    super ("G Graphics Library - Demo 14");
    setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

    nDiscs_ = nDiscs;

    // Create the graphic canvas
    window_ = new GWindow (new Color (200, 230, 200));
    getContentPane().add (window_.getCanvas());
    
    // Create scene
    GScene scene = new GScene (window_);
    double w0[] = {0.0,  0.0, 0.0};
    double w1[] = {4.0,  0.0, 0.0};
    double w2[] = {0.0,  nDiscs_ * 2, 0.0};
    scene.setWorldExtent (w0, w1, w2);

    // Add title object and add to scene
    scene.add (new Title());
    
    // Create the 3 pegs and add to the scene
    int nPegs = 3;
    pegs_ = new Peg[nPegs];
    for (int i = 0; i < nPegs; i++) {
      pegs_[i] = new Peg (i + 1.0);
      scene.add (pegs_[i]);
    }

    // Create the discs and add to the first peg
    for (int i = 0; i < nDiscs; i++) {
      Disc disc = new Disc ((double) (nDiscs - i) / nDiscs);
      disc.setPosition (1.0, i);
      pegs_[0].add (disc);
    }

    pack();
    setSize (new Dimension (500, 500));
    setVisible (true);

    // Create the puzzle and execute the solution
    towersOfHanoi_ = new TowersOfHanoi();
    towersOfHanoi_.solve();
  }



  public void discMoved (int source, int destination)
  {
    // This is the disc to move
    Disc disc = (Disc) pegs_[source].getChild (pegs_[source].getNChildren()-1);
    
    double y0 = disc.getY();
    double y1 = nDiscs_ + 4.0;

    double x0 = pegs_[source].getX();
    double x1 = pegs_[destination].getX();

    // Animate vertical up movement
    double step = 0.2;
    double y = y0;
    while (y < y1) {
      disc.setPosition (x0, y);
      disc.redraw();
      window_.refresh();
      y += step;
    }

    // Reparent peg
    pegs_[source].remove (disc);
    pegs_[destination].add (disc);
    
    // Animate horizontal movement
    step = 0.05;
    double x = x0;
    while (x != x1) {
      disc.setPosition (x, y);
      disc.redraw();
      window_.refresh();
      x += (x1 > x0 ? step : -step);
      if (Math.abs (x - x1) < 0.01) x = x1;
    }
    
    // Animate vertical down movement
    step = 0.2;
    y  = y1;
    y1 = pegs_[destination].getNChildren() - 1;
    while (y > y1) {
      if (Math.abs (y - y1) < 0.01) y = y1;
      disc.setPosition (x, y);
      disc.redraw();
      window_.refresh();
      y -= step;
    }
  }
  

  
  /**
   * Graphics object for canvas title.
   */
  class Title extends GObject
  {
    private GSegment  anchor_;
    
    public Title()
    {
      GStyle style = new GStyle();
      style.setLineStyle (GStyle.LINESTYLE_INVISIBLE);
      style.setForegroundColor (new Color (100, 100, 200));
      style.setFont (new Font ("serif", Font.PLAIN, 36));
      setStyle (style);

      anchor_ = new GSegment();
      addSegment (anchor_);
      
      GText text = new GText ("Towers of Hanoi", GPosition.SOUTHEAST);
      anchor_.setText (text);
    }
    
    
    public void draw()
    {
      anchor_.setGeometry (20, 20);
    }
  }
  

  
  /**
   * Graphics representation of a peg.
   */
  class Peg extends GObject
  {
    private double    x_;
    private GSegment  peg_;
    private double[]  xy_;
    
    
    public Peg (double x)
    {
      x_ = x;

      GStyle style = new GStyle();
      style.setBackgroundColor (new Color (100, 100, 100));
      setStyle (style);

      peg_ = new GSegment();
      addSegment (peg_);

      xy_ = new double[] {x_ - 0.05, 0.0,
                          x_ - 0.05, nDiscs_ + 2,
                          x_ + 0.05, nDiscs_ + 2,
                          x_ + 0.05, 0.0,
                          x_ - 0.05, 0.0};
    }


    public double getX()
    {
      return x_;
    }
    

    public void draw()
    {
      peg_.setGeometryXy (xy_);
    }
  }


  
  /**
   * Graphics representation of a disc.
   */
  class Disc extends GObject
  {
    private double    size_;
    private GSegment  disc_;
    private double    x_, y_;
    

    public Disc (double size)
    {
      size_ = size;
      
      GStyle style = new GStyle();
      style.setForegroundColor (new Color (255, 0, 0));
      style.setBackgroundColor (new Color (255, 150, 150));
      setStyle (style);
      
      disc_ = new GSegment();
      addSegment (disc_);
    }


    public void setPosition (double x, double y)
    {
      x_ = x;
      y_ = y;
    }


    public double getY()
    {
      return y_;
    }
    
    
    public void draw()
    {
      double[] xy = new double[] {x_ - size_ / 2.0, y_,
                                  x_ - size_ / 2.0, y_ + 1.0,
                                  x_ + size_ / 2.0, y_ + 1.0,
                                  x_ + size_ / 2.0, y_,
                                  x_ - size_ / 2.0, y_};

      disc_.setGeometryXy (xy);
    }
  }
  


  
  /**
   * Class for solving the "Towers of Hanoi" puzzle.
   */   
  class TowersOfHanoi
  {
    public void solve()
    {
      solve (nDiscs_, 0, 2, 1);    
    }
    
    
    private void solve (int nDiscs, int source, int destination, int auxiliary)
    {
      if (nDiscs == 1)
        discMoved (source, destination);
      
      else if (nDiscs > 1) {
        solve (nDiscs - 1, source, auxiliary, destination);
        discMoved (source, destination);
        solve (nDiscs - 1, auxiliary, destination, source);
      }
    }
  }
  
  

  public static void main (String[] args)
  {
    int nDiscs = 8;
    Demo14 demo = new Demo14 (nDiscs);
  }
}