/* CreateTrackCommand.java - 
 *
 * (C) 2005 fnh
 */

package jfig.demo;

// import java.awt.*;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.*;
import javax.swing.*;
import java.util.Enumeration;
import java.util.Vector;

import jfig.gui.JModularEditor;
import jfig.canvas.*;
import jfig.commands.*;
import jfig.objects.*;
import jfig.utils.MouseMapper;


/**
 * add the selected track to the current drawing.
 * Provides 'smart place' option.
 */
public class CreateTrackCommand extends Command {

  FigObject target = null;
  Point     pos    = null;

  static FigObject globalLastUsedTrack = null;



  public CreateTrackCommand( FigBasicEditor editor, FigCanvas objectCanvas ) 
  {
    super( editor, objectCanvas );
    statusMessage( "L: smart place object M: place object R: cancel" );
    editor.hideAllObjectCorners();
    objectCanvas.changeRubberbandMode( FigCanvasRubberband.pointRubber );
    ready = false;

    if (target == null) {
      if (globalLastUsedTrack == null) return;

      target = globalLastUsedTrack.copy();

      // move new copy back to origin for correct rubberbanding
      Point p = target.getPosition();
      target.move( -p.getX(), -p.getY() );

      objectCanvas.setRubberbandBasePoint(
         objectCanvas.getTrafo().getScreenCoords( new Point(0,0) ));
      objectCanvas.changeRubberbandMode( FigCanvasRubberband.objectRubber,
                                         objectCanvas.getTrafo(),
                                         target );
    //objectCanvas.changeRubberbandMode( FigCanvasRubberband.bboxRubber,
    //                                   objectCanvas.getTrafo(),
    //                                   target.getBbox() );
    

    }
  }


  public void setTrack( FigObject o ) {
    target = o;
    globalLastUsedTrack = target;

    // editor.addTmpObject( target );
    objectCanvas.setRubberbandBasePoint(
      objectCanvas.getTrafo().getScreenCoords( new Point(0,0) ));
    objectCanvas.changeRubberbandMode( FigCanvasRubberband.objectRubber,
                                       objectCanvas.getTrafo(),
                                       target );
  }


  public void execute() {
    if (target != null) {
      editor.insertIntoObjectList( target );
      editor.getUndoStack().push( this );
      objectCanvas.doFullRedraw();
    }
  }


  public void undo() {
    if (target != null) {
      editor.deleteFromObjectList( target );
      objectCanvas.doFullRedraw();
    }
  }


  private void rotateTarget( double degrees ) {
    try {
      Point sp = objectCanvas.getRawMousePositionOrNull();
      Point wp = objectCanvas.getTrafo().getWorldCoordsSnapped( sp );
// System.out.println( "XXX rotateTarget: " + sp + "   " + wp );
      if (target != null) {
        target.rotate( wp, degrees*Math.PI*2.0/360.0 );
      }
    }
    catch( Exception e ) {
      e.printStackTrace();
    }
  }



  public void mousePressed( MouseEvent evt ) {
    Point WP = getWorldCoords( evt );
    Point SP = getScreenCoords( evt );

    // safety check
    if (target == null) {
      throw new RuntimeException( "Internal error: target object is null!" );
    }


    if (MouseMapper.isRightClick(evt)) { // cancel
        if (target != null) editor.deleteTmpObject( target );
        target = null;
        objectCanvas.changeRubberbandMode( FigCanvasRubberband.pointRubber );
        objectCanvas.doFullRedraw();
        ready = true;
    }
    else if (MouseMapper.isMiddleClick(evt)) { // smart place
        pos = WP;

        boolean nearEnough = smartPlaceToNearestMarker( WP );
        if (nearEnough) {
          execute();
          ready = true;
          notifyEditor();
        }
    }
    else { // left click, raw place
      pos = WP;
      // System.out.println( "-#- WP= " + WP );
      // System.out.println( "-#- pos= " + target.getPosition() );
      // target.move( WP.getX(), WP.getY() );
      execute();
      ready = true;
      notifyEditor();
    }
  } // mousePressed


  public void keyPressed( KeyEvent evt ) {
    char key = evt.getKeyChar();
    int code = evt.getKeyCode();
    Point  p = null;
      JModularEditor ed = (JModularEditor) editor;

    if (evt.isActionKey()) { // some keys for panning etc.
      switch (code) {
        case KeyEvent.VK_DOWN :  ed.doPanDown( evt ); break;
        case KeyEvent.VK_UP   :  ed.doPanUp(  evt );  break;
        case KeyEvent.VK_LEFT :  ed.doPanLeft( evt ); break;
        case KeyEvent.VK_RIGHT:  ed.doPanRight(evt ); break;
        case KeyEvent.VK_HOME :  ed.doPanHome( evt ); break;
      }
    }
    else {
     switch( key ) {
      case 'f' : 
                 ed.doZoomFit( null );
                 break;
      case 's' : 
                 rotateTarget(  -18.0 );
                 target.rebuild();
                 break;
      case 'S' : 
                 rotateTarget( -36.0 );
                 target.rebuild();
                 break;
      case 'r' : 
                 rotateTarget(  18.0 );
                 target.rebuild();
                 break;
      case 'R' : 
                 rotateTarget( 36.0 );
                 target.rebuild();
                 break;
      case 'x' : 
      case 'X' : 
                 p = target.getPosition();
                 target.mirrorX( p.getX(), p.getY() );
                 target.rebuild();
                 break;
      case 'y' : 
      case 'Y' : 
                 p = target.getPosition();
                 target.mirrorY( p.getX(), p.getY() );
                 target.rebuild();
                 break;
      case 'z' :
                 objectCanvas.doZoomOut();
                 break;
      case 'Z' :
                 objectCanvas.doZoomIn();
                 break;
      default  :
                 System.out.println( "-W- CTC.keyPressed: ignored " + key );
     } // switch
    } // else action key

    objectCanvas.doFullRedraw();
  }


