/*
 * (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 java.awt.event.*;
import java.text.*;

import javax.swing.*;

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



/**
 * G demo program. Demonstrates:
 *
 * <ul>
 * <li> A math application
 * <li> Polyline performance
 * <li> Geometry generation
 * </ul>
 * 
 * @author <a href="mailto:info@geosoft.no">GeoSoft</a>
 */   
public class Demo20 extends JFrame
  implements ActionListener
{
  private JButton        addLevelButton_;
  private JButton        removeLevelButton_;
  private JLabel         nEdgesLabel_;
  private JLabel         lengthLabel_;
  private JLabel         areaLabel_;
  private KochSnowflake  kochSnowflake_;
  
  
  
  public Demo20()
  {
    super ("G Graphics Library - Demo 20");    
    setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

    JPanel controlPanel = new JPanel();
    addLevelButton_ = new JButton("+");
    addLevelButton_.addActionListener (this);
    controlPanel.add (addLevelButton_);

    removeLevelButton_ = new JButton("-");
    removeLevelButton_.addActionListener (this);    
    controlPanel.add (removeLevelButton_);

    nEdgesLabel_ = new JLabel();
    controlPanel.add (nEdgesLabel_);
    
    lengthLabel_ = new JLabel();
    controlPanel.add (lengthLabel_);
    
    areaLabel_ = new JLabel();
    controlPanel.add (areaLabel_);
    getContentPane().add (controlPanel, BorderLayout.NORTH);
    
    // Create the graphic canvas
    GWindow window = new GWindow (new Color (255, 255, 255));
    getContentPane().add (window.getCanvas(), BorderLayout.CENTER);
    
    // Create scene with default viewport and world extent settings
    GScene scene = new GScene (window);
    double w0[] = {0.0,  0.0, 0.0};
    double w1[] = {1.0,  0.0, 0.0};
    double w2[] = {0.0,  1.0, 0.0};
    scene.setWorldExtent (w0, w1, w2);

    // Create the graphics object and add to the scene
    kochSnowflake_ = new KochSnowflake();
    scene.add (kochSnowflake_);

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

    window.startInteraction (new ZoomInteraction (scene));
    refresh();
  }



  public void actionPerformed (ActionEvent event)
  {
    if (event.getSource() == addLevelButton_)
      kochSnowflake_.addLevel();
    else
      kochSnowflake_.removeLevel();

    refresh();
  }


  
  public void refresh()
  {
    nEdgesLabel_.setText ("#edges = " + kochSnowflake_.getNEdges());

    DecimalFormat format = new DecimalFormat ("0.00000");
    
    double length = kochSnowflake_.computeLength();
    lengthLabel_.setText ("  Length = " + format.format (length));
    
    double area = kochSnowflake_.computeArea();
    areaLabel_.setText ("  Area = " + format.format (area));
    
    kochSnowflake_.refresh();
  }
  

  
  /**
   * Defines the geometry and presentation for the sample
   * graphic object.
   */   
  private class KochSnowflake extends GObject
  {
    private GSegment  curve_;
    private double[]  geometry_;
    
    
    KochSnowflake()
    {
      GStyle style = new GStyle();
      style.setForegroundColor (new Color (255, 0, 0));
      style.setBackgroundColor (new Color (255, 255, 0));
      setStyle (style);

      curve_ = new GSegment();
      geometry_ = new double[] {0.2, 0.2,
                                0.8, 0.2,
                                0.5, 0.2 + 0.6 * Math.sin (Math.PI / 3.0),
                                0.2, 0.2};
      addSegment (curve_);
    }


    public int getNEdges()
    {
      return geometry_.length / 2 - 1;
    }
    
    
    public double computeLength()
    {
      double length = 0;
      for (int i = 0; i < geometry_.length - 2; i += 2) {
        length += Geometry.length (geometry_[i+0], geometry_[i+1],
                                   geometry_[i+2], geometry_[i+3]);
      }
      return length;
    }

    

    public double computeArea()
    {
      return Geometry.computePolygonArea (geometry_);
    }
    

    
    public void addLevel()
    {
      int nLegs   = geometry_.length / 2 - 1;
      int nPoints = nLegs * 4 * 2 + 2;

      double[] geometry = new double[nPoints];

      int j = 0;
      for (int i = 0; i < nLegs*2; i += 2) {
        geometry[j+0] = geometry_[i+0];
        geometry[j+1] = geometry_[i+1];

        double[] v0 = Geometry.computePointOnLine (geometry_[i+0],
                                                   geometry_[i+1],
                                                   geometry_[i+2],
                                                   geometry_[i+3],
                                                   1.0 / 3.0);
        geometry[j+2] = v0[0];
        geometry[j+3] = v0[1];        

        double[] v1 = Geometry.computePointOnLine (geometry_[i+0],
                                                   geometry_[i+1],
                                                   geometry_[i+2],
                                                   geometry_[i+3],
                                                   2.0 / 3.0);

        geometry[j+6] = v1[0];
        geometry[j+7] = v1[1];        

        double length = Geometry.length (v0[0], v0[1], v1[0], v1[1]);

        double dx = geometry_[i+2] - geometry_[i+0];
        double dy = geometry_[i+3] - geometry_[i+1];

        double l = Math.sqrt (dx * dx + dy * dy);
        double angle = Math.acos (dx / l);
        if (dy < 0) angle = 2.0 * Math.PI - angle;
        angle -= Math.PI / 3.0;

        geometry[j+4] = geometry[j+2] + length * Math.cos (angle);
        geometry[j+5] = geometry[j+3] + length * Math.sin (angle);

        j+= 8;
      }

      geometry[geometry.length-2] = geometry[0];
      geometry[geometry.length-1] = geometry[1];      
      
      geometry_ = geometry;
      redraw();
    }

    

    public void removeLevel()
    {
      int nLegs   = geometry_.length / 2 - 1;
      if (nLegs == 3) return;
      
      int nPoints = nLegs / 4 * 2 + 2;

      double[] geometry = new double[nPoints];

      int j = 0;
      for (int i = 0; i < geometry.length - 2; i += 2) {
        geometry[i+0] = geometry_[j+0];
        geometry[i+1] = geometry_[j+1];
        j += 8;
      }

      geometry[geometry.length-2] = geometry[0];
      geometry[geometry.length-1] = geometry[1];      

      geometry_ = geometry;
      redraw();
    }
    
    
    
    public void draw()
    {
      curve_.setGeometryXy (geometry_);
    }
  }
  


  public static void main (String[] args)
  {
    new Demo20();
  }
}