/* 
 * PROJECT: NyARToolkitCS(Extension)
 * --------------------------------------------------------------------------------
 * The NyARToolkitCS is C# edition ARToolKit class library.
 * Copyright (C)2008-2009 Ryo Iizuka
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * For further information please contact.
 *	http://nyatla.jp/nyatoolkit/
 *	<airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp>
 * 
 */
using System;
using System.Collections.Generic;
using System.Text;
using jp.nyatla.nyartoolkit.cs.core;
using jp.nyatla.nyartoolkit.cs.nyidmarker;
using jp.nyatla.nyartoolkit.cs.rpf.reality.nyartk;
using jp.nyatla.nyartoolkit.cs.rpf.realitysource.nyartk;
using jp.nyatla.nyartoolkit.cs.rpf.tracker.nyartk.status;


namespace jp.nyatla.nyartoolkit.cs.rpf.mklib
{
    /**
     * 簡易な同期型NyIdマーカIDテーブルです。
     * このクラスは、RawBitフォーマットドメインのNyIdマーカのIdとメタデータセットテーブルを定義します。
     * SerialIDは、RawBitマーカのデータパケットを、[0][1]...[n]の順に並べて、64bitの整数値に変換した値です。
     * 判別できるIdマーカは、domain=0(rawbit),model&lt;5,mask=0のもののみです。
     * <p>
     * このクラスは、NyRealityTargetをRawBitフォーマットドメインのSerialNumberマーカにエンコードする
     * 機能を提供します。
     * 使い方は、ユーザは、このクラスにIDマーカのSerialNumberとそのサイズを登録します。その後に、
     * NyRealityTargetをキーに、登録したデータからそのSerialNumberをサイズを得ることができます。
     * </p>
     * 
     * NyIdRawBitSerialNumberTable
     */
    public class RawbitSerialIdTable
    {
	    /**
	     * selectTarget関数の戻り値を格納します。
	     * 入れ子クラスの作れない処理系では、RawbitSerialIdTable_IdentifyIdResultとして宣言してください。
	     */
	    public class IdentifyIdResult
	    {
		    /** ID番号です。*/
		    public long id;
		    /** 名前です。*/
		    public String name;
		    /** 登録時に設定したマーカサイズです。*/
		    public double marker_width;
		    /** ARToolKit準拠の、マーカの方位値です。*/
		    public int artk_direction;
	    }
    	

	    private class SerialTable : NyARObjectStack<SerialTable.SerialTableRow>
	    {
		    public class SerialTableRow
		    {
			    public long id_st;
			    public long id_ed;
			    public double marker_width;
			    public String name;
			    public void setValue(String i_name,long i_st,long i_ed,double i_width)
			    {
				    this.id_ed=i_ed;
				    this.id_st=i_st;
				    this.marker_width=i_width;
				    this.name=i_name;
			    }
		    }		
		    public SerialTable(int i_length)
		    {
			    base.initInstance(i_length);
		    }
		    protected override SerialTableRow createElement()
		    {
			    return new SerialTableRow();
		    }
		    public SerialTableRow getItembySerialId(long i_serial)
		    {
			    for(int i=this._length-1;i>=0;i--)
			    {
				    SerialTableRow s=this._items[i];
				    if(i_serial<s.id_st || i_serial>s.id_ed){
					    continue;
				    }
				    return s;
			    }
			    return null;
		    }
	    }

	    private SerialTable _table;
	    private NyIdMarkerPickup _id_pickup = new NyIdMarkerPickup();
	    private NyIdMarkerPattern _temp_nyid_info=new NyIdMarkerPattern();
	    private NyIdMarkerParam _temp_nyid_param=new NyIdMarkerParam();
    	
	    private NyIdMarkerDataEncoder_RawBit _rb=new NyIdMarkerDataEncoder_RawBit();
	    private NyIdMarkerData_RawBit _rb_dest=new NyIdMarkerData_RawBit();

