package FDATI;

import java.awt.*;
import java.util.Vector;
import SModeling.*;

/**
 * A Canvas for displaying a 3D polyhedral. This component
 * contains the model object and controls mouse events and painting.
 * Portions of this are based on code by James Gosling.<p>
 *
 * <hr>
 * Copyright (c) 1995, H. Robert Frost, Stanford University.
 * All rights reserved.<p>
 * Copyright (c) 1996, H. Robert Frost, Enterprise Integration Technologies,
 * Inc. All rights reserved.<p>
 *
 * RESTRICTED RIGHTS LEGEND: Use, duplication or disclosure by the 
 * Government is subject to restrictions as set forth in 
 * subparagraph(c)(1)(ii) of the Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 and in similar clauses in the
 * FAR and NASA FAR supplement.<p>
 * 
 * This software is bound by the terms and conditions listed in the
 * attached <a href="../LICENSE">LICENSE file</A>.
 * <hr>
 * @author <A HREF="http://cdr.stanford.edu/html/people/frost-bio.html"> Rob Frost</A>
 * @version 0.3 5/21/96 for Java(tm) Language Version 1.0.2
 */

public class ModelViewer extends Canvas {
  
  /**
   *  true for rotation, false for point selection
   */
  boolean mode = true; /* mode state is only false when space bar is 
			  being pressed */

  boolean first_painting = true;

  /**
   * Are we repainting in a rotation context?
   */
  boolean rotation = false;
  
  /**
   * Should we do hidden line removal before painting?
   * Always checked the first time the model is painted and 
   * after rotation is ended.	
   */
  boolean hiddenlineremoval = false; 

  /**
   * Should we do back face culling during rotation? 
   * Culling will always be done when rotation has ended.
   */
  boolean backface_culling = false;

  /**
   * Should edges be drawn on the model?
   */
  boolean edges = false;

  /**
   * Should the faces of the model be filled when drawn?
   */ 
  boolean render = true; 

  /**
   * Is a key being pressed?
   */ 
  boolean key_down = false; 

  /**
   * Object which contains the solid model, input parser, orientation matrix
   * and applied fixture points.
   */
  public model M;

  /**
   * Object which knows how to display the model M.
   */
  public ModelDisplay MD;

  /**
   * used during dragging to record the previous mouse position
   */
  int prevx, prevy;

  /**
   * The panel which contains this ModelViewer and all of the user interface
   * buttons.
   */

  public ModelPanel parent;
  
  /**
   * Constructor for an ModelViewer.
   * @param m Model to display
   * @param mp The ModelPanel in which the ModelViewer is contained
   */

  public ModelViewer(model m, ModelPanel mp, Color bg_color) {
  
    super();
    setBackground(bg_color);
    M = m;
    MD = new ModelDisplay(M);
    MD.parent = this;
    parent = mp;
      
    parent.action(this, "# verticies = " + M.num_verticies + "\n" +
		  "# edges = " + M.num_lines + "\n" +	
		  "# faces = " + M.num_faces );    

    /* initial display flag settings */

    hiddenlineremoval = true;
    rotation = true;
    backface_culling = true;
    render = true;   

  }

  public Dimension minimumSize(){
    return new Dimension(150,150);
  }

  public Dimension preferredSize(){
    return minimumSize();
  }

  /**
   *  Based on the initial size of the model, determines a scaling factor and
   *  scales the model appropriately before painting.
   */

  void Scale_Model () {
    float scalefudge = 1;
    float xfac;
    
    /* find a scaling factor for the model */
    float xw = MD.xmax - MD.xmin;
    float yw = MD.ymax - MD.ymin;
    float zw = MD.zmax - MD.zmin;
    if (yw > xw)
      xw = yw;
    if (zw > xw)
      xw = zw;
    float f1 = size().width / xw;
    float f2 = size().height / xw;
    xfac = (float)( 0.7 * (f1 < f2 ? f1 : f2) * scalefudge);
    MD.width = size().width;
    MD.height =  size().height;

    MD.Mat.scale(xfac, xfac, xfac, true);

  }


  /**
   * Called by the model to report messages.
   * @param m Model which called the action method
   * @param message Message which the model is sending
   */
  
  public void action(model m, String message) {
    if (parent != null) {
      parent.action(this, message);
    }
  }
  
 /**
   * Called by the ModelDisplay to report messages.
   * @param md ModelDislplay which called the action method
   * @param message Message which the ModelDisplay is sending
   */
  
  public void action(ModelDisplay md, String message) {
    if (parent != null) {
      parent.action(this, message);
    }
  }
  
  /**
   * Determines whether backface culling should be performed during
   * rotation.
   * @param new_value Should backface culling be done?
   */
  
  public void change_culling(boolean new_value) {
    backface_culling  = new_value;
  }

  /**
   * Determines whether the model should be rendered
   * @param new_value Should the model be rendered?
   */
  
  public void set_render(boolean new_value){
    render  = new_value;
  }

  /**
   * Determines whether edges should be drawn.
   * @param new_value Should edges be drawn?
   */
  
  public void set_edge(boolean new_value){
    edges  = new_value;
  }

  /**
   * Called when the mouse button is clicked, represents either an attempt to 
   * add a fixture point or the start of a mouse drag.
   * @param evt Event which called the method
   * @param x Current x position
   * @param y Current y position
   */
  
