package yi.util.io;

import java.io.*;
import java.awt.*;
import java.awt.image.*;
import Acme.JPM.Encoders.GifEncoder;
import java.util.*;
import java.util.List;

public final class OutputResult
{
  /**
   * Postfix of the result output files.
   */
  public static String POSTFIX = ".rst";

  /**
   * When suffix a serial number to a file name, like 1, 2, .., 10, 11, better
   * to make them fixed length, like 01, 02, ..., 10, 11.  This parameter is
   * the length of the suffixs.
   */
  public static int SUFFIX_LENGTH = 2;
  static boolean VERBOSE=false;

  static int tempNum=0;

  /**
   * different types of file may have different suffix number (a suffix number
   * can be used to output several different version of copies of same name
   * file).  m_fileNums store the current number of each type of file.
   */
  static HashMap m_fileNums = new HashMap();

  /**
   * Return the suffix number for the next output file
   * @return
   */
  public static int getNextFileNum(String file_type) {
    if (m_fileNums.containsKey(file_type)) {
      int current_num = ((Integer)m_fileNums.get(file_type)).intValue();
      m_fileNums.put(file_type, new Integer(current_num + 1));
      return current_num;
    } else {  // first of the file type
      m_fileNums.put(file_type, new Integer(1));
      return 0;
    }
  }

  /**
   * Return the suffix number for the next output file
   * @return
   */
  public static int getNextFileNum() { return ++tempNum;}

