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

<script>
	<name>Import GNU Triangulated Surface</name>
	<author>Enrique Perez</author>
	<version>1.0</version>
	<date>29-Apr-2008</date>
	<description>
This script imports GNU Triangulated Surfaces as triangle meshes.
    </description>
	<licenseType>gpl</licenseType>
</script>
*/

/**
Add a delete the last object of the scene undo record, to the scene.
*/

void addUndoRecord() {
  scene = window.getScene();
  undoAdd = new UndoRecord ( window, false, UndoRecord.DELETE_OBJECT, new Object[] { new Integer( scene.getNumObjects() - 1 ) } );
  window.setUndoRecord( undoAdd );
}

int getSameVertexIndex( edgeFirst, edgeSecond )
{
  for ( int endpointIndex = 0; endpointIndex < 2; endpointIndex++ ) {
    endpoint = edgeFirst[ endpointIndex ];

    if ( endpoint == edgeSecond[ 0 ] ) {
      return endpoint;
    }

    if ( endpoint == edgeSecond[ 1 ] ) {
      return endpoint;
    }
  }

  print( "Inconsistent GNU Triangulated Surface" );
  print( edgeFirst );
  print( edgeSecond );
  return 0;
}

/**
Import GNU Triangulated Surface from a file.
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 importFile( gnuFile )
{
  bufferedReader = new BufferedReader( new FileReader( gnuFile ) );
  Vector lineVector = new Vector();
  line = bufferedReader.readLine();
  name = gnuFile.getName();
  int lastIndexOfDot = name.lastIndexOf( '.' );

  if ( lastIndexOfDot > 0 ) {
    name = name.substring( 0, lastIndexOfDot );
  }

  while ( line != null ) {

    if ( line.length() > 0 ) {
      firstCharacter = line.charAt( 0 );

      if ( firstCharacter != '#' && firstCharacter != '!' ) {
        lineVector.add( line );
      }
    }

    line = bufferedReader.readLine();
  }

  splitLine = lineVector.get( 0 ).split( " " );
  int numberOfVertices = Integer.valueOf( splitLine[ 0 ] );
  int numberOfEdges = Integer.valueOf( splitLine[ 1 ] );
  int numberOfFaces = Integer.valueOf( splitLine[ 2 ] );
  edges = new int[ numberOfEdges ][ 2 ];
  faces = new int[ numberOfFaces ][ 3 ];
  vertices = new Vec3[ numberOfVertices ];

  for ( int vertexIndex = 0; vertexIndex < numberOfVertices; vertexIndex++ ) {
    line = lineVector.get( vertexIndex + 1 );
    splitLine = line.split( " " );
    vertex = new Vec3( Double.valueOf( splitLine[ 0 ] ), Double.valueOf( splitLine[ 1 ] ), Double.valueOf( splitLine[ 2 ] ) );
    vertices[ vertexIndex ] = vertex;
  }

  int edgeStart = numberOfVertices + 1;

  for ( int edgeIndex = 0; edgeIndex < numberOfEdges; edgeIndex++ ) {
    line = lineVector.get( edgeIndex + edgeStart );
    splitLine = line.split( " " );
    int[] edge = { Integer.valueOf( splitLine[ 0 ] ) - 1, Integer.valueOf( splitLine[ 1 ] ) - 1 };
    edges[ edgeIndex ] = edge;
  }

  int faceStart = edgeStart + numberOfEdges;

  for ( int faceIndex = 0; faceIndex < numberOfFaces; faceIndex++ ) {
    line = lineVector.get( faceIndex + faceStart );
    splitLine = line.split( " " );
    edgeFirst = edges[ Integer.valueOf( splitLine[ 0 ] ) - 1 ];
    edgeSecond = edges[ Integer.valueOf( splitLine[ 1 ] ) - 1 ];
    edgeThird = edges[ Integer.valueOf( splitLine[ 2 ] ) - 1 ];
    int[] vertexIndices = { getSameVertexIndex( edgeFirst, edgeSecond ), getSameVertexIndex( edgeSecond, edgeThird ), getSameVertexIndex( edgeThird, edgeFirst ) };
    faces[ faceIndex ] = vertexIndices;
  }

  CoordinateSystem coordinateSystem = new CoordinateSystem();

  while ( window.getScene().getObject( name ) != null ) {
    name += "2";
  }

  mesh = new TriangleMesh ( vertices, faces );
  mesh.setSmoothingMethod( Mesh.NO_SMOOTHING );
  window.addObject( mesh, coordinateSystem, name, null );
  addUndoRecord();
}

/**
Import GNU Triangulated Surface from a file or from files in a directory.
*/

void importFiles()
{
  if ( openFileButton.getState() ) {
    importFile( file );
    return;
  }

  filesInDirectory = file.getParentFile().listFiles();

  for ( int fileIndex = 0; fileIndex < filesInDirectory.length; fileIndex++ ) {
    directoryFile = filesInDirectory[ fileIndex ];
    String directoryFileName = directoryFile.getName();

    if ( directoryFileName.endsWith( ".gts" ) ) {
      importFile( directoryFile );
    }
  }
}

/**
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.
directoryRadioButtonGroup = new RadioButtonGroup();
openDirectoryButton = new BRadioButton( "Import All GNU Triangulated Surface Files in a Directory", false, directoryRadioButtonGroup );
openFileButton = new BRadioButton( "Import File", true, directoryRadioButtonGroup );
directoryGridContainer = new GridContainer( 1, 2 );
directoryGridContainer.setDefaultLayout( new LayoutInfo( LayoutInfo.WEST, LayoutInfo.NONE, new Insets( 2, 2, 2, 2 ), null ) );
directoryGridContainer.add( openDirectoryButton, 0, 0 );
directoryGridContainer.add( openFileButton, 0, 1 );
gnuSurfaceFilenameTextField = new BTextField( "" );
String preferencesFilename = "import_gnu_triangulated_surface_preferences.csv";

Widget[] widgets = new Widget[] {	directoryGridContainer };
String[] widgetStrings = new String[] {	"Open File or Directory:" };

// change the user interface parameters from default to preferences
Vector widgetVector = new Vector();
RadioButtonGroup[] radioButtonGroups = new RadioButtonGroup[] { directoryRadioButtonGroup };
preferencesAddRadioButtonGroups( radioButtonGroups, widgetVector );
preferencesAddWidgetsWithStrings( widgets, widgetStrings, widgetVector );
preferencesAddWidgetWithString( gnuSurfaceFilenameTextField, "GCode Filename:", widgetVector );
preferencesRead( preferencesFilename, widgetVector );

dialog = new ComponentsDialog( window, "Import GNU Triangulated Surface File or Directory", widgets, widgetStrings );

if ( !dialog.clickedOk() ) return;

fileChooser = new BFileChooser( BFileChooser.OPEN_FILE, "Import GNU Triangulated Surface File");
gnuSurfaceFile = new File( gnuSurfaceFilenameTextField.getText() );

if ( gnuSurfaceFile.canRead() ) {
  fileChooser.setSelectedFile( gnuSurfaceFile );
}

fileChooser.showDialog( window );
file = fileChooser.getSelectedFile();

if ( file == null ) {
  return;
}

gnuSurfaceFilenameTextField.setText( file.getAbsolutePath() );
preferencesWrite( preferencesFilename, widgetVector );
importFiles();
