/* 
 * PROJECT: NyARToolkit
 * --------------------------------------------------------------------------------
 * This work is based on the original ARToolKit developed by
 *   Hirokazu Kato
 *   Mark Billinghurst
 *   HITLab, University of Washington, Seattle
 * http://www.hitl.washington.edu/artoolkit/
 *
 * The NyARToolkit is Java version ARToolkit class library.
 * Copyright (C)2008 R.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 2
 * 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 framework; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * For further information please contact.
 *	http://nyatla.jp/nyatoolkit/
 *	<airmail(at)ebony.plala.or.jp>
 * 
 */
package jp.nyatla.nyartoolkit.core;




import jp.nyatla.nyartoolkit.NyARException;




/**
 * イメージからマーカー情報を検出するクラス。
 * このクラスは、arDetectMarker2.cとの置き換えになります。
 * ラベリング済みのラスタデータからマーカー位置を検出して、結果を保持します。
 *
 */
public class NyARDetectMarker {
    private static final int AR_AREA_MAX=100000;//#define   AR_AREA_MAX      100000
    private static final int AR_AREA_MIN=70;//#define   AR_AREA_MIN          70

//    private final NyARMarker[] marker_holder;	    //マーカーデータの保持配列
//    private final NyARMarker[] marker_info2_array;//マーカーデータのインデックス配列
//    private int marker_num;
    private int width,height;
    /**
     * 最大i_squre_max個のマーカーを検出するクラスを作成する。
     * @param i_width
     * @param i_height
     */
    public NyARDetectMarker(int i_width,int i_height)
    {
	this.width =i_width;
	this.height=i_height;
	
//	this.marker_holder=new NyARMarker[i_squre_max];
//	this.marker_info2_array=new NyARMarker[i_squre_max];
//	//先にマーカーホルダにオブジェクトを作っておく
//	for(int i=0;i<i_squre_max;i++){
//	    this.marker_holder[i]=new NyARMarker();
//	}
    }
    private static final int AR_CHAIN_MAX=10000;
    private final int[] wk_arGetContour_xdir={0,  1, 1, 1, 0,-1,-1,-1};
    private final int[] wk_arGetContour_ydir={-1,-1, 0, 1, 1, 1, 0,-1};
    private final int[] wk_arGetContour_xcoord=new int[AR_CHAIN_MAX];
    private final int[] wk_arGetContour_ycoord=new int[AR_CHAIN_MAX];
    /**
     * int arGetContour( ARInt16 *limage, int *label_ref,int label, int clip[4], ARMarkerInfo2 *marker_info2 )
     * 関数の代替品
     * detectMarker関数から使う関数です。o_markerにlabelとclipで示される1個のマーカーを格納します。
     * marker_holder[i_holder_num]にオブジェクトが無ければまず新規に作成し、もし
     * 既に存在すればそこにマーカー情報を上書きして記録します。
     * Optimize:STEP[369->336]
     * @param o_marker
     * @param limage
     * @param label_ref
     * @param label
     * @param clip
     * @throws NyARException
     */
    private final void arGetContour(NyARMarker o_marker,int[][] limage, int[] label_ref,int i_labelnum, NyARLabel i_label) throws NyARException
    {
	final int[] xcoord=wk_arGetContour_xcoord;
	final int[] ycoord=wk_arGetContour_ycoord;
	final int[] xdir=wk_arGetContour_xdir; //static int      xdir[8] = { 0, 1, 1, 1, 0,-1,-1,-1};
	final int[] ydir=wk_arGetContour_ydir;//static int      ydir[8] = {-1,-1, 0, 1, 1, 1, 0,-1};
	//ShortPointer p1;//ARInt16         *p1;
	int coord_num;
	int             sx=0, sy=0, dir;
	int             dmax, d, v1=0;
	int             i, j,w;

	int[] limage_j;
	j = i_label.clip2;
	limage_j=limage[j];
	final int clip1=i_label.clip1;
	//p1=ShortPointer.wrap(limage,j*xsize+clip.get());//p1 = &(limage[j*xsize+clip[0]]);
	for( i = i_label.clip0; i <= clip1; i++){//for( i = clip[0]; i <= clip[1]; i++, p1++ ) {
	    w=limage_j[i];
	    if(w > 0 && label_ref[w-1] == i_labelnum ) {//if( *p1 > 0 && label_ref[(*p1)-1] == label ) {
		sx = i;
		sy = j;
		break;
	    }
	}
	if(i>clip1){//if( i > clip[1] ) {
	    System.out.println("??? 1");//printf();
	    throw new NyARException();//return(-1);
	}

//	//マーカーホルダが既に確保済みかを調べる
//	if(marker_holder[i_holder_num]==null){
//	    //確保していなければ確保
//	    marker_holder[i_holder_num]=new NyARMarker();
//	}


	coord_num=1;//marker_info2->coord_num = 1;
	xcoord[0]=sx;//marker_info2->x_coord[0] = sx;
	ycoord[0]=sy;//marker_info2->y_coord[0] = sy;
	dir = 5;
	
	int r,c;
        c=xcoord[0];
        r=ycoord[0];
        dmax=0;
        //本家はdmaxの作成とxcoordの作成を別のループでやってるけど、非効率なので統合
	for(;;){
	    //xcoord[1]-xcoord[n]までのデータを作る。
	    
//	    1個前のxcoordとycoordはループ後半で格納される。
//	    c=xcoord[coord_num-1];
//	    r=ycoord[coord_num-1];
	    //p1 = &(limage[marker_info2->y_coord[marker_info2->coord_num-1] * xsize+ marker_info2->x_coord[marker_info2->coord_num-1]]);
	    dir = (dir+5)%8;
	    for(i=0;i<8;i++) {
		if(limage[r+ydir[dir]][c+xdir[dir]]>0){//if( p1[ydir[dir]*xsize+xdir[dir]] > 0 ){
		    break;
		}
		dir = (dir+1)%8;	
	    }
	    if( i == 8 ){
		System.out.println("??? 2");//printf("??? 2\n");
		throw new NyARException();//return(-1);
	    }
//	    xcoordとycoordをc,rにも保存
	    c= c + xdir[dir];//marker_info2->x_coord[marker_info2->coord_num]= marker_info2->x_coord[marker_info2->coord_num-1] + xdir[dir];
	    r= r + ydir[dir];//marker_info2->y_coord[marker_info2->coord_num]= marker_info2->y_coord[marker_info2->coord_num-1] + ydir[dir];
	    xcoord[coord_num]=c;//marker_info2->x_coord[marker_info2->coord_num]= marker_info2->x_coord[marker_info2->coord_num-1] + xdir[dir];
	    ycoord[coord_num]=r;//marker_info2->y_coord[marker_info2->coord_num]= marker_info2->y_coord[marker_info2->coord_num-1] + ydir[dir];
	    if(c == sx && r == sy ){
		break;
	    }
	    //dmaxの計算
	    d=(c-sx)*(c-sx)+(r-sy)*(r-sy);
	    if( d > dmax ) {
		dmax = d;
		v1 = coord_num;
	    }
	    //終了条件判定
	    coord_num++;
	    if(coord_num == AR_CHAIN_MAX-1){//if( marker_info2.coord_num == Config.AR_CHAIN_MAX-1 ){
		System.out.println("??? 3");//printf("??? 3\n");
		throw new NyARException();//return(-1);
	    }
	}
//
//	dmax = 0;
//	for(i=1;i<coord_num;i++) {//	for(i=1;i<marker_info2->coord_num;i++) {
//	    d = (xcoord[i]-sx)*(xcoord[i]-sx)+ (ycoord[i]-sy)*(ycoord[i]-sy);//	  d = (marker_info2->x_coord[i]-sx)*(marker_info2->x_coord[i]-sx)+ (marker_info2->y_coord[i]-sy)*(marker_info2->y_coord[i]-sy);
//	    if( d > dmax ) {
//		dmax = d;
//		v1 = i;
//	    }
//	}
	//NyARMarkerへcoord情報をセット
	//coordの並び替えと保存はNyARMarkerへ移動
	o_marker.setCoordXY(v1,coord_num,xcoord,ycoord);
	return;
    }

