/* Copyright (C) 2001-2004 by Peter Eastman

   This program is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
   Foundation; either version 2 of the License, or (at your option) any later version.

   This program 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 General Public License for more details. */

package artofillusion.tools;

import artofillusion.*;
import artofillusion.math.*;
import artofillusion.object.*;
import artofillusion.object.TriangleMesh.*;
import artofillusion.texture.*;
import artofillusion.ui.*;
import buoy.event.*;
import buoy.widget.*;
import java.awt.*;
import java.util.Vector;

/** This dialog box allows the user to specify options for creating skinned objects. */

public class SkinDialog extends BDialog
{
  LayoutWindow window;
  BList curveList;
  BCheckBox reverseBox;
  BButton upButton, downButton, okButton, cancelButton;
  ObjectPreviewCanvas preview;
  ObjectInfo curve[];
  boolean reverse[];
  
  private static int counter = 1;

  public SkinDialog(LayoutWindow window, Vector curves)
  {
    super(window, "Skin", true);
    this.window = window;
    curve = new ObjectInfo [curves.size()];
    reverse = new boolean [curves.size()];
    Scene scene = window.getScene();
    
    // Layout the window.
    
    FormContainer content = new FormContainer(3, 2);
    setContent(BOutline.createEmptyBorder(content, ModellingApp.standardDialogInsets));
    content.setDefaultLayout(new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.BOTH, null, null));
    content.add(UIUtilities.createScrollingList(curveList = new BList()), 0, 0);
    for (int i = 0; i < curves.size(); i++)
    {
      curve[i] = (ObjectInfo) curves.elementAt(i);
      curveList.add(curve[i].name);
    }
    curveList.setMultipleSelectionEnabled(false);
    curveList.setSelected(0, true);
    curveList.addEventLink(SelectionChangedEvent.class, this, "selectionChanged");
    ColumnContainer col = new ColumnContainer();
    content.add(col, 1, 0);
    col.setDefaultLayout(new LayoutInfo(LayoutInfo.CENTER, LayoutInfo.HORIZONTAL, new Insets(2, 2, 2, 2), null));
    col.add(upButton = new BButton("Move Up"));
    upButton.addEventLink(CommandEvent.class, this, "doMoveUp");
    col.add(downButton = new BButton("Move Down"));
    downButton.addEventLink(CommandEvent.class, this, "doMoveDown");
    col.add(reverseBox = new BCheckBox("Reverse Direction", false));
    reverseBox.addEventLink(ValueChangedEvent.class, this, "selectionChanged");
    content.add(preview = new ObjectPreviewCanvas(null), 2, 0);
    preview.setPreferredSize(new Dimension(150, 150));
    RowContainer buttons = new RowContainer();
    content.add(buttons, 0, 1, 3, 1, new LayoutInfo());
    buttons.add(okButton = Translate.button("ok", this, "doOk"));
    buttons.add(cancelButton = Translate.button("cancel", this, "dispose"));
    makeObject();
    pack();
    UIUtilities.centerDialog(this, window);
    updateComponents();
    setVisible(true);
  }
  
  /** Find the center of the curves the skin should consist of.*/
  
  protected Vec3 findCurvesCenter()
  {
    Vec3 center = new Vec3();
    ObjectInfo info;
            
    for (int i = 0; i < curve.length; i++)
      center.add(curve[i].coords.getOrigin());

    center.scale(1.0/curve.length);
    
    return center;
  }

  
  private void selectionChanged(WidgetEvent e)
  {
    if (e.getWidget() == reverseBox)
      reverse[curveList.getSelectedIndex()] = reverseBox.getState();
    makeObject();
    updateComponents();
  }
  
  // Enable or disable components, based on the current selections.
  
  private void updateComponents()
  {
    int which = curveList.getSelectedIndex();
    
    upButton.setEnabled(which > 0);
    downButton.setEnabled(which < curve.length-1);
    reverseBox.setState(which > -1 && reverse[which]);
  }
  
  private void doOk()
  {
    window.addObject(preview.getObject().object, new CoordinateSystem(findCurvesCenter(), Vec3.vz(), Vec3.vy()), "Skinned Object "+(counter++), null);
    window.setSelection(window.getScene().getNumObjects()-1);
    window.setUndoRecord(new UndoRecord(window, false, UndoRecord.DELETE_OBJECT, new Object [] {new Integer(window.getScene().getNumObjects()-1)}));
    window.updateImage();
    dispose();
  }
  
  private void doMoveUp()
  {
    int which = curveList.getSelectedIndex();
    ObjectInfo swap1 = curve[which];
    curve[which] = curve[which-1];
    curve[which-1] = swap1;
    boolean swap2 = reverse[which];
    reverse[which] = reverse[which-1];
    reverse[which-1] = swap2;
    Object swap3 = curveList.getItem(which);
    curveList.remove(which);
    curveList.add(which-1, swap3);
    curveList.setSelected(which-1, true);
    makeObject();
    updateComponents();
  }
  
  private void doMoveDown()
  {
    int which = curveList.getSelectedIndex();
    ObjectInfo swap1 = curve[which];
    curve[which] = curve[which+1];
    curve[which+1] = swap1;
    boolean swap2 = reverse[which];
    reverse[which] = reverse[which+1];
    reverse[which+1] = swap2;
    Object swap3 = curveList.getItem(which);
    curveList.remove(which);
    curveList.add(which+1, swap3);
    curveList.setSelected(which+1, true);
    makeObject();
    updateComponents();
  }
  
  // Create the skinned object.
  
  private void makeObject()
  {
    Vec3 v[][] = new Vec3 [curve.length][], center = new Vec3();
    float us[] = new float [curve.length], vs[] = new float [((Curve) curve[0].object).getVertices().length];
    int smoothMethod = Mesh.INTERPOLATING;
    boolean closed = false;
    
    for (int i = 0; i < curve.length; i++)
      {
	Curve cv = (Curve) curve[i].object;
	MeshVertex vert[] = cv.getVertices();
	v[i] = new Vec3 [vert.length];
	float smooth[] = cv.getSmoothness();
	if (cv.getSmoothingMethod() > smoothMethod)
	  smoothMethod = cv.getSmoothingMethod();
	closed |= cv.isClosed();
	for (int j = 0; j < vert.length; j++)
	  {
	    int k = (reverse[i] ? vert.length-j-1 : j);
	    v[i][j] = curve[i].coords.fromLocal().times(vert[k].r);
	    center.add(v[i][j]);
	    if (cv.getSmoothingMethod() != Mesh.NO_SMOOTHING)
	      vs[j] += smooth[k];
	  }
	us[i] = 1.0f;
      }
    for (int i = 0; i < vs.length; i++)
      vs[i] /= curve.length;
    
    // Center it.
    
    center.scale(1.0/(v.length*v[0].length));
    for (int i = 0; i < v.length; i++)
      for (int j = 0; j < v[i].length; j++)
	v[i][j].subtract(center);
    SplineMesh mesh = new SplineMesh(v, us, vs, smoothMethod, false, closed);
    Texture tex = window.getScene().getDefaultTexture();
    mesh.setTexture(tex, tex.getDefaultMapping());
    mesh.makeRightSideOut();
    preview.setObject(mesh);
    preview.updateImage();
    preview.repaint();
  }
}