/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.fukurou.util;

import org.opengion.fukurou.system.OgRuntimeException ;		// 6.4.2.0 (2016/01/29)
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;

import org.opengion.fukurou.system.LogWriter;						// 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system

/**
 * ImageResizer は、画像ファイルのリサイズを行うためのクラスです。
 * ここでの使い方は、初期化時に、オリジナルの画像ファイルを指定し、
 * 変換時に各縮小方法に対応したメソッドを呼び出し、画像を変換します。
 * 変換方法としては、以下の3つがあります。
 * ①最大サイズ(px)指定による変換
 *   縦横の最大サイズ(px)を指定し、変換を行います。
 *   横長の画像については、変換後の横幅=最大サイズとなり、縦幅については、横幅の
 *   縮小率に従って決定されます。
 *   逆に縦長の画像については、変換後の縦幅=最大サイズとなり、横幅については、縦幅の
 *   縮小率に従って決定されます。
 * ②縦横サイズ(px)指定による変換
 *   縦横の変換後のサイズ(px)を個別に指定し、変換を行います。
 * ③縮小率指定による変換
 *   "1"を元サイズとする縮小率を指定し、変換を行います。
 *   縮小率は、縦横で同じ縮小率が適用されます。
 * 入力フォーマットとしてはJPEG/PNG/GIFに、出力フォーマットとしてはJPEG/PNGに対応しています。
 * 出力フォーマットについては、出力ファイル名の拡張子より自動的に決定されますが、一般的には
 * サイズが小さくなるjpegファイルを推奨します。
 * 入出力フォーマットについて、対応していないフォーマットが指定された場合は例外が発生します。
 * また、縦横の出力サイズが入力サイズの縦横よりも両方大きい場合、変換は行われず、入力ファイルが
 * そのままコピーされて出力されます。(拡大変換は行われません)
 *
 * @og.rev 6.0.2.3 (2014/10/10) static 系のメソッドは、ImageUtil に移動
 *
 * @version  4.0
 * @author   Hiroki Nakamura
 * @since    JDK5.0,
 */
public class ImageResizer {

	private final BufferedImage inputImage; // 入力画像オブジェクト

	private final int inSizeX;				// 入力画像の横サイズ
	private final int inSizeY;				// 入力画像の縦サイズ

	/**
	 * 入力ファイル名を指定し、画像縮小オブジェクトを初期化します。
	 *
	 * @og.rev 5.4.3.5 (2012/01/17) CMYK対応
	 * @og.rev 5.4.3.7 (2012/01/20) FAIでのファイル取得方法変更
	 * @og.rev 5.4.3.8 (2012/01/24) エラーメッセージ追加
	 * @og.rev 5.6.5.3 (2013/06/28) 入力画像の形式 を ImageIO から取り出します。
	 * @og.rev 5.6.5.3 (2013/06/28) 入力画像の形式 を ImageIO から取り出します。
	 * @og.rev 6.0.2.3 (2014/10/10) ImageUtil から取り出します。
	 *
	 * @param fin 入力ファイル名
	 */
	public ImageResizer( final String fin ) {
		inputImage = ImageUtil.readFile( fin );

		inSizeX = inputImage.getWidth();
		inSizeY = inputImage.getHeight();
	}

	/**
	 * 縦横の最大サイズ(px)を指定し、変換を行います。
	 * 横長の画像については、変換後の横幅=最大サイズとなり、縦幅については、横幅の
	 * 縮小率に従って決定されます。
	 * 逆に縦長の画像については、変換後の縦幅=最大サイズとなり、横幅については、縦幅の
	 * 縮小率に従って決定されます。
	 *
	 * @param fname 出力ファイル名
	 * @param maxSize 変換後の縦横の最大サイズ
	 */
	public void resizeByPixel( final String fname, final int maxSize ) {
		int sizeX = 0;
		int sizeY = 0;
		if( inSizeX > inSizeY ) {
			sizeX = maxSize;
			sizeY = inSizeY * maxSize / inSizeX;
		}
		else {
			sizeX = inSizeX * maxSize / inSizeY;
			sizeY = maxSize;
		}
		convert( inputImage, fname, sizeX, sizeY );
	}

	/**
	 * 縦横の変換後のサイズ(px)を個別に指定し、変換を行います。
	 *
	 * @param fname 出力ファイル名
	 * @param sizeX 変換後の横サイズ(px)
	 * @param sizeY 変換後の縦サイズ(px)
	 */
	public void resizeByPixel( final String fname, final int sizeX, final int sizeY ) {
		convert( inputImage, fname, sizeX, sizeY );
	}

	/**
	 * "1"を元サイズとする縮小率を指定し、変換を行います。
	 *  縮小率は、縦横で同じ縮小率が適用されます。
	 *
	 * @param fname 出力ファイル名
	 * @param ratio 縮小率
	 */
	public void resizeByRatio( final String fname, final double ratio ) {
		final int sizeX = (int)( inSizeX * ratio );
		final int sizeY = (int)( inSizeY * ratio );
		convert( inputImage, fname, sizeX, sizeY );
	}

