/**
 * 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 static net.cellcomputing.himawari.library.types.PublicFunctions.CLAMP;

import java.awt.image.WritableRaster;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;

import net.cellcomputing.himawari.accessory.STLVector;
import net.cellcomputing.himawari.library.IqBucket;
import net.cellcomputing.himawari.library.UserParameter;
import net.cellcomputing.himawari.library.types.PublicFunctions;

/**
 * @author NTT DATA Corporation
 * レンダリングされる画像データクラス。
 * T型のデータを持つ。
 * BufferedImageが主に利用される。
 * BufferedImageからは保存できない形式にする場合は
 * Vectorなどのデータ形式を用いてpixel情報を
 * 保つのべきである。
 * 
 * Singletonパターンで実装されており、
 * 利用者はgetInstanceを用いてインスタンス化する。
 * 2006/11/10
 * クラスだけではなくoffset値が異なれば
 * 違うインスタンスとして生成するように変更する
 * またHashMapのKeyはClass名+":"+offsetとする
 */
public abstract strictfp class ImageData <T> {
	protected String 			type;		//imageのType
	protected int				width;		//imageの幅
	protected int 				height;		//imageの高さ
	protected float[] 		pixelData;	//pixelのデータ
	protected WritableRaster	raster;		//Raster
	protected int				dataSize;	//pixelデータ幅
	protected T 				imageData;	//image用データ
	protected int 				offset;		//画像データoffset 2006/11/10 offset用追加
	
	
	//-------------------------------------
	protected STLVector<UserParameter>	m_customParams = new STLVector<UserParameter>(UserParameter.class,1);
	protected float	m_QuantizeZeroVal;
	protected float	m_QuantizeOneVal;
	protected float	m_QuantizeMinVal;
	protected float	m_QuantizeMaxVal;
	protected float	m_QuantizeDitherVal;
	//-------------------------------------
	
	
	/**
	 * バケットのデータを取得する
	 * @param bucket 描画データ
	 */
	public abstract void setBucket(IqBucket bucket);
	
	/**
	 * 保持している画像データを返す。
	 * 画像データの型は子クラスに依存する
	 * 
	 * 
	 * @return 画像データ
	 */
	public abstract T getImage();

	/**
	 * 初期化を行う
	 *
	 */
	public  void init(){
		width = QGetRenderContext() .optCurrent().GetIntegerOptionIndex( "System", "Resolution" , 0 ).value;		//imageの幅を取得
		height = QGetRenderContext() .optCurrent().GetIntegerOptionIndex( "System", "Resolution" , 1 ).value;	//imageの高さを取得
//		int xmin = (int)( PublicFunctions.CLAMP((float) Math.ceil( width * QGetRenderContext().optCurrent().GetFloatOption( "System", "CropWindow" ) [ 0 ] ), 0, width ) );
//		int xmax = (int)( PublicFunctions.CLAMP( (float)Math.ceil( width * QGetRenderContext().optCurrent().GetFloatOption( "System", "CropWindow" ) [ 1 ] ), 0, width ) );
//		int ymin = (int)( PublicFunctions.CLAMP( (float)Math.ceil( height * QGetRenderContext().optCurrent().GetFloatOption( "System", "CropWindow" ) [ 2 ] ), 0, height ) );
//		int ymax = (int)( PublicFunctions.CLAMP( (float)Math.ceil( height * QGetRenderContext().optCurrent().GetFloatOption( "System", "CropWindow" ) [ 3 ] ), 0, height ) );
		int xmin = (int)( PublicFunctions.CLAMP((float) Math.ceil( width * QGetRenderContext().optCurrent().GetFloatOptionIndex( "System", "CropWindow" , 0 ).value ), 0, width ) );
		int xmax = (int)( PublicFunctions.CLAMP( (float)Math.ceil( width * QGetRenderContext().optCurrent().GetFloatOptionIndex( "System", "CropWindow" , 1 ).value ), 0, width ) );
		int ymin = (int)( PublicFunctions.CLAMP( (float)Math.ceil( height * QGetRenderContext().optCurrent().GetFloatOptionIndex( "System", "CropWindow" , 2 ).value ), 0, height ) );
		int ymax = (int)( PublicFunctions.CLAMP( (float)Math.ceil( height * QGetRenderContext().optCurrent().GetFloatOptionIndex( "System", "CropWindow" , 3 ).value ), 0, height ) );

		width = xmax -xmin;
		height = ymax - ymin;
	}
	
	//--Singleton用フィールド・メソッド
	private static HashMap<String,ImageData> classMap 	= new HashMap<String,ImageData>();	//サブクラスのインスタンスを管理するHashMap
	private static Object	 _lock		= new Object();		//lock用Object
	
//	/**
//	 * コンストラクタ
//	 *
//	 */
//	protected ImageData(){
//		synchronized(_lock){
//			String classname =this.getClass().getName();
//			
//			//Hashmapからクラス名でインスタンスが生成されているか検索する
//			if(classMap.get(classname) != null){
//				throw new RuntimeException("Already created: " + classname);
//			}
//			
//			classMap.put(classname,this);
//		}
//	}

	//2006/11/10 offset付きSingleton用変更
	/**
	 * コンストラクタ
	 *
	 */
	protected ImageData(int offset){
		synchronized(_lock){
			String classname =this.getClass().getName();
			classname = classname + ":" + offset;
			//Hashmapからクラス名でインスタンスが生成されているか検索する
			if(classMap.get(classname) != null){
				throw new RuntimeException("Already created: " + classname);
			}
			this.offset = offset;
			classMap.put(classname,this);
		}
	}
	//--
	/**
	 * ImageDataを取得するメソッド。
	 * <<Singleton>>
	 * @param cls 取得したいClass
	 * @return 指定されたImageData
	 */
	public static  ImageData getInstance(Class cls,int offset){
		synchronized (_lock) {
			String classname = cls.getName();
			//2006/11/10追加 offset付きでインスタンスを判別する
			classname = classname + ":" + offset;
			//--
			ImageData obj = (ImageData)classMap.get(classname);
			if (obj == null) {
				try {
//					Class cls = Class.forName(classname);
//					obj = (ImageData)cls.newInstance(); //2006/11/10 offset用変更
					Constructor constr = cls.getDeclaredConstructor(int.class);
					constr.setAccessible(true);
					obj = (ImageData)constr.newInstance(offset);
					
				} catch (IllegalAccessException e) {
					throw new RuntimeException(classname + " cannot be accessed.");
				} catch (InstantiationException e) {
					throw new RuntimeException(classname + " cannot be instantiated.");
				}//--2006/11/10 offset用変更
				catch (NoSuchMethodException e){
					throw new RuntimeException(classname + " cannot be instantiated.");					
				} catch (InvocationTargetException e){
					throw new RuntimeException(classname + " cannot be instantiated.");
				}//--
			}
			return obj;
		}
		
	}
	
	/**
	 * 指定された値を量子化する
	 * @param value	量子化したい値
	 * @return	量子化された値
	 */
	protected float quantize( float value ) {
		value = Math.round(m_QuantizeZeroVal + value * (m_QuantizeOneVal - m_QuantizeZeroVal) + m_QuantizeDitherVal );
		value = CLAMP(value, m_QuantizeMinVal, m_QuantizeMaxVal) ;
		return value;
	}
	
	
}