  /**
   * Output Gif format image with given name.  If suffix = true, the file name
   * will has a suffix of a counter number and ".gif"; else the given name
   * will be used.
   * @param name
   * @param data
   * @param w
   * @param h
   * @param suffix
   */
  static public void outputGIFImage(String name, int[] data, int w, int h, boolean suffix)
  {
      if (suffix) {
        int tempNum = getNextFileNum();
        name = name + "_" + yi.util.data.Conversion.toString(tempNum, SUFFIX_LENGTH) + ".gif";
      }
      Image img= Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(w, h, data, 0, w));
      try {
          GifEncoder gif=  new GifEncoder(img, new FileOutputStream(name));
          gif.encode();
      }
      catch (java.io.IOException e) {
          System.err.println(e);
      }
  }

  static public void outputGIFImage(String name, int[] data, int w, int h)
  {
    outputGIFImage(name, data, w, h, false);
  }

  public static PrintStream getPrintStream(String filename) {
    try {
      PrintStream out = new PrintStream(
        new BufferedOutputStream(new FileOutputStream(filename)));
      return out;
    } catch (java.io.FileNotFoundException e) {
      e.printStackTrace();
    }
    return null;
  }


  /**
   * Construct a PrintStream for the output.  The real output name will be
   * root_#.postfix.  The number is increased by 1 every time this method is
   * invoked.  For example, if root = "test" and postfix = ".rst", then the
   * first time the method is invoked, it print stream returned is "test_1.rst";
   * the second time, "test_2.rst".  If the stream construction fails, return
   * null.
   * @param root the root name of output file
   * @param postfix the postfix of the filename
   * @return the constructed print stream
   */
  static PrintStream getPrintStream(String root, String postfix) {
    int tempNum = getNextFileNum();
    String filename = root + "_" + tempNum + postfix;
    try {
      PrintStream out = new PrintStream(
        new BufferedOutputStream(new FileOutputStream(filename)));
      return out;
    } catch (java.io.FileNotFoundException e) {
      e.printStackTrace();
    }
    return null;
  }

  /**
   * Print out the 1D data to the given PrintStream out
   * @param out the output stream
   * @param data the data to print
   */
  static public void print(PrintStream out, int[] data) {
      int w = data.length;
      for (int j=0;j<w;j++) {
          out.print(data[j]+"\t");
      }
      out.println();
  }

  /**
   * Print out the 1D data to the given PrintStream out
   * @param out the output stream
   * @param data the data to print
   */
  static public void print(PrintStream out, int[] data, boolean as_column) {
      if (!as_column) {
        print(out, data) ;
        return;
      }
      int w = data.length;
      for (int j=0;j<w;j++) out.println(data[j]);
  }


  /**
   * Print out the 1D int data to the given PrintStream out
   * @param out the output stream
   * @param data the data to print
   */
  static public void printIntCollection(PrintStream out, Collection data) {
      int w = data.size();
      for (Iterator i=data.iterator(); i.hasNext(); ) {
        int value = ((Integer)i.next()).intValue();
        out.print(value + "\t");
      }
      out.println();
  }

  /**
   * Print out the 1D String data to the given PrintStream out
   * @param out the output stream
   * @param data the data to print
   */
  static public void printStringCollection(PrintStream out, Collection data, char separator) {
      boolean first = true;
      int i_index = 0;
      for (Iterator i=data.iterator(); i.hasNext(); i_index++) {
        String value = (String)i.next();
        if (!first) out.print(separator);
        out.print(value);
        first = false;
      }
      out.flush();
  }

  public static class TestprintStringCollection {
    public static void main(String[] args) {
      Collection a = new LinkedList();
      for (int i=0; i<90; i++) {
        a.add("img" + i);
      }
      OutputResult.printStringCollection(OutputResult.getPrintStream(
            "t.rst"), a, '\n');
    }
  }

  /**
   * Print the collection with the content sorted
   * @param out
   * @param c
   */
  static public void printSortedCollection(PrintStream out, Collection c) {
    List list = new ArrayList(c);
    Collections.sort(list);
    for (Iterator i=list.iterator(); i.hasNext(); ) {
      out.println(i.next());
    }
  }

  /**
   * Print the map with the content sorted by key
   * @param out
   * @param m
   */
  static public void printSortedMap(PrintStream out, Map m) {
    List list = new ArrayList(m.keySet());
    Collections.sort(list);
    for (Iterator i=list.iterator(); i.hasNext(); ) {
      Object key = i.next();
      Object value = m.get(key);
      out.println(key.toString() + " -> " + value);
    }
  }



  /**
   * Print out the 1D data to the file with the given name.
   * @param name the root name of the file to output.  The real output name
   *             will be name_#.rst.  See #getPrintStream for more details
   * @param data the data to print
   */
  static public void print(String name, int[] data) {
    print(name, data, false);
  }

  static public void print(String name, int[] data, boolean as_column) {
    PrintStream out = getPrintStream(name, POSTFIX);
    if (out != null) {
      print(out, data, as_column);
      out.close();
    }
  }

  /**
   *
   * @param root_name
   * @param data
   * @param suffix
   * @param file_type
   */
  static public void print(PrintStream out, String[] data)
  {
      int w = data.length;
      for (int j=0;j<w;j++) {
          out.println(data[j]);
      }
  }

  /**
   *
   * @param root_name
   * @param data
   * @param suffix
   * @param file_type
   */
  static public void print(String name, String[] data, boolean suffix, String file_type) throws java.io.IOException
  {
      if (suffix) {
        int tempNum = getNextFileNum(file_type);
        name = name + "." + yi.util.data.Conversion.toString(tempNum, SUFFIX_LENGTH) + ".file_type";
      }

      PrintStream out = new PrintStream (new FileOutputStream(name));
      print(out, data);
  }

  /**
   * Print out the 2D data to the given PrintStream out
   * @param out the output stream
   * @param data the data to print
   * @param w the width of the data
   * @param h the height of the data
   */
  static public void print(PrintStream out, int[] data, int w, int h) {
      for (int i=0;i<h;i++) {
          for (int j=0;j<w;j++) {
              out.print(data[i*w+j]+"\t");
          }
          out.println();
      }
  }

  /**
   * Print out the 2D data to the file with the given name.
   * @param name the root name of the file to output.  The real output name
   *             will be name_#.rst.  See #getPrintStream for more details
   * @param data the data to print
   * @param w the width of the data
   * @param h the height of the data
   */
  static public void print(String name, int[] data, int w, int h) {
    PrintStream out = getPrintStream(name, POSTFIX);
    if (out != null) {
      print(out, data, w, h);
      out.close();
    }
  }

  /**
   * Print out the 2D data to the given PrintStream out
   * @param out the output stream
   * @param data the data to print
   * @param w the width of the data
   * @param h the height of the data
   */
  static public void print(PrintStream out, int[][] data, int w, int h) {
      for (int i=0;i<h;i++) {
          for (int j=0;j<w;j++) {
              out.print(data[i][j]+"\t");
          }
          out.println();
      }
  }

  /**
   * Print out the 2D data to the file with the given name.
   * @param name the root name of the file to output.  The real output name
   *             will be name_#.rst.  See #getPrintStream for more details
   * @param data the data to print
   * @param w the width of the data
   * @param h the height of the data
   */
  static public void print(String name, int[][] data, int w, int h) {
    PrintStream out = getPrintStream(name, POSTFIX);
    if (out != null) {
      print(out, data, w, h);
      out.close();
    }
  }

  /**
   * Print out the 1D data to the given PrintStream out
   * @param out the output stream
   * @param data the data to print
   */
  static public void print(PrintStream out, double[] data) {
      int w = data.length;
      for (int j=0;j<w;j++) {
          out.print(data[j]+"\t");
      }
      out.println();
  }

  /**
   * Print out the 1D data to the file with the given name.
   * @param name the root name of the file to output.  The real output name
   *             will be name_#.rst.  See #getPrintStream for more details
   * @param data the data to print
   */
  static public void print(String name, double[] data) {
    PrintStream out = getPrintStream(name, POSTFIX);
    if (out != null) {
      print(out, data);
      out.close();
    }
  }

  /**
   * Print out the 2D data to the given PrintStream out
   * @param out the output stream
   * @param data the data to print
   * @param w the width of the data
   * @param h the height of the data
   */
  static public void print(PrintStream out, double[] data, int w, int h) {
//      for (int j=0;j<w;j++) out.print(j+"\t");
//      out.println();
      for (int i=0;i<h;i++) {
          for (int j=0;j<w;j++) {
              out.print(data[i*w+j]+"\t");
          }
          out.println();
      }
  }

  /**
   * Print out the 2D data to the file with the given name.
   * @param name the root name of the file to output.  The real output name
   *             will be name_#.rst.  See #getPrintStream for more details
   * @param data the data to print
   * @param w the width of the data
   * @param h the height of the data
   */
  static public void print(String name, double[] data, int w, int h) {
    PrintStream out = getPrintStream(name, POSTFIX);
    if (out != null) {
      print(out, data, w, h);
      out.close();
    }
  }

  /**
   * Print out the 2D data to the given PrintStream out
   * @param out the output stream
   * @param data the data to print
   * @param w the width of the data
   * @param h the height of the data
   */
  static public void print(PrintStream out, double[][] data, int w, int h) {
      print(out, data);
  }

  static public void print(PrintStream out, double[][] data) {
      if (data != null) {
        for (int i = 0; i < data.length; i++) {
          if (data[i] != null) {
            for (int j = 0; j < data[i].length; j++) {
              if (j!=0) out.print("\t");
              out.print(data[i][j]);
            }
            out.println();
          }
        }
      }
  }

  static public void print(String name, double[][] data) {
    PrintStream out = getPrintStream(name, POSTFIX);
    if (out != null) {
      print(out, data);
      out.close();
    }
  }

  static public void print(PrintStream out, int[][] data) {
      if (data != null) {
        for (int i = 0; i < data.length; i++) {
          if (data[i] != null) {
            for (int j = 0; j < data[i].length; j++) {
              if (j!=0) out.print("\t");
              out.print(data[i][j]);
            }
            out.println();
          }
        }
      }
  }

  static public void print(String name, int[][] data) {
    PrintStream out = getPrintStream(name, POSTFIX);
    if (out != null) {
      print(out, data);
      out.close();
    }
  }

  /**
   * Print out the 2D data to the file with the given name.
   * @param name the root name of the file to output.  The real output name
   *             will be name_#.rst.  See #getPrintStream for more details
   * @param data the data to print
   * @param w the width of the data
   * @param h the height of the data
   */
  static public void print(String name, double[][] data, int w, int h) {
    PrintStream out = getPrintStream(name, POSTFIX);
    if (out != null) {
      print(out, data, w, h);
      out.close();
    }
  }

  /**
   * Test print methods
   */
  public static class TestPrint {
    public static void main(String[] args) {
      int[] data = {100, -200, 5000, -6};
      OutputResult.print(System.out, data, 4, 1);
      OutputResult.print(System.err, data, 4, 1);
      OutputResult.print("test", data, 4, 1);
      OutputResult.print("test", data, 2, 2);
      OutputResult.print("test", data, 4, 1);
    }
  }

  static public void outputResult(String name, double[][] data)
  {
      int h=data.length;
      int tempNum = getNextFileNum();
      try {
          FileWriter out= new FileWriter("rst\\"+name+"_"+tempNum+".rst");
          for (int j=0;j<data[0].length;j++) out.write(j+"\t");
          out.write("\n");
          for (int i=0;i<h;i++) {
              for (int j=0;j<data[i].length;j++) {
                  out.write(data[i][j]+"\t");
              }
              out.write("\n");
          }
          out.close();
      }
      catch (IOException e) {
          System.err.println("io error: "+e);
      }

  }

  static public void outputResult(String name, char[][] data)
  {
      int h=data.length;
      int tempNum = getNextFileNum();
      try {
          FileWriter out= new FileWriter("rst\\"+name+"_"+tempNum+".rst");
          for (int j=0;j<data[0].length;j++) out.write(j+"\t");
          out.write("\n");
          for (int i=0;i<h;i++) {
              for (int j=0;j<data[i].length;j++) {
                  out.write((int)data[i][j]+"\t");
              }
              out.write("\n");
          }
          out.close();
      }
      catch (IOException e) {
          System.err.println("io error: "+e);
      }

  }

  /**
   * Output Result
   * @param data
   * @param w
   * @param h
   * @deprecated replaced by print(int[], int, int)
   */
  static public void outputResult(double[][] data, int w, int h)
  {
      int tempNum = getNextFileNum();
      try {
          FileWriter out= new FileWriter("rst\\"+"rst"+tempNum);
          for (int j=0;j<w;j++) out.write(j+"\t");
          out.write("\n");
          for (int i=0;i<h;i++) {
              for (int j=0;j<w;j++) {
                  out.write(data[i][j]+"\t");
              }
              out.write("\n");
          }
          out.close();
      }
      catch (IOException e) {
          System.err.println("io error: "+e);
      }

  }

  /**
   * Output Result
   * @param data
   * @param w
   * @param h
   * @deprecated replaced by print(String, double[], int, int)
   */
  static public void outputResult(String name, double[] data, int w, int h)
  {
      int tempNum = getNextFileNum();
      try {
          FileWriter out= new FileWriter("rst\\"+name+"_"+tempNum+".rst");
          for (int j=0;j<w;j++) out.write(j+"\t");
          out.write("\n");
          for (int i=0;i<h;i++) {
              for (int j=0;j<w;j++) {
                  out.write(data[i*w+j]+"\t");
              }
              out.write("\n");
          }
          out.close();
      }
      catch (IOException e) {
          System.err.println("io error: "+e);
      }
  }


  static public void outputResult(int[] data, int w, int h)
  {
      outputResult("rst", data, w, h);
  }

  /**
   * Output Result
   * @param data
   * @param w
   * @param h
   * @deprecated replaced by print(String, double[], int, int)
   */
  static public void outputResult(String name, int[] data, int w, int h)
  {
      int tempNum = getNextFileNum();
      try {
          FileWriter out= new FileWriter("rst\\"+name+"_"+tempNum+".rst");
          for (int j=0;j<w;j++) out.write(j+"\t");
          out.write("\n");
          for (int i=0;i<h;i++) {
              for (int j=0;j<w;j++) {
                  out.write(data[i*w+j]+"\t");
              }
              out.write("\n");
          }
          out.close();
      }
      catch (IOException e) {
          System.err.println("io error: "+e);
      }
  }

  /**
   * Output Result
   * @param data
   * @param w
   * @param h
   * @deprecated replaced by print(String, double[])
   */
  static public void outputResult(String name, double[] data)
  {
      int h=1;
      int w=data.length;
      outputResult(name,data,w,h);
  }


  /**
   * Output Result
   * @param data
   * @param w
   * @param h
   * @deprecated replaced by print(String, int[], int, int)
   */
  static public void outputResult(String name, int[][] data, int w, int h)
  {
      int tempNum = getNextFileNum();
      try {
          FileWriter out= new FileWriter("rst\\"+name+"_"+tempNum+".rst");
          for (int j=0;j<w;j++) out.write(j+"\t");
          out.write("\n");
          for (int i=0;i<h;i++) {
              for (int j=0;j<w;j++) {
                  out.write(data[i][j]+"\t");
              }
              out.write("\n");
          }
          out.close();
      }
      catch (IOException e) {
          System.err.println("io error: "+e);
      }
  }
  static public void outputResult(String name, int[][] data)
  {
      int tempNum = getNextFileNum();
      try {
          FileWriter out= new FileWriter("rst\\"+name+"_"+tempNum+".rst");
          for (int j=0;j<data[0].length;j++) out.write(j+"\t");
          out.write("\n");
          for (int i=0;i<data.length;i++) {
              for (int j=0;j<data[i].length;j++) {
                  out.write(data[i][j]+"\t");
              }
              out.write("\n");
          }
          out.close();
      }
      catch (IOException e) {
          System.err.println("io error: "+e);
      }
  }

  static public void outputResult(String name, int[] data)
  {
      int h=1;
      int w=data.length;
      outputResult(name,data,w,h);
  }

  static public void outputColorImage(int[] imageData, int width, int height) {
      int tempNum = getNextFileNum();
      String file="rst"+"_"+tempNum+".ppm";
      outputColorImage(file,imageData,width,height);
  }



  static public void outputColorImage(int[][] imageData, int width, int height) {
    int[] data = yi.util.data.Conversion.from2Dto1D(imageData, width, height);
    outputColorImage(data, width, height);
  }

  static public void outputColorImage(String file, int[][] imageData, int width, int height) {
    int[] data = yi.util.data.Conversion.from2Dto1D(imageData, width, height);
    outputColorImage(file, data, width, height);
  }

  static public void outputColorImage(String file, int[] imageData, int width, int height)
  {
    outputColorImage(file, imageData, width, height, false);
  }

  /**
   * Output ppm color format image with given name.  If suffix = true, the file
   * name will has a suffix of a counter number and ".ppm"; else the given name
   * will be used.
   *
   * @param file
   * @param imageData
   * @param width
   * @param height
   * @param suffix
   */
  static public void outputColorImage(String file, int[] imageData, int width, int height, boolean suffix)
  {
      if (suffix) {
        int tempNum = getNextFileNum();
        file = file + "_" + yi.util.data.Conversion.toString(tempNum, SUFFIX_LENGTH) + ".ppm";
      }
      if (VERBOSE) System.err.println("outputcolorimage:"+file);
      // make data
      byte[] data= new byte[height*width*3];
      for (int i=0;i<height*width;i++) {
              data[i*3]= (byte)(imageData[i]>>16&0xff);
              data[i*3+1]= (byte)(imageData[i]>>8&0xff);
              data[i*3+2]= (byte)(imageData[i]&0xff);
      }

      // write to file
      try {
              DataOutputStream out= new DataOutputStream (
                      new FileOutputStream (file));

              out.writeBytes("P6\n"+width+" "+height+"\n"+"255\n");
              out.write(data);
              out.flush();
              out.close();
      }
      catch (Exception e) {
              System.out.println("SaveResult:="+e);
      }

      data=null;
      System.gc();
  }

  static public void outputGrayImage(String file, int[] imageData, int width, int height)
  {
      // make data
      byte[] data= new byte[height*width];
      for (int i=0;i<height*width;i++) {
              data[i]= (byte)(imageData[i]&0xff);
      }

      // write to file
      try {
              DataOutputStream out= new DataOutputStream (
                      new FileOutputStream (file));
              out.writeBytes("P5\n"+width+" "+height+"\n"+"255\n");
              out.write(data);
              out.flush();
              out.close();
      }
      catch (Exception e) {
              System.out.println("SaveResult:="+e);
      }

      data=null;
      System.gc();
  }

  static public void outputGrayImage(int[] imageData, int width, int height)
  {
      String fileName=getFileName();
      outputGrayImage(fileName, imageData, width, height);
  }

  static String getFileName(){
      Frame f= new Frame();
      FileDialog fd= new FileDialog(f, "Save the result", FileDialog.SAVE);
      fd.show();
      return fd.getDirectory()+fd.getFile();
  }

  static public void outputImageRegions(String file, int[] pixels, int[] labels, int w, int h) {
    int region_num = yi.stat.StatUtil.maximum(labels) + 1;
    int label_len = Integer.toString(region_num).length();
    int[] img = new int[labels.length];
    for (int i=0; i<region_num; i++) {
      for (int j=0; j<img.length; j++) {
        img[j] = (labels[j] == i) ? pixels[j] : 0xff000000;
      }
      outputColorImage(file + ".l"
                       + yi.util.data.Conversion.toString(i, label_len)
                       + ".ppm", img, w, h, false) ;
    }
  }

  public static void print(PrintStream out, java.util.List list) {
    for (Iterator i=list.iterator(); i.hasNext();) {
      out.println(i.next());
    }
  }

  public static void print(PrintWriter out, Collection list) {
    for (Iterator i=list.iterator(); i.hasNext();) {
      out.println(i.next());
    }
  }

  /**
   * Print the matlab format matrix.  eg
   * [1 1 1; 2 2 2]
   * @param out
   * @param var_name variable name
   * @param col column number
   * @param data
   */
  public static void printMatlabMatrix(PrintWriter out, String var_name, int col, double[] data) {

    out.print(var_name + "(:," + col + ") = [");
    for (int i = 0; i < data.length; i++) {
      if (i!=0) out.print("; ");
      out.print(data[i]);
    }
    out.println("];");
  }

  public static void printMatlabMatrix(PrintWriter out, String var_name, int col, double[][] data) {

    for (int i = 0; i < data.length; i++) {
      out.print(var_name + "(" + (i+1) + ",:," + col + ") = [");
      for (int j = 0; j < data[i].length; j++) {
        if (j != 0)
          out.print(" ");
        out.print(data[i][j]);
      }
      out.println("];");
    }
  }

  public static String shortName(String className) {
    int index = className.lastIndexOf('.');
    return className.substring(index + 1);
  }

}