/*
 * YUI Compressor
 * Author: Julien Lecomte <jlecomte@yahoo-inc.com>
 * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
 * Code licensed under the BSD License:
 *	   http://developer.yahoo.net/yui/license.txt
 */

package org.opengion.tools;

import com.yahoo.platform.yui.compressor.JavaScriptCompressor;
import com.yahoo.platform.yui.compressor.CssCompressor;
import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.EvaluatorException;

import java.io.File;
import java.io.InputStreamReader;
import java.io.FileInputStream;
import java.io.OutputStreamWriter;
import java.io.FileOutputStream;
import java.io.FileFilter;
import java.io.Reader;
import java.io.Writer;
import java.io.IOException;

/**
 * OGCompressor は、com.yahoo.platform.yui.compressor.YUICompressor を参考に、
 * フォルダを一括処理するための、mainクラスです。
 * js ファイルと css ファイルを圧縮処理します。
 * なお、オリジナルのファイルと圧縮後のファイルのタイムスタンプが同じ場合は、
 * 処理をスキップします。
 * また、圧縮変換後は、オリジナルのタイムスタンプで圧縮ファイルのタイムスタンプを
 * 書き換えます。
 *
 * 注意：このクラスは、同期処理されていません。
 *
 * @og.rev 5.2.1.0 (2010/10/01) 新規追加
 *
 * @version  5.2
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class OGCompressor {

	/**
	 * 起動するメインメソッドです。
	 *
	 *	&lt;java classname="org.opengion.tools.OGCompressor" classpath="${env.DEV_PATH}/yuicompressor-2.4.2og.jar" fork="yes"&gt;
	 *		&lt;arg value="-dir" /&gt;
	 *		&lt;arg value="${env.DEV_PATH}/src/script" /&gt;
	 *		&lt;arg value="${env.DEV_PATH}/jsp" /&gt;
	 *	&lt;/java&gt;
	 *
	 * @param args[] String 引数配列
	 */
	public static void main( final String args[] ) {

		// 特別版は、args をそのまま使用します。(-dir inDir outDir)
		if( args.length == 3 && "-dir".equals( args[0] ) ) {
			copyFile( new File(args[1]) , new File(args[2]) );
		}
	}

	/**
	 * 呼び出されるコピー処理メソッドです。
	 *
	 * このメソッドは、再起呼び出しでディレクトリを下っていきます。
	 * 処理対象は、.js と .css ファイルのみです。
	 * ディレクトリ内のファイルに対しコピー処理を行います。
	 * 変換元と変換先のタイムスタンプが異なる場合のみ変換し、
	 * 変換後のファイルは、変換前のタイムスタンプに変更します。
	 *
	 * @param inDir  File 入力フォルダ
	 * @param outDir File 出力フォルダ
	 */
	private static void copyFile( final File inDir , final File outDir ) {

		File[] files = inDir.listFiles(
			// 処理対象は、.js と .css ファイルのみです。
			new FileFilter() {
				public boolean accept( final File pathname ) {
					if( pathname.isDirectory() ) { return true; }
					String name = pathname.getName().toLowerCase();
					return name.endsWith(".js") || name.endsWith(".css") ;
				}
			}
		);

		// 出力先フォルダを生成
		if( !outDir.exists() && !outDir.mkdirs() ) {
			System.err.println("\n[ERROR] " + outDir + "フォルダが作成できませんでした。" );
			return ;
		}

		// ディレクトリ内のファイルに対しコピー処理を行う
		for( int i = 0; i<files.length; i++ ){
			File outFile = new File( outDir , files[i].getName() );
			if( files[i].isDirectory() ) {
				copyFile( files[i] , outFile );
			}
			else {
				// 変換元と変換先のタイムスタンプが異なる場合のみ変換します。
				long srcTime = files[i].lastModified();
				if( srcTime != outFile.lastModified() ) {
					exec( files[i] , outFile );
					outFile.setLastModified( srcTime );		// 変換後のファイルは、変換前のタイムスタンプに変更
				}
			}
		}
	}

	/**
	 * 圧縮処理を行うメソッドです。
	 *
	 * このメソッドは、実際の圧縮処理を行うメソッドです。
	 *
	 * @param inDir  File 入力フォルダ
	 * @param outDir File 出力フォルダ
	 */
	private static void exec( final File inFile , final File outFile ) {
		System.err.println( "IN:" + inFile );	// 元のクラスで、err を out に変換しているので、同期を取るため、err とする。

		Reader in = null;
		Writer out = null;

		try {
			String charset = "UTF-8" ;

			int linebreakpos = -1;

			in = new InputStreamReader(new FileInputStream(inFile), charset);

			String fname = inFile.getName().toLowerCase();
			if ( fname.endsWith(".js") ) {
				try {
					JavaScriptCompressor compressor = new JavaScriptCompressor(in, new ErrorReporter() {
						public void warning(String message, String sourceName,
								int line, String lineSource, int lineOffset) {
							if (line < 0) {
								System.err.println("\n[WARNING] " + inFile + ": " + message);
							} else {
								System.err.println("\n[WARNING] " + inFile + "(" + line + ':' + lineOffset + "): " + message);
							}
							System.err.println("\t" + lineSource);
						}

						public void error(String message, String sourceName,
								int line, String lineSource, int lineOffset) {
							if (line < 0) {
								System.err.println("\n[ERROR] " + inFile + ": " + message);
							} else {
								System.err.println("\n[ERROR] " + inFile + "(" + line + ':' + lineOffset + "): " + message);
							}
							System.err.println("\t" + lineSource);
						}

						public EvaluatorException runtimeError(String message, String sourceName,
								int line, String lineSource, int lineOffset) {
							error(message, sourceName, line, lineSource, lineOffset);
							return new EvaluatorException(message);
						}
					});

					// Close the input stream first, and then open the output stream,
					// in case the output file should override the input file.
					in.close(); in = null;

					out = new OutputStreamWriter(new FileOutputStream(outFile), charset);

					boolean verbose = false;
					boolean munge = true;
					boolean preserveAllSemiColons = false;
					boolean disableOptimizations = false;

					compressor.compress(out, linebreakpos, munge, verbose,
							preserveAllSemiColons, disableOptimizations);

				} catch (EvaluatorException e) {
					e.printStackTrace();
					// Return a special error code used specifically by the web front-end.
		//			System.exit(2);
				}
			} else if( fname.endsWith(".css") ) {

				CssCompressor compressor = new CssCompressor(in);

				// Close the input stream first, and then open the output stream,
				// in case the output file should override the input file.
				in.close(); in = null;

				out = new OutputStreamWriter(new FileOutputStream(outFile), charset);
				compressor.compress(out, linebreakpos);
			}
		}
		catch (IOException e) {
			e.printStackTrace();
	//		System.exit(1);
		}
		finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