	/**
	 * 画像の変換を行うための内部共通メソッドです。
	 *
	 * @og.rev 5.4.1.0 (2011/11/01) 画像によってgetTypeが0を返し、エラーになる不具合を修正
	 * @og.rev 5.6.5.3 (2013/06/28) 出力画像の形式 を ImageIO から取り出します。
	 * @og.rev 5.6.5.3 (2013/06/28) 5.6.6.1 (2013/07/12) getSuffix するタイミングを後ろにする。
	 * @og.rev 5.6.6.1 (2013/07/12) 拡張子の変更があるので、変換しない処理は、ない。
	 * @og.rev 6.0.2.3 (2014/10/10) ImageUtil のメソッドを一部使用するように変更します。
	 *
	 * @param inputImage 入力画像オブジェクト
	 * @param fname 出力ファイル名
	 * @param sizeX 横サイズ(px)
	 * @param sizeY 縦サイズ(px)
	 */
	private void convert( final BufferedImage inputImage, final String fname, final int sizeX, final int sizeY ) {
		// 5.6.6.1 (2013/07/12) getSuffix するタイミングを後ろにする。
		// 5.6.5.3 (2013/06/28) 出力画像の形式 を ImageIO から取り出します。
		// 6.0.2.3 (2014/10/10) ImageUtil のメソッド
		if( !ImageUtil.isWriterSuffix( fname ) ) {
			final String errMsg = "出力ファイルは" + ImageUtil.WRITER_SUFFIXES + "のいずれかの形式のみ指定可能です。" + "File=[" + fname + "]";
			throw new OgRuntimeException( errMsg );
		}

		// 5.4.1.0 (2011/11/01) 画像によってgetTypeが0を返し、エラーになる不具合を修正
		final int type = inputImage.getType();
		BufferedImage resizeImage = null;
		if( type == 0 ) {
			resizeImage = new BufferedImage( sizeX, sizeY, BufferedImage.TYPE_4BYTE_ABGR_PRE );
		}
		else {
			resizeImage = new BufferedImage( sizeX, sizeY, inputImage.getType() );
		}
		AffineTransformOp ato = null;
		ato = new AffineTransformOp(
				AffineTransform.getScaleInstance(
						(double)sizeX/inSizeX, (double)sizeY/inSizeY ), null );
		ato.filter( inputImage, resizeImage );

		ImageUtil.saveFile( resizeImage,fname );		// 6.0.2.3 (2014/10/10) ImageUtil のメソッド
	}

	/**
	 * メイン処理です。
	 * Usage: java org.opengion.fukurou.util.ImageResizer [Input Filename] [OutputFilename] [-max=maxResize] [-ratio=ratio] [-x=sizeX] [-y=sizeY]
	 *
	 * @og.rev 6.4.5.1 (2016/04/28) mainメソッドの起動方法を変更します。
	 *
	 *  [-max=MaxResize] :最大サイズを指定して、リサイズします。
	 *  [-ratio=ratio]   :縮小率を指定して、リサイズします。
	 *  [-x=sizeX] [-y=sizeY] : X,Y サイズを指定して、リサイズします。
	 *
	 * @param  args  引数文字列配列 入力ファイル、出力ファイル、縦横最大サイズ
	 */
	public static void main( final String[] args ) {
		if( args.length < 3 ) {
			LogWriter.log( "Usage: java org.opengion.fukurou.util.ImageResizer [Input Filename] [OutputFilename] [-max=maxResize] [-ratio=ratio] [-x=sizeX] [-y=sizeY]" );
			return ;
		}

		final String inFile  = args[0];
		final String outFile = args[1];

		int		maxResize	= -1;
		double	ratio		= -1d;
		int		sizeX		= -1;
		int		sizeY		= -1;

		for( int i=2; i<args.length; i++ ) {
			if( args[i].startsWith( "-max="   ) ) { maxResize	= Integer.parseInt(		args[i].substring( 5 ) ); }		// 5 = "-max=".length()
			if( args[i].startsWith( "-ratio=" ) ) { ratio		= Double.parseDouble(	args[i].substring( 7 ) ); }		// 7 = "-ratio=".length()
			if( args[i].startsWith( "-x="     ) ) { sizeX		= Integer.parseInt(		args[i].substring( 3 ) ); }		// 3 = "-x=".length()
			if( args[i].startsWith( "-y="     ) ) { sizeY		= Integer.parseInt(		args[i].substring( 3 ) ); }		// 3 = "-y=".length()
		}

		final ImageResizer ir = new ImageResizer( inFile );

		if( maxResize > 0 ) {
			ir.resizeByPixel( outFile, maxResize );
		}
		if( ratio > 0d ) {
			ir.resizeByRatio( outFile, ratio );
		}
		if( sizeX > 0 && sizeY > 0 ) {
			ir.resizeByPixel( outFile, sizeX , sizeY );
		}
	}
}
