/**
 * Copyright (C) 2006-2007  NTT DATA CORPORATION
 * 
 * Version: 1.0.0 2007/04/01
 *  
 */
package net.cellcomputing.himawari.display;

import static net.cellcomputing.himawari.library.RiGlobal.QGetRenderContext;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

import net.cellcomputing.himawari.accessory.primitive.p_int;
import net.cellcomputing.himawari.library.types.CqColor;
import net.cellcomputing.himawari.library.types.PublicFunctions;
import net.cellcomputing.himawari.util.HimawariLogger;
/**
 * @author NTT DATA Corporation
 * HDR形式の命令を管理するクラス。
 * 画像イメージ、ファイル名を持つ。
 * 
 */
public strictfp class HDRRequest extends DisplayRequest {
	/**
	 * 扱う画像データの幅
	 */
	private int imageWidth;
	/**
	 * 扱う画像データの高さ
	 */
	private int imageHeight;
	/**
	 * コンストラクタ
	 * Request
	 * @param name ファイルの名前
	 * @param type 保存形式
	 * @param mode RGB,RGBAなどのカラー情報がはいる
	 * @param modeID modeのhash
	 * @param dataOffset 
	 * @param dataSize
	 * @param imageData このRequestが管理するImageData (BufferedImage)
	 */
	public HDRRequest(String name, final String type,final String mode,
			int modeID, int dataOffset, int dataSize ,ImageData imageData){
		super(name,type,mode,modeID,dataOffset,dataSize,imageData);
	}

	/**
	 * ファイルを保存するための情報を設定する
	 */
	public void openRequest() {
		imageWidth = QGetRenderContext() .optCurrent().GetIntegerOption( "System", "Resolution" ) [ 0 ];		//imageの幅を取得
		imageHeight = QGetRenderContext() .optCurrent().GetIntegerOption( "System", "Resolution" ) [ 1 ];	//imageの高さを取得
//		int xmin = (int)( PublicFunctions.CLAMP((float) Math.ceil( imageWidth * QGetRenderContext().optCurrent().GetFloatOption( "System", "CropWindow" ) [ 0 ] ), 0, imageWidth ) );
//		int xmax = (int)( PublicFunctions.CLAMP( (float)Math.ceil( imageWidth * QGetRenderContext().optCurrent().GetFloatOption( "System", "CropWindow" ) [ 1 ] ), 0, imageWidth ) );
//		int ymin = (int)( PublicFunctions.CLAMP( (float)Math.ceil( imageHeight * QGetRenderContext().optCurrent().GetFloatOption( "System", "CropWindow" ) [ 2 ] ), 0, imageHeight ) );
//		int ymax = (int)( PublicFunctions.CLAMP( (float)Math.ceil( imageHeight * QGetRenderContext().optCurrent().GetFloatOption( "System", "CropWindow" ) [ 3 ] ), 0, imageHeight ) );
		int xmin = (int)( PublicFunctions.CLAMP((float) Math.ceil( imageWidth * QGetRenderContext().optCurrent().GetFloatOptionIndex( "System", "CropWindow" , 0 ).value ), 0, imageWidth ) );
		int xmax = (int)( PublicFunctions.CLAMP( (float)Math.ceil( imageWidth * QGetRenderContext().optCurrent().GetFloatOptionIndex( "System", "CropWindow" , 1 ).value ), 0, imageWidth ) );
		int ymin = (int)( PublicFunctions.CLAMP( (float)Math.ceil( imageHeight * QGetRenderContext().optCurrent().GetFloatOptionIndex( "System", "CropWindow" , 2 ).value ), 0, imageHeight ) );
		int ymax = (int)( PublicFunctions.CLAMP( (float)Math.ceil( imageHeight * QGetRenderContext().optCurrent().GetFloatOptionIndex( "System", "CropWindow" , 3 ).value ), 0, imageHeight ) );

		imageWidth = xmax -xmin;
		imageHeight = ymax - ymin;

	}
	
	/**
	 * 何もしない
	 */
	public void display() {
		
	}
	
	/**
	 * 画像データからHDR形式のファイルに
	 * 書き出す。
	 */
	public void closeRequest() {
		RandomAccessFile file = null;
		CqColor[][] bucketList = (CqColor[][])imageData.getImage();
		
		try {
			file = new RandomAccessFile(name,"rw");
			file.write("#?RGBE\n".getBytes());
			file.write("FORMAT=32-bit_rle_rgbe\n\n".getBytes());
			file.write(("-Y "+ imageHeight +" +X "+ imageWidth + "\n").getBytes());
			
			byte[] rgbe = new byte[4];
			
			for(int y = 0; y < bucketList.length ; y++){
				for(int x = 0; x < bucketList[0].length;x++){
					//色情報が入っているかチェックする
					if(bucketList[y][x] == null)
						continue;
					
					//2006.11.28 データサイズをチェック
					switch( dataSize ){
					case 1:
						//１チャンネルの情報を３チャンネルに。
						float2rgbe(rgbe,bucketList[y][x].fRed(),bucketList[y][x].fRed(),bucketList[y][x].fRed());
						break;
					case 3:
					case 4:
					default:
						//色情報をRGBEに変換
						float2rgbe(rgbe,bucketList[y][x].fRed(),bucketList[y][x].fGreen(),bucketList[y][x].fBlue());
					}					

					//RGBEデータをファイルに書き出す
					file.write(rgbe);
				}
			}
			//全てのBucketを書き終えたのでファイルを閉じる。
			file.close();
			
		} catch (FileNotFoundException e) {
			HimawariLogger.outputException(e);
			HimawariLogger.getLogger().error( "Cannot open output image file: " + name + "\n");
			
		} catch (IOException e) {
			HimawariLogger.outputException(e);
			HimawariLogger.getLogger().error( "Cannot open output image file: " + name + "\n");
		}
		imageData = null;
	}
	
	
	/**
	 * 0〜1のfloatで表現されたRGB情報を
	 * RGBE形式のデータに変換する。
	 * 
	 * @param rgbe RGBE形式の情報
	 * @param red 赤の色情報(0〜1)
	 * @param green 緑の色情報(0〜1)
	 * @param blue 青の色情報(0〜1)
	 */
	public void float2rgbe(byte[] rgbe, float red, float green, float blue)
	{
		float v;
		p_int e = new p_int();

			
		v = red;
		if (green > v) v = green;
		if (blue > v) v = blue;
		if (v < 1e-32) {
			rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
		}
		else {
			v = (float)(frexp(v,e) * 256.0/v);
			rgbe[0] = (byte) (red * v);
			rgbe[1] = (byte) (green * v);
			rgbe[2] = (byte) (blue * v);
			rgbe[3] = (byte) (e.value + 128);
		}
	}
	
	/**
	 * C++のfrexpと同様の動きをする関数。
	 * 第1引数で与えられた浮動小数を指数部、仮数部に分解する。
	 * v = 2^e + return
	 * という数式が成り立つように変換する
	 * 
	 * @param v 分解対象となる浮動少数
	 * @param e 指数部
	 * @return 仮数部
	 */
	public  float frexp(float v,p_int e) {
		int i = 0;
		if (v != 0.0) {
			int sign = 1;
			if (v < 0) {
				sign = -1;
				v = -v;
			}
			// slow...
			while (v < 0.5) {
				v = v*2.0f;
				i = i-1;
			}
			while (v >= 1.0) {
				v = v*0.5f;
				i = i+1;
			}
			v = v*sign;
		}
		float ret  = v;
		e.value = i;
		return ret;
	}
	
}