	    /**
	     * コンストラクタです。
	     * @param i_max
	     * 登録するアイテムの最大数です。
	     * @throws NyARException 
	     */
	    public RawbitSerialIdTable(int i_max)
	    {
		    this._table=new SerialTable(i_max);
	    }
	    /**
	     * IDの範囲に対するメタデータセットを、テーブルに追加します。
	     * この要素にヒットする範囲は,i_st&lt;=n&lt;=i_edになります。
	     * @param i_name
	     * このID範囲の名前を指定します。不要な場合はnullを指定します。
	     * @param i_st
	     * ヒット範囲の開始値です。
	     * @param i_ed
	     * ヒット範囲の終了値です。
	     * @param　i_width
	     * ヒットしたマーカのサイズ値を指定します。
	     */
        public bool addSerialIdRangeItem(String i_name, long i_st, long i_ed, double i_width)
	    {
		    SerialTable.SerialTableRow d=this._table.prePush();
		    if(d==null){
			    return false;
		    }
		    d.setValue(i_name,i_st,i_ed,i_width);
		    return true;
	    }
	    /**
	     * SerialIDに対するメタデータセットを、テーブルに追加します。
	     * @param i_serial
	     * ヒットさせるシリアルidです。
	     * @param i_width
	     * ヒットしたマーカのサイズ値です。
	     * @return
	     * 登録に成功するとtrueを返します。
	     */
        public bool addSerialIdItem(String i_name, long i_serial, double i_width)
	    {
		    SerialTable.SerialTableRow d=this._table.prePush();
		    if(d==null){
			    return false;
		    }
		    d.setValue(i_name,i_serial,i_serial,i_width);
		    return true;
	    }
	    /**
	     * 全てのSerialIDにヒットするメタデータセットを、テーブルに追加します。
	     * @param i_width
	     * ヒットしたマーカのサイズ値です。
	     * @return
	     * 登録に成功するとtrueです。
	     */
        public bool addAnyItem(String i_name, double i_width)
	    {
		    SerialTable.SerialTableRow d=this._table.prePush();
		    if(d==null){
			    return false;
		    }
		    d.setValue(i_name,0,long.MaxValue,i_width);
		    return true;
	    }	
	    /**
	     * i_raster上にあるi_vertexの頂点で定義される四角形のパターンから、一致するID値を特定します。
	     * @param i_vertex
	     * 4頂点の座標
	     * @param i_raster
	     * @param o_result
	     * @return
	     * @throws NyARException
	     */
	    public bool identifyId(NyARDoublePoint2d[] i_vertex,INyARRgbRaster i_raster,IdentifyIdResult o_result)
	    {
		    if(!this._id_pickup.pickFromRaster(i_raster,i_vertex,this._temp_nyid_info,this._temp_nyid_param))
		    {
			    return false;
		    }
		    //受け付けられるControlDomainは0のみ
		    if(this._temp_nyid_info.ctrl_domain!=0)
		    {
			    return false;
		    }
		    //受け入れられるMaskは0のみ
		    if(this._temp_nyid_info.ctrl_mask!=0)
		    {
			    return false;
		    }
		    //受け入れられるModelは5未満
		    if(this._temp_nyid_info.model>=5)
		    {
			    return false;
		    }

		    this._rb.createDataInstance();
		    if(!this._rb.encode(this._temp_nyid_info,this._rb_dest)){
			    return false;
		    }
		    //SerialIDの再構成
		    long s=0;
            //最大4バイト繋げて１個のint値に変換
            for (int i = 0; i < this._rb_dest.length; i++)
            {
                s = (s << 8)|(uint)this._rb_dest.packet[i];
            }	
		    //SerialID引きする。
            SerialTable.SerialTableRow d=this._table.getItembySerialId(s);
		    if(d==null){
			    return false;
		    }
		    //戻り値を設定
		    o_result.marker_width=d.marker_width;
		    o_result.id=s;
		    o_result.artk_direction=this._temp_nyid_param.direction;
		    o_result.name=d.name;
		    return true;		
	    }
	    /**
	     * RealityTargetに一致するID値を特定します。
	     * 複数のパターンにヒットしたときは、一番初めにヒットした項目を返します。
	     * @param i_target
	     * Realityが検出したターゲット。
	     * Unknownターゲットを指定すること。
	     * @param i_rtsorce
	     * i_targetを検出したRealitySourceインスタンス。
	     * @param o_result
	     * 返却値を格納するインスタンスを設定します。
	     * 返却値がtrueの場合のみ、内容が更新されています。
	     * @return
	     * 特定に成功すると、trueを返します。
	     * @throws NyARException 
	     */
	    public bool identifyId(NyARRealityTarget i_target,NyARRealitySource i_rtsorce,IdentifyIdResult o_result)
	    {
		    //NyARDoublePoint2d[] i_vertex,NyARRgbRaster i_raster,SelectResult o_result
		    return this.identifyId(
			    ((NyARRectTargetStatus)(i_target._ref_tracktarget._ref_status)).vertex,
			    i_rtsorce.refRgbSource(),
			    o_result);
	    }
	    //指定したIDとパターンが一致するか確認するAPIも用意するか？
    }
}