/*
* 01/07/2003 - 15:19:32
*
* ConvertEncoding.java -
* Copyright (C) 2004 karneim.com Gesellschaft fuer Softwarearchitektur mbH
* http://www.karneim.com
* Author: Michael Karneim
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

import java.io.*;
import java.nio.charset.*;
import java.util.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;


public class ConvertEncoding {
  /**
   * Usage: java -jar Util.jar list
   *        java -jar Util.jar test -n <num_of_bytes> [-o <offset>] [-tenc <to_encoding>] [-ffile <from_file>] [-tfile <to_file>|-gui <yes|no>]
   *        java -jar Util.jar convert [-fenc <from_encoding>] [-tenc <to_encoding>] [-ffile <from_file>] [-tfile <to_file>]
   *        java -jar Util.jar convertfiles [-fenc <from_encoding>] [-tenc <to_encoding>] [-tdir <to_file>] files..
   * @param args String[]
   */
  public static void main(String[] args) {
    if ( args.length == 0) {
      printUsage();
      System.exit( 0);
    }
    String cmd = args[0];
    Properties options = new Properties();
    LinkedList fileList = new LinkedList();
    for( int i=1; i<args.length;) {
      String name = args[i];
      if ( name.startsWith( "-") == false) {
        fileList.add( name);
        i++;
      } else {
        String value = args[i + 1];
        options.setProperty( name, value );
        i=i+2;
      }
    }


    if ( "list".equals( cmd)) {
      printEncodings();
    } else if ( "convert".equals( cmd) ) {

      InputStream in = null;
      {
        String fromFile = options.getProperty( "-ffile");
        if ( fromFile != null) {
          try {
            in = new FileInputStream(fromFile);
          } catch (FileNotFoundException ex) {
            System.err.println("File '"+fromFile+"' not found.");
            System.exit( -1);
          }
        } else {
          in = System.in;
        }
      }

      OutputStream out = null;
      {
        String toFile = options.getProperty( "-tfile");
        if ( toFile != null) {
          try {
            File f = new File( toFile);
            out = new FileOutputStream( f);
          } catch (FileNotFoundException ex) {
            throw new Error( "huh? "+ex);
          }
        } else {
          out = System.out;
        }
      }

      String fromEncoding = options.getProperty( "-fenc");
      String toEncoding = options.getProperty( "-tenc");

      try {
        convertStream( in, fromEncoding, toEncoding, out );
      } catch ( UnsupportedEncodingException ex1 ) {
        System.err.println("Unsupported encoding '"+ex1.getMessage()+"'.");
        System.exit( -1);
      } catch ( IOException ex2) {
        System.err.println( "Error while reading/writing: ex=" + ex2 );
        System.exit( -1);
      }
    } else if ( "convertfiles".equals( cmd) ) {

      String toDir = options.getProperty( "-tdir", ".");
      String fromEncoding = options.getProperty( "-fenc");
      String toEncoding = options.getProperty( "-tenc");
      String[] files = (String[])fileList.toArray( new String[0]);

      try {
        convertFiles( files, fromEncoding, toEncoding, toDir );
      } catch ( UnsupportedEncodingException ex1 ) {
        System.err.println("Unsupported encoding '"+ex1.getMessage()+"'.");
        System.exit( -1);
      } catch ( IOException ex2) {
        System.err.println( "Error while reading/writing: ex=" + ex2 );
        System.exit( -1);
      }
    } else if ( "test".equals( cmd)) {
      InputStream in = null;
      {
        String fromFile = options.getProperty( "-ffile");
        if ( fromFile != null) {
          try {
            in = new FileInputStream(fromFile);
          } catch (FileNotFoundException ex) {
            System.err.println("File '"+fromFile+"' not found.");
            System.exit( -1);
          }
        } else {
          in = System.in;
        }
      }

      String toEncoding = options.getProperty( "-tenc");
      int numOfBytes = Integer.parseInt( options.getProperty( "-n", "-1"));
      int offset = Integer.parseInt( options.getProperty( "-o", "0"));
      boolean gui = options.getProperty( "-gui","no").equals( "yes");
      OutputStream out = null;
      if ( gui ) {
        out = new ByteArrayOutputStream( numOfBytes);
      } else {
        String toFile = options.getProperty( "-tfile");
        if ( toFile != null) {
          try {
            File f = new File( toFile);
            out = new FileOutputStream( f);
          } catch (FileNotFoundException ex) {
            throw new Error( "huh? "+ex);
          }
        } else {
          out = System.out;
        }
      }

      try {
        test( in, toEncoding, out, numOfBytes, offset);
        if ( out instanceof ByteArrayOutputStream) {
          String content = null;
          if ( toEncoding != null) {
            content = new String( ( ( ByteArrayOutputStream ) out ).toByteArray(), toEncoding );
          } else {
            content = new String( ( ( ByteArrayOutputStream ) out ).toByteArray() );
          }
          JFrame f = new JFrame("Output");
          JTextArea ta = new JTextArea();
          JScrollPane p = new JScrollPane( ta);
          f.getContentPane().add( p, BorderLayout.CENTER);
          f.setSize( 600, 500);
          f.setLocation( 100,100);
          ta.setFont( new Font("Monospaced", Font.PLAIN, 12));
          ta.setText( content);

          f.addWindowListener( new java.awt.event.WindowAdapter() {
            public void windowClosing(WindowEvent e) {
              System.exit( 0);
            }
          } );
          f.show();
        }
      } catch ( UnsupportedEncodingException ex) {
        throw new Error( ex);
      } catch ( IOException ex) {
        System.err.println( "Error while reading/writing: ex=" + ex );
        ex.printStackTrace();
        System.exit( -1);
      }
    } else {
      printUsage();
    }

  }


  public static void printUsage() {
    System.out.println("Usage: java -jar Util.jar "+ConvertEncoding.class.getName()+" list");
    System.out.println("       - lists all available encodings");
    System.out.println("       java -jar Util.jar "+ConvertEncoding.class.getName()+" test -n <num_of_bytes> [-o <offset>] [-tenc <to_encoding>] [-ffile <from_file>] [-tfile <to_file>|-gui <yes|no>] ");
    System.out.println("       - converts the num_of_bytes bytes starting from offset of the from_file into to_file, using every known encoding for read and to_encoding for write, into separate lines");
    System.out.println("       java -jar Util.jar "+ConvertEncoding.class.getName()+" convert [-fenc <from_encoding>] [-tenc <to_encoding>] [-ffile <from_file>] [-tfile <to_file>]");
    System.out.println("       - converts the content of the from_file into to_file, using the encoding from_encoding for read and to_encoding for write");
    System.out.println("       java -jar Util.jar "+ConvertEncoding.class.getName()+" convertfiles [-fenc <from_encoding>] [-tenc <to_encoding>] [-tdir <to_file>] files..");
    System.out.println("       - converts the specified files, using the encoding from_encoding for read and to_encoding for write");

  }

  public static void convertFiles( String[] files,  String fromEncoding, String toEncoding, String toDir) throws UnsupportedEncodingException, IOException {
    File toDirFile = new File( toDir);
    if ( toDirFile.exists() == false) {
      throw new IOException("toDir '"+toDir+"' does not exist");
    }

    File currDir = new File(".");
    if ( currDir.equals( toDirFile) ) {
      throw new IOException("Can not overwrite files in directory '"+toDir+"', since I am reading from there. Specify a different to_dir directory.");
    }

    for( int i=0; i<files.length; ++i) {
      InputStream in = null;
      File inFile = null;
      {
        String fromFile = files[i];
        try {
          inFile = new File( fromFile);
          in = new FileInputStream( inFile );
        } catch ( FileNotFoundException ex ) {
          System.err.println( "File '" + fromFile + "' not found." );
          System.exit( -1 );
        }
      }

      OutputStream out = null;
      {
        String toFile = inFile.getName();
        if ( toFile != null) {
          try {
            File f = new File( toDirFile, toFile);
            out = new FileOutputStream( f);
          } catch (FileNotFoundException ex) {
            throw new Error( "huh? "+ex);
          }
        } else {
          out = System.out;
        }
      }
      System.out.println("[INFO] Converting "+files[i]+".");
      convertStream( in, fromEncoding, toEncoding, out);
    }
    System.out.println("Finished.");
  }

  public static void convertStream( InputStream in, String fromEncoding, String toEncoding, OutputStream out) throws UnsupportedEncodingException, IOException {
    InputStreamReader r = null;
    try {
      if ( fromEncoding != null) {
        r = new InputStreamReader( in, fromEncoding );
      } else {
        r = new InputStreamReader( in );
        System.err.println("from_encoding undefined. Using default encoding '"+r.getEncoding()+"'");
      }
    } catch ( UnsupportedEncodingException ex ) {
      throw new UnsupportedEncodingException( fromEncoding);
    }

    OutputStreamWriter w = null;
    try {
      if ( toEncoding != null) {
        w = new OutputStreamWriter( out, toEncoding );
      } else {
        w = new OutputStreamWriter( out);
        System.err.println("to_encoding undefined. Using default encoding '"+w.getEncoding()+"'");
      }

    } catch ( UnsupportedEncodingException ex1 ) {
      throw new UnsupportedEncodingException( toEncoding);
    }
    char[] buf = new char[1024*4];
    int read = r.read( buf);
    while( read > 0) {
      w.write( buf, 0, read);
      read = r.read( buf);
    }
    w.close();
    r.close();
  }

  public static void test( InputStream in, String toEncoding, OutputStream out, int numOfBytes, int offset) throws UnsupportedEncodingException, IOException {
    in.skip( offset);
    byte[] content = new byte[ numOfBytes];
    in.read( content);
    in.close();

    InputStreamReader r = null;
    OutputStreamWriter w = new OutputStreamWriter( out);

    String[] encodings = getEncodings();
    int maxLenght = 0;
    for( int i = 0; i< encodings.length; ++i) {
      if ( encodings[i].length() > maxLenght ) {
        maxLenght = encodings[i].length();
      }
    }

    for( int i = 0; i< encodings.length; ++i) {
      String fromEncoding = encodings[i];
//      System.err.println("[DEBUG] fromEncoding="+fromEncoding);
      try {
        ByteArrayInputStream bin = new ByteArrayInputStream( content);
        r = new InputStreamReader( bin, fromEncoding );
      } catch ( UnsupportedEncodingException ex ) {
        throw new UnsupportedEncodingException( fromEncoding );
      }
      w.write( "\n".toCharArray());
      w.write( fromEncoding.toCharArray());
      for( int s=0; s<maxLenght - fromEncoding.length(); ++s) {
        w.write( " ".toCharArray());
      }
      w.write( "|".toCharArray());

      char[] buf = new char[ 1024*4];
      int read = r.read( buf );

      while ( read > 0 ) {
        w.write( buf, 0, read );
        read = r.read( buf );
      }
      r.close();
    }

    w.write( "\n".toCharArray());
    w.close();
  }


  public static String[] getEncodings() {
    Map availcs = Charset.availableCharsets();
    String[] result = new String[ availcs.size()];
    Set keys = availcs.keySet();
    Iterator iter = keys.iterator();
    for (int i=0; iter.hasNext(); ++i) {
      result[i] = (String)iter.next();
    }
    return result;
  }

  public static void printEncodings() {
    Map availcs = Charset.availableCharsets();
    Set keys = availcs.keySet();
    for (Iterator iter =
         keys.iterator(); iter.hasNext(); ) {
      System.out.println(iter.next());
    }

  }
}
