/* 
 * PROJECT: NyARToolkit(Extension)
 * --------------------------------------------------------------------------------
 * The NyARToolkit is Java 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.Collections.Generic;
using jp.nyatla.nyartoolkit.cs.core;
namespace jp.nyatla.nyartoolkit.cs.markersystem.utils
{
    public class TrackingList : List<TMarkerData>
    {
        private VertexSortTable _tracking_list;
        public TrackingList()
        {
            this._tracking_list = new VertexSortTable(1);
        }
        public void prepare()
        {
            //トラッキングリストをリセット
            this._tracking_list.reset();
            //検出のために初期値設定
            for (int i = this.Count - 1; i >= 0; i--)
            {
                TMarkerData target = this[i];
                //直前のsqのリセット
                target.sq = null;
                //消失カウンタのインクリメント
                if (target.life > 0)
                {
                    target.lost_count++;
                }
            }
        }
        private int[] __ret = new int[2];
        public bool update(SquareStack.Item i_new_sq)
        {
            int[] ret = this.__ret;
            int new_area = i_new_sq.rect_area;
            //頂点の対角距離
            int new_sq_dist = i_new_sq.vertex_area.getDiagonalSqDist();
            bool is_dispatched = false;
            for (int i = this.Count - 1; i >= 0; i--)
            {
                TMarkerData target = this[i];
                if (target.lost_count > 1)
                {
                    continue;
                }
                //面積比が急激0.8-1.2倍以外の変動なら無視
                int a_rate = new_area * 100 / target.tl_rect_area;
                if (a_rate < 50 || 150 < a_rate)
                {
                    continue;
                }
                //移動距離^2の二乗が対角線距離^2の4倍以上なら無視
                long sq_move = target.tl_center.sqDist(i_new_sq.center2d);
                if (sq_move * 4 / new_sq_dist > 0)
                {
                    continue;
                }
                compareVertexSet(i_new_sq.ob_vertex, target.tl_vertex, ret);
                int sqdist = ret[1];
                int shift = ret[0];
                //頂点移動距離の合計が、(中心点移動距離+8)の10倍を超えてたらNG <-
                if (sqdist > (sq_move + 8) * 10)
                {
                    continue;
                }
                //登録可能か確認
                VertexSortTable.Item item = this._tracking_list.getInsertPoint(sqdist);
                if (item == null)
                {
                    continue;
                }
                //登録
                item = this._tracking_list.insertFromTailBefore(item);
                item.marker = target;
                item.shift = shift;
                item.sq_dist = sqdist;
                item.ref_sq = i_new_sq;
                is_dispatched = true;
            }
            return is_dispatched;
        }

        /**
         * この関数は、頂点セット同士のシフト量を計算して、配列に値を返します。
         * 並びが同じである頂点セット同士の最低の移動量を計算して、その時のシフト量と二乗移動量の合計を返します。
         * @param i_square
         * 比較対象の矩形
         * @return
         * [0]にシフト量を返します。
         * [1]に頂点移動距離の合計の二乗値を返します。
         * シフト量はthis-i_squareです。1の場合、i_v1[0]とi_v2[1]が対応点になる(shift量1)であることを示します。
         */
        public static void compareVertexSet(NyARIntPoint2d[] i_v1, NyARIntPoint2d[] i_v2, int[] ret)
        {
            //3-0番目
            int min_dist = int.MaxValue;
            int min_index = 0;
            int xd, yd;
            for (int i = 3; i >= 0; i--)
            {
                int d = 0;
                for (int i2 = 3; i2 >= 0; i2--)
                {
                    xd = (int)(i_v1[i2].x - i_v2[(i2 + i) % 4].x);
                    yd = (int)(i_v1[i2].y - i_v2[(i2 + i) % 4].y);
                    d += xd * xd + yd * yd;
                }
                if (min_dist > d)
                {
                    min_dist = d;
                    min_index = i;
                }
            }
            ret[0] = min_index;
            ret[1] = min_dist;
        }
        /**
         * トラッキングリストへ追加。このadd以外使わないでね。
         */
        public void add(TMarkerData e)
        {
            //1マーカ辺りの最大候補数
            for (int i = 0; i < 2; i++)
            {
                this._tracking_list.append();
            }
            base.Add(e);
        }
        public void finish()
        {
            //一致率の最も高いアイテムを得る。
            VertexSortTable.Item top_item = this._tracking_list.getTopItem();
            //アイテムを検出できなくなるまで、一致率が高い順にアイテムを得る。
            while (top_item != null)
            {
                //検出したアイテムのARmarkerIndexのデータをセット
                TMarkerData target = top_item.marker;
                //検出カウンタが1以上（未検出の場合のみ検出）
                if (target.lost_count > 0)
                {
                    target.lost_count = 0;
                    target.life++;
                    target.sq = top_item.ref_sq;
                    target.sq.rotateVertexL(4 - top_item.shift);
                    NyARIntPoint2d.shiftCopy(top_item.ref_sq.ob_vertex, target.tl_vertex, 4 - top_item.shift);
                    target.tl_center.setValue(top_item.ref_sq.center2d);
                    target.tl_rect_area = top_item.ref_sq.rect_area;
                }
                //基準アイテムと重複するアイテムを削除する。
                this._tracking_list.disableMatchItem(top_item);
                top_item = this._tracking_list.getTopItem();
            }
        }
    }
}