/*
 * NCSA Data To Knowledge (D2K) Project
 * Automated Learning Group
 * National Center for Supercomputing Applications
 * University of Illinois at Urbana-Champaign
 * 605 E. Springfield, Champaign, IL 61820
 *
 * d2k@ncsa.uiuc.edu
 *
 * Copyright 2003, Board of Trustees of the University of Illinois,
 * All Rights Reserved
 *
 * NCSA Data To Knowledge (D2K) software, source code, binary code, and Java-byte 
 * code including D2K modules and D2K itineraries (hereafter, Software) is 
 * copyrighted by the Board of Trustees of the University of Illinois (UI),
 * and ownership remains with the UI.
 *
 * Prior to receiving this Software Source Code, You must have agreed to a
 * non-exclusive, non-transferable, restricted License of Software with UI for
 * limited Research Use and/or Internal Business Use. The terms of that License
 * control the use of this Software Source Code. For the sake of convenience, 
 * certain provisions of that License are stated here. However, in the event
 * of any real or perceived differences between the terms of the License and the
 * statements made herein, the License controls.  
 *
 * You may not distribute the Software including the Binary Code and this Source
 * Code to third parties.
 *
 * You may make Derivative Works. You are encouraged to provide information to 
 * UI regarding Derivative Works and your experience with Software. However, if
 * You make any Derivative Work based on or derived from the Software, then You
 * will: (1) clearly notify users that such Derivative Work is a modified version
 * and uses or is derived from the original NCSA Data To Knowledge (D2K)
 * developed at UI, and include specific language in that notice as provided for
 * in the License, and (2) acknowledge via citation and provide UI with a copy of
 * any report or publication using the Software or Derivative Work.
 *
 * If You wish to make Commercial Use of the Software or Derivative Works, then
 * You should contact the UI, c/o NCSA, to negotiate an appropriate license for
 * such Commercial Use. Commercial Use includes sale, lease, license,
 * distribution or otherwise making the Software or Derivative Works available to
 * third parties, which includes, but is not limited to, integration of all or
 * part of the Software or Derivative Work into a product for sale or license by
 * or on behalf of You to third parties.
 *
 * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY
 * PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. THE UI
 * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE USERS OF THIS SOFTWARE.
 *
 * By using or copying this Software, You agree to abide by the copyright law
 * and all other applicable laws of the U.S. including, but not limited to,
 * export control laws, and the terms of the License. UI shall have the right
 * to terminate its license with You immediately upon Your breach of, or
 * non-compliance with, any of its terms. You may be held legally responsible
 * for any copyright infringement that is caused or encouraged by Your failure
 * to abide by the terms of the License.
 */

package ncsa.d2k.modules.core.io.file.output;

import ncsa.d2k.core.modules.*;
import ncsa.d2k.modules.core.datatype.table.*;
import java.io.*;
import java.beans.PropertyVetoException;

/**
 * This module writes the contents of a <code>Table</code> to a flat file.
 *
 * @author David Clutter
 */
public class WriteTableToFile extends OutputModule {

   transient String delimiter;

   String  delimChar = "C";
   boolean useDataTypes = true;
   boolean useColumnLabels = true;

   public void setDelimChar(String c) throws PropertyVetoException {
      // here we check for valid entries and save as upper case
      if (c.equalsIgnoreCase("C")) {
         delimChar = "C";
      } else if (c.equalsIgnoreCase("S")) {
         delimChar = "S";
      } else if (c.equalsIgnoreCase("T")) {
         delimChar = "T";
      } else {
         throw new PropertyVetoException(
              "An invalid Delimiter Character was entered. "+
              "Enter C for comma, S for space, or T for tab.",
              null);
      }
   }

   public String getDelimChar() {
      return delimChar;
   }

   public void setUseDataTypes(boolean b) {
      useDataTypes = b;
   }

   public boolean getUseDataTypes() {
      return useDataTypes;
   }

   public void setUseColumnLabels(boolean b) {
      useColumnLabels = b;
   }

   public boolean getUseColumnLabels() {
      return useColumnLabels;
   }

    /**
       Return a description of the function of this module.
       @return A description of this module.
    */
    public String getModuleInfo() {
       StringBuffer sb = new StringBuffer("<p>Overview: ");
       sb.append("<p>This module writes the contents of a Table to a file. ");
       sb.append("</p><p>Detailed Description: ");
       sb.append("This module writes the contents of the input ");
       sb.append("<i>Table</i> to the file specified by the input <i>File Name</i> ");
       sb.append("The user can select a space, a common, or a tab as the ");
       sb.append("column delimiter using the properties editor. ");
       sb.append("If the <i>useColumnLabels</i> property is set, ");
       sb.append("the first row of the file will be the column labels. ");
       sb.append("If the <i>useDataTypes</i> property is set, the data type of ");
       sb.append("each column will be written to the file.");
       sb.append("</p><p>Data Handling: ");
       sb.append("This module does not destroy or modify its input data.");
       sb.append("</p>");
       return sb.toString();
   }

