/*
<?xml version='1.0' standalone='yes' ?>

<script>
	<name>Export Gnu Triangulated Surface</name>
	<author>Enrique Perez</author>
	<version>1.0</version>
	<date>29-Apr-2008</date>
	<description>
This script exports triangle meshes to GNU Triangulated Surface (GTS) format files.  The GTS Library homepage is at: http://gts.sourceforge.net/
    </description>
	<licenseType>gpl</licenseType>
</script>
*/
//later once stable remove unused preferences, all the preferences will be kept in the unused Skeinforge, finish comments for this and import
/**
Get the info for all the selected curves and meshes.
*/

ObjectInfo[] getCurvesMeshesInfo()
{
  scene = window.getScene();
  selection = scene.getSelection();
  Vector objectInfoVector = new Vector();

  for ( int objectIndex = 0; objectIndex < selection.length; objectIndex++ ) {
    objectInfo = scene.getObject( selection[ objectIndex ] );

    if ( objectInfo.object instanceof TriangleMesh ) {
      objectInfoVector.add( objectInfo );
    }
  }

  if ( objectInfoVector.size() > 0 ) {
    return objectInfoVector.toArray( new ObjectInfo[ objectInfoVector.size() ] );
  }

  for ( int objectIndex = 0; objectIndex < scene.getNumObjects(); objectIndex++ ) {
    objectInfo = scene.getObject( objectIndex );

    if ( objectInfo.object instanceof TriangleMesh ) {
      return new ObjectInfo[] { objectInfo };
    }
  }

  return new ObjectInfo[ 0 ];
}

String getEdgeString( edges, edgeTriple, vertexIndexFirst, vertexIndexSecond )
{
  for ( int edgeTripleIndex = 0; edgeTripleIndex < edgeTriple.length; edgeTripleIndex++ ) {
    edgeIndex = edgeTriple[ edgeTripleIndex ];
    edge = edges[ edgeIndex ];

    if ( edge.v1 == vertexIndexFirst && edge.v2 == vertexIndexSecond ) {
      return getPlusOneToString( edgeIndex );
    }

    if ( edge.v1 == vertexIndexSecond && edge.v2 == vertexIndexFirst ) {
      return getPlusOneToString( edgeIndex );
    }
  }

  print( "Inconsistent triangle mesh." );
  print( edgeTriple );
  print( vertexIndexFirst );
  print( vertexIndexSecond );
}

String getPlusOneToString( int number )
{
  return ( number + 1 ).toString() ;
}

/**
Get Vec3 rotated around X axis from counterclockwise angle and vector.

@param  angle counterclockwise angle from 1, 0
@param  vector3 Vec3 whose rotation will be returned
@return  vector3 rotated around X axis
*/

Vec3 getRoundXAxis( double angle, Vec3 vector3 )
{
  x = Math.cos( angle );
  y = Math.sin( angle );

  return new Vec3( vector3.x, vector3.y * x - vector3.z * y, vector3.y * y + vector3.z * x );
}

/**
Get Vec3 rotated around Y axis from counterclockwise angle and vector.

@param  angle counterclockwise angle from 1, 0
@param  vector3 Vec3 whose rotation will be returned
@return  vector3 rotated around Y axis
*/

Vec3 getRoundYAxis( double angle, Vec3 vector3 )
{
  x = Math.cos( angle );
  y = Math.sin( angle );

  return new Vec3( vector3.x * x - vector3.z * y, vector3.y, vector3.x * y + vector3.z * x );
}

/**
Get Vec3 rotated around Z axis from counterclockwise angle and vector.

@param  angle counterclockwise angle from 1, 0
@param  vector3 Vec3 whose rotation will be returned
@return  vector3 rotated around Z axis
*/

Vec3 getRoundZAxis( double angle, Vec3 vector3 )
{
  x = Math.cos( angle );
  y = Math.sin( angle );

  return new Vec3( vector3.x * x - vector3.y * y, vector3.x * y + vector3.y * x, vector3.z );
}

/**
Output the curves and/or meshes.
*/