  public Vector findObjectMarkersViaComments( FigCompound c, Vector v ) {
    // msg( "-#- CTC.findObjectMarkers..." );
    if (v == null) v = new Vector();

    for( Enumeration e = c.getMembers().elements(); e.hasMoreElements(); ) {
      FigObject tmp = (FigObject) e.nextElement();
      String comment = tmp.getComment();
      if (comment == null) continue;

      if ("start".equals( comment ) || "end".equals( comment )) {
        // msg( "-#- found marker: " + comment + " " + tmp );
        v.addElement( tmp );
      }
    }
    // msg( "    total: " + v.size() + " markers." );
    return v; 
  }


  public Vector findObjectMarkersViaLayers( FigCompound c, Vector v ) {
    // msg( "-#- CTC.findObjectMarkers..." );
    if (v == null) v = new Vector();

    for( Enumeration e = c.getMembers().elements(); e.hasMoreElements(); ) {
      FigObject tmp = (FigObject) e.nextElement();

      if (tmp instanceof FigCompound) { // recurse
        findObjectMarkersViaLayers( (FigCompound) tmp, v );
      }
      else { // check non-compound objects for matching layer
        int layer = tmp.getLayer();
        if (   (layer == ProfiGleisFactory.START_MARKER_LAYER) 
            || (layer == ProfiGleisFactory.END_MARKER_LAYER)) 
        {
          // msg( "-#- found marker at " + tmp.getPosition() );
          v.addElement( tmp );
        }
      }
    }
    // msg( "    total: " + v.size() + " markers." );
    return v; 
  }


  private Point[] getMarkerPositions( Vector v ) {
    Point[] pp = new Point[ v.size() ];
    for( int i=0; i < v.size(); i++ ) {
      pp [i] = ((FigObject) v.elementAt(i)).getPosition();
    }
    return pp;
  }


  public boolean smartPlaceToNearestMarker( Point wp ) {
// msg( "-#- CTC.findObjectMarkers: " + wp );
    FigCompound nearestMatch = null;
    FigObject   nearestMarker1 = null; // on target object
    FigObject   nearestMarker2 = null; // on existing object
    double      nearestDistance = Double.MAX_VALUE;

// msg( "-1- target: " );
    Vector v1 = findObjectMarkersViaLayers( (FigCompound)target, null );
    Point[] pp1 = getMarkerPositions( v1 );
    // Point targetCenter = targetBbox.getCenterPoint();

// msg( "-2- others: " );
    FigBbox targetBbox = target.getBbox();
    for( Enumeration e = editor.getObjects(); e.hasMoreElements(); ) {
      Object tmp = e.nextElement();
      if (tmp instanceof FigCompound) {
        FigCompound comp = (FigCompound) tmp; 
        FigBbox compBbox = comp.getBbox();
        // Point compCenter = compBbox.getCenterPoint();

        // near enough for detailed checking ?
        double NEAR_ENOUGH = 10000;
        // double distance    = targetCenter.distance( compCenter );
        double distance = targetBbox.minManhattanDistance( compBbox );
// msg( "... ... " + distance );
        if (distance < NEAR_ENOUGH) {
          Vector v2 = findObjectMarkersViaLayers( comp, null );
          Point[] pp2 = getMarkerPositions( v2 );

          for( int i=0; i < pp1.length; i++ ) {
            for( int j=0; j < pp2.length; j++ ) {
              double d = pp1[i].distance( pp2[j] );
// msg( "... ... ... " + i + " " + j + "  " + d );
              if (d < nearestDistance) {
                nearestMatch = comp;
                nearestMarker1 = (FigObject) v1.elementAt(i);
                nearestMarker2 = (FigObject) v2.elementAt(j);
                nearestDistance = d;
              }
            }
          }
        } // near_enough
      }
    } // for all objects

// msg( "*** nearestMatch: " + nearestMatch );
// msg( "*** nearestMarker1: " + nearestMarker1 );
// msg( "*** nearestMarker2: " + nearestMarker2 );
// msg( "*** distance      : " + nearestDistance );

    if (nearestDistance < 4800) {
      Point t = target.getPosition();         // current position of target obj 
      Point x = nearestMarker1.getPosition(); // current pos of marker on target
      Point z = nearestMarker2.getPosition(); // nearest external marker pos

      target.move( z.getX() - t.getX() + (t.getX() - x.getX()),
                   z.getY() - t.getY() + (t.getY() - x.getY()) );
      return true;
    }
    return false;
  }


  public void msg( String s ) {
    System.out.println( s );
  }


  public String getDescription() {
    return "create track";
  }

  public String toString() {
    return "CreateTrackCommand";
  }

} // class CreateTrackCommand