   public PropertyDescription[] getPropertiesDescriptions() {

      PropertyDescription[] descriptions = new PropertyDescription [3];

      descriptions[0] = new PropertyDescription("delimChar",
         "Delimiter Character (C=comma, S=space, T=tab)",
         "Selects the delimiter character used to separate columns in the file.  "+
         "Enter C for comma, S for space, or T for tab.");

      descriptions[1] = new PropertyDescription("useColumnLabels",
         "Write Column Labels",
         "Controls whether the table's column labels are written to the file.");

      descriptions[2] = new PropertyDescription("useDataTypes",
         "Write Data Types",
         "Controls whether the table's column data types are written to the file.");

      return descriptions;

   }

    /**
       Return the name of this module.
       @return The name of this module.
    */
    public String getModuleName() {
      return "Write Table to File";
   }

    /**
       Return a String array containing the datatypes the inputs to this
       module.
       @return The datatypes of the inputs.
    */
    public String[] getInputTypes() {
      String[] types = {"java.lang.String","ncsa.d2k.modules.core.datatype.table.Table"};
      return types;
   }

    /**
       Return a String array containing the datatypes of the outputs of this
       module.
       @return The datatypes of the outputs.
    */
    public String[] getOutputTypes() {
      String[] types = {      };
      return types;
   }

    /**
       Return a description of a specific input.
       @param i The index of the input
       @return The description of the input
    */
    public String getInputInfo(int i) {
      switch (i) {
         case 0: return "The name of the file to be written.";
         case 1: return "The Table to write.";
         default: return "No such input";
      }
   }

    /**
       Return the name of a specific input.
       @param i The index of the input.
       @return The name of the input
    */
    public String getInputName(int i) {
      switch(i) {
         case 0:
            return "File Name";
         case 1:
            return "Table";
         default: return "No such input";
      }
   }

    /**
       Return the description of a specific output.
       @param i The index of the output.
       @return The description of the output.
    */
    public String getOutputInfo(int i) {
      switch (i) {
         default: return "No such output";
      }
   }

    /**
       Return the name of a specific output.
       @param i The index of the output.
       @return The name of the output
    */
    public String getOutputName(int i) {
      switch(i) {
         default: return "No such output";
      }
   }

    /**
      Write the table to the file.
   */
    public void doit() throws Exception {
      String fileName = (String)pullInput(0);
      Table vt = (Table)pullInput(1);
      FileWriter fw;
      String newLine = "\n";

      delimiter = ",";      // default to comma
      if (delimChar.equals("S")) {
         delimiter = " ";
      } else if (delimChar.equals("T")) {
         delimiter = "\t";
      }

      try {
          // write the actual data
         writeTable(vt, delimiter, fileName, useColumnLabels, useDataTypes);
      }
      catch (IOException e) {
         throw new IOException( getAlias() +
              ": Could not open file: " + fileName +
              "\n" + e );
      }

   }


   /**
      Get the datatype of a column.
   */
   public static final String getDataType(int i) {
      switch(i) {
         case 0:
            return "int";
         case 1:
            return "float";
         case 2:
            return "double";
         case 3:
            return "short";
         case 4:
            return "long";
         case 5:
            return "String";
         case 6:
            return "char[]";
         case 7:
            return "byte[]";
         case 8:
            return "boolean";
         case 9:
            return "Object";
         case 10:
            return "byte";
         case 11:
            return "char";
         default:
            return "String";
      }
   }

   public static void writeTable(Table vt, String delimiter, String fileName,
      boolean writeColumnLabels, boolean writeColumnTypes) throws IOException {

      FileWriter fw = new FileWriter(fileName);
      String newLine = "\n";

      // write the column labels
      if(writeColumnLabels) {
         for(int i = 0; i < vt.getNumColumns(); i++) {
            String s = vt.getColumnLabel(i);
            if (s == null || s.length() == 0)
               s = "column_" + i;
            fw.write(s, 0, s.length());
            if(i != (vt.getNumColumns() - 1))
               fw.write(delimiter.toCharArray(), 0, delimiter.length());
         }
         fw.write(newLine.toCharArray(), 0, newLine.length());
      }

      // write the datatypes.
      if(writeColumnTypes) {
         for(int i = 0; i < vt.getNumColumns(); i++) {
            String s = getDataType(vt.getColumnType(i));
            fw.write(s, 0, s.length());
            if(i != (vt.getNumColumns() - 1))
               fw.write(delimiter.toCharArray(), 0, delimiter.length());
         }
         fw.write(newLine.toCharArray(), 0, newLine.length());
      }

      // write the actual data
      for(int i = 0; i < vt.getNumRows(); i++) {
         for(int j = 0; j < vt.getNumColumns(); j++) {
            String s;

            if (vt.isValueMissing(i, j) || vt.isValueEmpty(i, j))
                s = "";
            else
                s = vt.getString(i, j);

            //System.out.println("s: "+s);
            fw.write(s, 0, s.length());
            if(j != (vt.getNumColumns() - 1) )
               fw.write(delimiter.toCharArray(), 0, delimiter.length());
         }
         fw.write(newLine.toCharArray(), 0, newLine.length());
      }
      fw.flush();
      fw.close();

   }

}
// Start QA Comments
// 3/6/03 - Received from David C and Greg & QA started by Ruth
//        - Added code to allow for selection of delimiter character and
//          updated description accordingly.   Removed lots of commented-out
//          code.  Added exception handler for IO exceptions instead of just
//          message to stderr.   Committing to Basic.
// End QA Comments