void outputCurvesMeshes( ObjectInfo[] objectInfoArray )
{
  if ( objectInfoArray.length < 1 ) {
    return;
  }

  for ( int objectIndex = 0; objectIndex < objectInfoArray.length; objectIndex++ ) {
    objectInfo = objectInfoArray[ objectIndex ];

    if ( objectInfoArray.length > 1 && exportSelectionCheckbox.getState() ) {

      if ( bufferedWriter != null ) {
        //Close the output stream
        bufferedWriter.close();
        bufferedWriter = null;
      }

      file = null;
      parentFile = new File( gnuSurfaceFilenameTextField.getText() ).getParentFile();

      if ( parentFile != null ) {
        file = new File( parentFile, objectInfo.name + ".gts" );
      }

      if ( file != null ) {
        bufferedWriter = new BufferedWriter( new FileWriter( file ) );
      }
    }

    if ( file != null && bufferedWriter != null ) {
      print( objectInfo.name + " is being saved as the GNU Triangulated Surface file " + file.getAbsolutePath() );
    }

    outputCurveMesh( objectInfo );

    if ( bufferedWriter != null ) {
      //Close the output stream
      bufferedWriter.close();
      bufferedWriter = null;
    }
  }
}

/**
Output the curve and/or mesh.
Quoted from http://gts.sourceforge.net/reference/gts-surfaces.html#GTS-SURFACE-WRITE
"All the lines beginning with GTS_COMMENTS (#!) are ignored. The first line contains three unsigned integers separated by spaces. The first integer is the number of vertices, nv, the second is the number of edges, ne and the third is the number of faces, nf.

Follows nv lines containing the x, y and z coordinates of the vertices. Follows ne lines containing the two indices (starting from one) of the vertices of each edge. Follows nf lines containing the three ordered indices (also starting from one) of the edges of each face.

The format described above is the least common denominator to all GTS files. Consistent with an object-oriented approach, the GTS file format is extensible. Each of the lines of the file can be extended with user-specific attributes accessible through the read() and write() virtual methods of each of the objects written (surface, vertices, edges or faces). When read with different object classes, these extra attributes are just ignored."
*/

void outputCurveMesh( ObjectInfo objectInfo )
{
  triangleMesh = objectInfo.object;
  edges = triangleMesh.getEdges();
  faces = triangleMesh.getFaces();
  vertexPositions = triangleMesh.getVertexPositions();
  origin = objectInfo.coords.getOrigin();
  orientation = objectInfo.coords.getRotationAngles();
  orientationXRadians = orientation[ 0 ] * Math.PI / 180.0;
  orientationYRadians = orientation[ 1 ] * Math.PI / 180.0;
  orientationZRadians = orientation[ 2 ] * Math.PI / 180.0;
  outputString( vertexPositions.length + " " + edges.length + " " + faces.length + " Number of Vertices, Number of Edges, Number of Faces" );

  for ( int vertexIndex = 0; vertexIndex < vertexPositions.length; vertexIndex++ ) {
    vertexPosition = vertexPositions[ vertexIndex ];
    vertexPosition = getRoundZAxis( - orientationZRadians, vertexPosition );
    vertexPosition = getRoundXAxis( - orientationXRadians, vertexPosition );
    vertexPosition = getRoundYAxis( orientationYRadians, vertexPosition );
    vertexPosition.add( origin );
    vertexLine = vertexPosition.x.toString() + " " + vertexPosition.y.toString() + " " + vertexPosition.z.toString();

    if ( vertexIndex == 0 ) {
      vertexLine += " Vertex Coordinates XYZ";
    }

    outputString( vertexLine );
  }

  for ( int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++ ) {
    edge = edges[ edgeIndex ];
    edgeLine = getPlusOneToString( edge.v1 ) + " " + getPlusOneToString( edge.v2 );

    if ( edgeIndex == 0 ) {
      edgeLine += " Edge Vertex Indices Starting from 1";
    }

    outputString( edgeLine );
  }

  for ( int faceIndex = 0; faceIndex < faces.length; faceIndex++ ) {
    face = faces[ faceIndex ];
    int[] edgeTriple = { face.e1, face.e2, face.e3 };

    faceLine = getEdgeString( edges, edgeTriple, face.v1, face.v2 ) + " " + getEdgeString( edges, edgeTriple, face.v2, face.v3 ) + " " + getEdgeString( edges, edgeTriple, face.v3, face.v1 );

    if ( faceIndex == 0 ) {
      faceLine += " Face Edge Indices Starting from 1";
    }

    outputString( faceLine );
  }
}