    /**
     * ARMarkerInfo2 *arDetectMarker2( ARInt16 *limage, int label_num, int *label_ref,int *warea, double *wpos, int *wclip,int area_max, int area_min, double factor, int *marker_num )
     * 関数の代替品
     * ラベリング情報からマーカー一覧を作成してo_marker_listを更新します。
     * 関数はo_marker_listに重なりを除外したマーカーリストを作成します。
     * 
     * @param i_labeling
     * ラベリング済みの情報を持つラベリングオブジェクト
     * @param i_factor
     * 何かの閾値？
     * @param o_marker_list
     * 抽出したマーカーを格納するリスト
     * @throws NyARException
     */
    public final void detectMarker(NyARLabeling i_labeling,double i_factor,NyARMarkerList o_marker_list) throws NyARException
    {
	int label_area;
	int i;
	int xsize, ysize;
	NyARLabel[] labels=i_labeling.getLabel();
//	int[] warea  	=i_labeling.getArea();
	int label_num	=i_labeling.getLabelNum();
//	int[][] wclip	=i_labeling.getClip();
//	double[] wpos	=i_labeling.getPos();
	int[][] limage=i_labeling.getLabelImg();
	int[] label_ref	=i_labeling.getLabelRef();

	//マーカーホルダをリセット
	o_marker_list.reset();
//	marker_num=0;
	xsize =width;
	ysize =height;
//	マーカーをmarker_holderに蓄積する。
	NyARMarker current_marker=o_marker_list.getCurrentHolder();
	NyARLabel label_pt;
	for(i=0; i<label_num; i++ ){
	    label_pt=labels[i];
	    label_area=label_pt.area;
	    if(label_area < AR_AREA_MIN || label_area > AR_AREA_MAX ){
		continue;
	    }
	    if( label_pt.clip0 == 1 || label_pt.clip1 == xsize-2 ){//if( wclip[i*4+0] == 1 || wclip[i*4+1] == xsize-2 ){
		continue;
	    }
	    if( label_pt.clip2 == 1 || label_pt.clip3 == ysize-2 ){//if( wclip[i*4+2] == 1 || wclip[i*4+3] == ysize-2 ){
		continue;
	    }
	    //ret = arGetContour( limage, label_ref, i+1,&(wclip[i*4]), &(marker_info2[marker_num2]));
	    arGetContour(current_marker,limage, label_ref, i+1,label_pt);

	    if(!current_marker.checkSquare(label_area,i_factor,label_pt.pos_x,label_pt.pos_y)){
		//後半で整理するからここはいらない。//        	marker_holder[marker_num2]=null;
		continue;
	    }
//	    この3行はcheckSquareの最終段に含める。
//	    marker_holder[marker_num2].area   = warea[i];
//	    marker_holder[marker_num2].pos[0] = wpos[i*2+0];
//	    marker_holder[marker_num2].pos[1] = wpos[i*2+1];
	    //マーカー検出→次のホルダを取得
	    current_marker=o_marker_list.getNextHolder();
	    //マーカーリストが上限に達したか確認
	    if(current_marker==null){
		break;
	    }
	}
	//マーカーリストを整理(重なり処理とかはマーカーリストに責務押し付け)
	o_marker_list.updateMarkerArray();
//	重なり処理かな？
//	double[] pos_j,pos_i;
//	for(i=0; i < marker_num2; i++ ){
//	    pos_i=marker_holder[i].pos;
//	    for(j=i+1; j < marker_num2; j++ ) {
//		pos_j=marker_holder[j].pos;
//		d = (pos_i[0] - pos_j[0])*(pos_i[0] - pos_j[0])+
//		(pos_i[1] - pos_j[1])*(pos_i[1] - pos_j[1]);
//		if(marker_holder[i].area >marker_holder[j].area ) {
//		    if( d <marker_holder[i].area / 4 ) {
//			marker_holder[j].area = 0;
//		    }
//		}else{
//		    if( d < marker_holder[j].area / 4 ) {
//			marker_holder[i].area = 0;
//		    }
//		}
//	    }
//	}
//	みつかったマーカーを整理する。
//	エリアが0のマーカーを外した配列を作って、その数もついでに計算
//	for(i=0;i<marker_num2;i++){
//	    if(marker_holder[i].area==0.0){
//		continue;
//	    }
//	    marker_info2_array[marker_num]=marker_holder[i];
//	    marker_num++;
//	}        
//	for( i=0; i < marker_num2; i++ ) {
//	if( marker_info2_array[i].area == 0.0 ) {
//	for( j=i+1; j < marker_num2; j++ ){
//	marker_info2_array[j-1] = marker_info2_array[j];
//	}
//	marker_num2--;
//	}
//	}
//	発見したマーカー数をセット
//	marker_num=marker_num2;//*marker_num = marker_num2;
//	return( &(marker_info2[0]) );
	return;
    }

}