  public boolean mouseDown(Event evt, int x, int y) { 
    //if(!mode) {
      if(evt.shiftDown()) {
      MD.To_Middle();
      if(MD.all_intersect(x, y)) {
	parent.action(this, "Fixture point #" +
		      M.fix_points.size() + " selected.");
      }
    } else {
      prevx = x;
      prevy = y;
    }
    return true;
  }

  /**
   * Called when the mouse button has been released. This occurs after
   * rotation or after a fixture point has been added.
   * @param evt Event which called the method
   * @param x Current x position
   * @param y Current y position
   */

  public boolean mouseUp(Event evt, int x, int y) {
    if(mode){
      hiddenlineremoval=true;
      repaint();
    }
    return true;
  }
  
  /**
   * Called when mouse is being dragged across the model, represents rotation.
   * @param evt Event which called the method
   * @param x Current x position
   * @param y Current y position
   */

  public boolean mouseDrag(Event evt, int x, int y) {
    //if(mode) {
    if(!evt.shiftDown()){
      float xtheta =(float)( (prevy - y) * (360.0 /  size().width));
      float ytheta =(float)( (x - prevx) * (360.0 /  size().height));
      MD.Mat.rotate(-xtheta,1);
      MD.Mat_triad.rotate(-xtheta,1);
      MD.Mat.rotate(-ytheta,2);
      MD.Mat_triad.rotate(-ytheta,2);
      rotation = true;
      repaint();        
      prevx = x;
      prevy = y;
    } 
    return true;
  }
  
  /**
   * Called as mouse moves across the model. Does nothing now.
   * @param evt Event which called the method
   * @param x Current x position
   * @param y Current y position
   */

  public boolean mouseMove(Event evt, int x, int y){
    return true;
  }

  /**
   * Called to change between rotation and fixel selection modes.
   * @param nextMode False for fixel selection, true for rotation
   */
	
  public void changeMode(boolean nextMode){
    mode = nextMode;
  }

  /**
   * Called when key is pressed. This is used to change between 
   * rotation and fixture point selection modes when the space bar
   * is pressed.
   * @param evt Event which called the method
   * @param ch The key which was pressed
   */
  /*
  public boolean keyDown(Event evt, int ch) {
    parent.addSystemMessage("keyDown event called in ModelViewer");
    if(ch == 32){
      if(mode){
	mode = false;
	parent.ModeChange("Fixel Selection");
      } else {
	mode = true;
	parent.ModeChange("Rotation");
      }
    }
    return true;
  }
  */


  /**
   * Called to paint the model.
   * @param g Graphics context to which the model should be painted.
   */

  public void paint(Graphics g) {
    
    if (M != null) { /* if there is a model */
      MD.width = size().width;
      MD.height = size().height;
      if(first_painting){
	Scale_Model();
	first_painting = false;
      } 
      MD.setTransformed(false); /* transform the model */
      try {
	if(rotation) { /* model being rotated, so reset everything and
			  do backface culling if desired */
	  MD.paint(g, true, hiddenlineremoval, backface_culling, render,
		   edges);
	} else { /* model being paint for non-rotation reason, don't 
		    reset anything */
	  MD.paint(g, false, hiddenlineremoval, hiddenlineremoval, render,
		   edges);
	}
	ResetDisplayFlags();
      } catch (Exception e) {
	parent.action(this, "problem painting model, " + e.toString());
      }
    } else {
      g.drawString("Error in model:", 3, 20);
    }
  }

  /**
   *  Resets all of the display flags to false. Before each repaint() call
   *  the appropriate flags will be set to true.
   */

  void ResetDisplayFlags() {
    hiddenlineremoval = false;
    rotation = false;
  }

  /* Set of methods for modifying the current fixture points */
  
  /**
   * Remove the current fixture point. 
   */
	
  public void modifyPoint () {
    int point = M.current_point;
    if(point <= M.fix_points.size() && point >=1) {
	M.fix_points.removeElementAt(point-1);
	MD.tfix_points.removeElementAt(point-1);
    } else {
      parent.action(this, "Selected fixture point does not exist, can't modify!");
    }
  }
  
  /**
   * Returns the total number of fixture points.
   * @return The number of fixels.
   */
  
  public int numPoints () {
    return M.fix_points.size();
  }

  /**
   * Gets the x,y,z,i,j or k value for the current fixel.
   * @param value One of either "x", "y", "z", "i", "j" or "k"
   * @return The floating point value of the specified parameter.
   */
  
  public float getValue(String value) {
    
    float answer = 0;
    int point = M.current_point;
    if ( point <= M.fix_points.size() && point >=1) {
      fix_point temp = (fix_point)M.fix_points.elementAt(point -1);

      if ( value.equals("x")) {
	answer =  temp.x();
      } else if ( value.equals("y")) {
	answer =  temp.y();
      } else if ( value.equals("z")) {
	answer =  temp.z();
      } else if ( value.equals("i")) {
	answer =  M.get_dircos(temp,1);
      } else if ( value.equals("j")) {
	answer =  M.get_dircos(temp,2);
      } else if ( value.equals("k")) {
	answer =  M.get_dircos(temp,3);
      } 
      
    }
    return answer;
    
  }
  
}