/**
Output comma separated strings followed by a linefeed.
*/

void outputString( string )
{
  if ( printSelectionCheckbox.getState() ) {
    print( string );
  }

  if ( bufferedWriter != null ) {
    bufferedWriter.write( string.replace( ", ", "," ) + "\n" );
  }
}

/**
Add radio button groups to the preference widgets.

@param  radioButtonGroups radio button groups which will be added to the memorable widgets
@param  widgetVector memorable widgets
*/

void preferencesAddRadioButtonGroups( RadioButtonGroup[] radioButtonGroups, Vector widgetVector )
{
  for ( int radioIndex = 0; radioIndex < radioButtonGroups.length; radioIndex++ ) {
    radioButtonGroup = radioButtonGroups[ radioIndex ];
    radioButtonGroupIterator = radioButtonGroup.getRadioButtons();

    while ( radioButtonGroupIterator.hasNext() ) {
      radioButton = radioButtonGroupIterator.next();
      preferencesAddWidgetWithString( radioButton, radioButton.getText(), widgetVector );
    }
  }
}

/**
Add widgets which have titles.

@param  widgets widgets which have titles
@param  widgetStrings widget titles
@param  widgetVector memorable widgets
*/

void preferencesAddWidgetsWithStrings( Widget[] widgets, String[] widgetStrings, Vector widgetVector )
{
  for ( int widgetIndex = 0; widgetIndex < widgets.length; widgetIndex++ ) {
    widget = widgets[ widgetIndex ];

    if ( widget instanceof BCheckBox || widget instanceof BOutline || widget instanceof BTextField || widget instanceof ValueField ) {
      preferencesAddWidgetWithString( widget, widgetStrings[ widgetIndex ], widgetVector );
    }
  }
}

/**
Give the widget a name and add it to the widget vector.

@param  widget widget which will be given a name
@param  widgetStrings widget name
@param  widgetVector memorable widgets
*/

void preferencesAddWidgetWithString( Widget widget, String widgetString, Vector widgetVector )
{
  widget.setName( widgetString );
  widgetVector.add( widget );
}

/**
Read widget settings from preferences file.

@param  preferencesFilename preferences filename
@param  widgetVector memorable widgets
*/

void preferencesRead( String preferencesFilename, Vector widgetVector )
{
  preferencesFile = new File( preferencesFilename );

  if ( !preferencesFile.canRead() ) {
    return;
  }

  BufferedReader preferencesReader = new BufferedReader( new FileReader( preferencesFile ) );

  line = preferencesReader.readLine();

  while ( line != null ) {
    preferencesReadLine( line, widgetVector );
    line = preferencesReader.readLine();
  }
}

/**
Read line of preferences and set widget to that line.

@param  line line of preferences
@param  widgetVector memorable widgets
*/

void preferencesReadLine( String line, Vector widgetVector )
{
  splitLine = line.split( "\t" );

  if ( splitLine.length < 2 ) {
    return;
  }

  name = splitLine[ 0 ];

  for ( int widgetIndex = 0; widgetIndex < widgetVector.size(); widgetIndex++ ) {
    widget = widgetVector.elementAt( widgetIndex );

    if ( widget.getName().equals( name ) ) {
      preferencesReadWidget( splitLine[ 1 ], widget );

      return;
    }
  }
}

/**
Set widget to preferences value.

@param  value preferences value
@param  widget widget to be set to value
*/

void preferencesReadWidget( String value, Widget widget )
{
  if ( widget instanceof BCheckBox || widget instanceof BRadioButton ) {
    widget.setState( Boolean.valueOf( value ) );

    return;
  }

  if ( widget instanceof BOutline ) { // it would be better to save the value instead of index because the list might change, but I'm lazy
    bList = widget.getContent().getContent();
    selectedIndex = Integer.valueOf( value );
    bList.setSelected( selectedIndex, true );
    bList.scrollToItem( selectedIndex );

    return;
  }

  if ( widget instanceof BTextField ) {
    widget.setText( value );

    return;
  }

  if ( widget instanceof ValueField ) {
    widget.setValue( Double.valueOf( value ) );
  }
}

