﻿using UnityEngine;
using System.Collections;

public class BoidClusterAnalysis : MonoBehaviour {


	/* ボイドに関する情報 */

	//ボイドオブジェクトの配列
	private SingleBoid[] boid;	
	//ボイドの個体数
	private int pop = 0;


	/* クラスタの定義 */

	//視界距離
	private float vision_space;
	//最小メンバ数
	private int minimumpop = 2;


	/* クラスタの解析結果 */

	//クラスタの総数
	public int csum = 0;
	//クラスタの重心
	public Vector3[] cog;
	//クラスタの半径
	public float[] rad;
	//クラスタの個体数
	public int[] cpop;

	/* クラスタのid（0,1,2,...）*/

	//どのクラスタにも属していない場合, -1とする. 
	public  int[] cluster_id;

    //友達履歴（何フレーム連続で同一のクラスターにいるか）
    public int[][] friends_history;



    public void Start()
    {

        //　あらかじめ、友達履歴記録用の配列を
        //　余裕を持って作成しておく. 
        friends_history = new int[9999][];

        for (int i = 0; i < 9999; i++)
        {
            friends_history[i] = new int[9999];

            for (int j = 0; j < 9999; j++)
            {
                friends_history[i][j] = 0;
            }

        }
    }



    public void SetBoid(BoidManager m){
		boid = m.boid;
		pop = m.pop;
		vision_space = m.vision_space;
		cluster_id = new int[pop];
	}

	public void SetMinimumPop(int p){
		this.minimumpop = p;
	}


    public void InitFriendsHistory(BoidManager m)
    {

        pop = m.pop;

        friends_history = new int[pop][];

        for(int i = 0; i < pop; i++)
        {
            friends_history[i] = new int[pop];

            for(int j = 0; j < pop; j++)
            {
                friends_history[i][j] = 0;
            }

        }
    }

    /* 友達履歴のアップデート 
     * 新たに、fperiod(frame)以上で同一のクラスタ内にいるペアができた回数を返す. 
     */
    public int UpdFriendsHistory(int fperiod)
    {

        int new_friends = 0;

        for(int i = 0; i < pop; i++)
        {
            int cid1 = cluster_id[i];

            for (int j = i + 1; j < pop; j++)
            {
                int cid2 = cluster_id[j];

                if (cid1 == -1)
                {
                    friends_history[i][j] = 0;
                    friends_history[j][i] = 0;

                }
                else if (cid1 == cid2)
                {

                    friends_history[i][j]++;
                    friends_history[j][i]++;

                    if (friends_history[i][j] == fperiod)
                    {
                        new_friends++;
                    }

                }
                else
                {
                    friends_history[i][j] = 0;
                    friends_history[j][i] = 0;
                }
            }
        }


        return new_friends;

    }


    public void CountCluster(){

		for (int i = 0; i < pop; i++) {
			cluster_id [i] = -1;
		}

			
		//直接の知り合い関係
		bool[][] nbh1 = new bool[pop] [];
		//間接の知り合い関係
		bool[][] nbh2 = new bool[pop] [];

		for (int i = 0; i < pop; i++) {
			nbh1 [i] = new bool[pop];
			nbh2 [i] = new bool[pop];
			for (int j = 0; j < pop; j++) {
				nbh1 [i] [j] = false;
				nbh2 [i] [j] = false;
			}
		}

		//直接の知り合い関係のスキャン
		for (int i = 0; i < pop; i++) {
			for (int j = 0; j < pop; j++) {
				if (j != i && Vector3.Distance (boid [i].pos, boid [j].pos) < vision_space) {
					nbh1 [i] [j] = true;
					nbh2 [i] [j] = true;	
				}
			}
		}

		//知り合いの伝播（nbh2）

		while (true) {
			bool escape = true;

			for (int i = 0; i < pop; i++) {
				for (int j = 0; j < pop; j++) {

					if (nbh2 [i] [j]) {
						for (int k = 0; k < pop; k++) {
							if (nbh1 [j] [k] && !nbh2 [i] [k]) {
								nbh2 [i] [k] = true;
								escape = false;
							}
						}
					}

				}
			}
			if (escape)	break;
		}

		int id = -1;
		bool[] belonging = new bool[pop];
		int[] groupsize1 = new int[pop];
		int[] groupsize2 = new int[pop];
		csum = 0;

		for (int i = 0; i < pop; i++) {
			belonging [i] = false;
			groupsize1 [i] = 0;
			groupsize2 [i] = 0;
		}

		//グループID（0,1,...）を割り振る作業
		for (int i = 0; i < pop; i++) {

			//iボイドの所属が決まっていない場合にのみ以下を実行
			if (belonging [i] == false) {
				id++;
				belonging [i] = true;
	
				for (int j = 0; j < pop; j++) {
					if (nbh2 [i] [j]) {
						belonging [j] = true;
						groupsize1 [id]++;
					}
				}

				if (groupsize1 [id] >= minimumpop) {
					csum++;
					groupsize2 [csum - 1] = groupsize1 [id];
					cluster_id [i] = csum - 1;
					for (int j2 = i + 1; j2 < pop; j2++) {
						if (nbh2 [i] [j2]) {
							cluster_id [j2] = csum - 1;
						}
					}
				}
			}
		}

		cog = new Vector3[csum];
		cpop = new int[csum];
		rad = new float[csum];

		for (int g = 0; g < csum; g++) {
			cpop [g] = groupsize2 [g];

			float count = 0;
			Vector3 psum = new Vector3();

			for (int i = 0; i < pop; i++) {
				if (cluster_id [i] == g) {
					psum += boid [i].pos;
					count++;
				}
			}

			cog [g] = new Vector3(psum.x / count, psum.y / count, psum.z / count);

			float max = 0;

			for (int i = 0; i < pop; i++) {
				if (cluster_id [i] == g) {
					float tmp = Vector3.Distance (cog [g], boid [i].pos);
					if (tmp > max) {
						max = tmp;
					}
				}
			}

			rad [g] = max;

		}

	}



}