/**
Write widget settings to preferences file.

@param  preferencesFilename preferences filename
@param  widgetVector memorable widgets
*/

void preferencesWrite( String preferencesFilename, Vector widgetVector )
{
  preferencesFile = new File( preferencesFilename );

  if ( preferencesFile == null ) {
    print( "Can not write preferences to " + preferencesFilename );

    return;
  }

  BufferedWriter preferencesWriter = new BufferedWriter( new FileWriter( preferencesFile ) );

  for ( int widgetIndex = 0; widgetIndex < widgetVector.size(); widgetIndex++ ) {
    widget = widgetVector.elementAt( widgetIndex );
    preferencesWriteWidget( preferencesWriter, widget );
  }

  //Close the output stream
  preferencesWriter.close();
}

/**
Write widget settings to line of preferences.

@param  preferencesWriter buffered preferences file writer
@param  widget widget to be written
*/

void preferencesWriteWidget( BufferedWriter preferencesWriter, Widget widget )
{
  widgetString = widget.getName() + "\t";

  if ( widget instanceof BCheckBox || widget instanceof BRadioButton ) {
    preferencesWriter.write( widgetString + widget.getState().toString() + "\n" );

    return;
  }

  if ( widget instanceof BOutline ) { // it would be better to save the value because the list might change, but I'm lazy
    BList bList = widget.getContent().getContent();
    bList = widget.getContent().getContent();
    preferencesWriter.write( widgetString + bList.getSelectedIndex().toString() + "\n" );

    return;
  }

  if ( widget instanceof BTextField ) {
    preferencesWriter.write( widgetString + widget.getText() + "\n" );

    return;
  }

  if ( widget instanceof ValueField ) {
    preferencesWriter.write( widgetString + widget.getValue().toString() + "\n" );
  }
}

// Set default parameters.
bufferedWriter = null;
exportSelectionCheckbox = new BCheckBox( "", false );
file = null;
gnuSurfaceFilenameTextField = new BTextField( "triangle_mesh.gts" );
String preferencesFilename = "gnu_triangulated_surface_preferences.csv";
printSelectionCheckbox = new BCheckBox( "", true );

Widget[] widgets = new Widget[] {	exportSelectionCheckbox,
						printSelectionCheckbox };

String[] widgetStrings = new String[] {	"Export Selection:",
						"Print Selection:" };

// change the user interface parameters from default to preferences
Vector widgetVector = new Vector();
preferencesAddWidgetsWithStrings( widgets, widgetStrings, widgetVector );
preferencesAddWidgetWithString( gnuSurfaceFilenameTextField, "GNU Triangulated Surface Filename:", widgetVector );
preferencesRead( preferencesFilename, widgetVector );

curvesMeshesInfo = getCurvesMeshesInfo();

if ( curvesMeshesInfo.length < 1 ) {
  print( "There is no triangle mesh in the scene, so nothing will be done." );
  print( "It may be that there is a solid shape which is not a triangle mesh, in which case convert that shape to a triangle mesh." );

  return;
}

dialog = new ComponentsDialog( window, "Export and/or Print Selection", widgets, widgetStrings );

if ( !dialog.clickedOk() ) return;

if ( exportSelectionCheckbox.getState() ) {
  fileDescriptor = new BFileChooser( BFileChooser.SAVE_FILE, "Select name for the GNU Triangulated Surface file.");
  fileDescriptor.setSelectedFile( new File( gnuSurfaceFilenameTextField.getText() ) );
  fileDescriptor.showDialog( window );
  file = fileDescriptor.getSelectedFile();

  if ( file != null ) {
    gnuSurfaceFilenameTextField.setText( file.getAbsolutePath() );
    bufferedWriter = new BufferedWriter( new FileWriter( file ) );
  }
}

preferencesWrite( preferencesFilename, widgetVector );
outputCurvesMeshes( curvesMeshesInfo );
