﻿using UnityEngine;
using System.Collections;

public class BoidManager : MonoBehaviour
{


    //解析オブジェクト
    BoidClusterAnalysis ana;

    //ボイドルールマネージャ
    BoidRuleManager rule;

    //飛翔スペース
    static public int flyspace = 400;   //一辺の長さ
    static public int flyheight = 200;  //高さ（3Dモード）
                                        //速さの最大値
    static public float speedmax = 100f;

    //二次元モードと三次元モードの切り替え（keyの2と3で切り替え）
    static public bool mode_2d = true;
    //解析モード（keyのaで切り替え）
    static public bool mode_analysis = false;
    //結合可視モード（keyのcで切り替え）
    static public bool mode_connectivity = false;

    //視界距離（見渡せる範囲）
    public float vision_space = 35f;
    //接触距離（ぶつかる範囲）
    public float neighbor_space = 10f;

    //ボイドの数
    public int pop = 50;





    //ボイドの配列（インスペクタには非表示）
    [HideInInspector]
    public SingleBoid[] boid;

    /*　SIRS実装時に使用します。 */
    // [HideInInspector]
    // public GameObject[] boidobj;


    //結合状態可視化用の各種オブジェクト
    private GameObject[] cball;        //クラスターの可視化
    private GameObject[][] connection; //結合の可視化


    //ボイド解析用の変数 （ルール作成の演習では全てprivateとする）
    private float countmax = 1000f; //計測フレーム
    private float cls_sum = 0f; //クラスターの総計
    private float cls_count = 0f; //現在のフレーム数
    private float cls_mean = 0f; //クラスターの平均値

    //新しい知り合いができたフレーム数
    //（10フレーム以上同一のクラスタにいるペアが新たに生まれた場合）
    private int make_friends_frame = 0;

    /*　集合知解析で使用します。 */
    /*
	public float cls_count = 0f; //現在のフレーム数
	public float cls_mean = 0f; //クラスターの平均値
	public int make_friends_frame = 0;
	*/




    void Start()
    {

        /* ボイドルールマネージャの生成 */
        rule = this.GetComponent<BoidRuleManager>();

        /* 解析オブジェクトの生成 */
        ana = this.GetComponent<BoidClusterAnalysis>();


        /* ボイドオブジェクト（SingleBoidクラス｜スクリプト） */
        boid = new SingleBoid[pop];
        //boidobj = new GameObject[pop]; //（SIRS実装時にコメントを外す）

        GameObject bpar = GameObject.Find("ParentBoid"); //親のゲームオブジェクトを探索

        for (int i = 0; i < pop; i++)
        {
            //GameObject bobj = Instantiate ((GameObject)Resources.Load ("Boid"));
            GameObject bobj = Instantiate((GameObject)Resources.Load("Boid"), bpar.transform);

            boid[i] = bobj.GetComponent<SingleBoid>();
            //boidobj[i] = bobj; //（SIRS実装時にコメントを外す）
        }



        /* 視界範囲内にあるボイドを結ぶ結合子（ゲームオブジェクト） */

        //サイズは、重複のない二次元配列
        connection = new GameObject[pop][];
        GameObject cpar = GameObject.Find("ParentConnection"); //親のゲームオブジェクトを探索

        for (int i = 0; i < pop; i++)
        {
            connection[i] = new GameObject[pop - i - 1];
            for (int j = i + 1; j < pop; j++)
            {
                connection[i][j - i - 1] = Instantiate((GameObject)Resources.Load("Connection"), cpar.transform);
            }
        }


        /* クラスターを包む球（ゲームオブジェクト） */

        //ボイドの数だけ配備しておく
        cball = new GameObject[pop];
        GameObject cbpar = GameObject.Find("ParentClusterBall"); //親のゲームオブジェクトを探索

        for (int i = 0; i < pop; i++)
        {
            cball[i] = Instantiate((GameObject)Resources.Load("ClusterBall"), cbpar.transform);
        }


    }

    void Update()
    {

        //すべてのconnetion・cballを非表示にする. 
        ResetActive();

        //ボイドルールマネージャによるルールの適用
        rule.SetBoid(this);
        rule.ApplyRules();


        //結合状態の可視化（CでON/OFF）
        if (mode_connectivity)
        {
            ShowConnectivity();
        }



        if (mode_analysis)
        {

            ana.SetBoid(this);
            //個体数（1/10）をクラスター成立の要件とする。
            ana.SetMinimumPop((int)Mathf.Floor(pop / 10f));
            //クラスタを計算
            ana.CountCluster();
            //新しく「知り合い」が成立したボイドの数
            int new_friends = ana.UpdFriendsHistory(10);
            //クラスタに半透明の球を描画
            ShowClusterBall();

            //カウンタが最大値（初期値1000）となるまで解析を続ける
            if (cls_count < countmax)
            {
                //クラスタのフレーム内の総数
                cls_sum += ana.csum;
                //クラスタの1フレーム平均
                cls_mean = cls_sum / cls_count;
                //計算フレーム数
                cls_count++;

                //新しい知り合いが一件でも成立すれば、
                //「知り合い成立機会」を1つ増やす。
                if (new_friends >= 1)
                {
                    make_friends_frame += 1;
                }


            }





        }



        /* Keyイベント */

        /* 全てのボイドの位置・速度を初期化（I） */
        if (Input.GetKeyDown(KeyCode.I))
        {
            InitBoidPosition();
            InitBoidVelocity();

            /*　集合知解析では以下の行をアンコメントします。 */
            /*
			cls_sum = 0f;
			cls_count = 0f;
			make_friends_frame = 0;
			ana.InitFriendsHistory(this);
			*/

        }

        /* 解析モードの切り替え（A） */
        if (Input.GetKeyDown(KeyCode.A))
        {
            mode_analysis = !mode_analysis;
        }

        /* ボイド間の接続モードの切り替え（C） */
        if (Input.GetKeyDown(KeyCode.C))
        {
            mode_connectivity = !mode_connectivity;
        }

        /* 二次元モード（２） */
        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            mode_2d = true;
        }

        /* 三次元モード（３） */
        if (Input.GetKeyDown(KeyCode.Alpha3))
        {
            mode_2d = false;
        }


    }



    /* 全てのボイドの位置をランダムに初期化*/
    private void InitBoidPosition()
    {
        for (int i = 0; i < pop; i++)
        {
            boid[i].SetRandomPosition();
        }
    }

    /* 全てのボイドの速度をランダムに初期化*/
    private void InitBoidVelocity()
    {
        for (int i = 0; i < pop; i++)
        {
            boid[i].SetRandomVelocity();
        }
    }

    /* ボイド同志の結合状態（vision_space未満の距離）を可視化 */
    private void ShowConnectivity()
    {

        for (int i = 0; i < pop; i++)
        {
            Vector3 ipos = boid[i].pos;

            for (int j = i + 1; j < pop; j++)
            {
                Vector3 jpos = boid[j].pos;
                float dis = Vector3.Distance(ipos, jpos);

                if (dis < vision_space)
                {
                    connection[i][j - i - 1].SetActive(true);
                    connection[i][j - i - 1].transform.position = (ipos + jpos) / 2;
                    connection[i][j - i - 1].transform.rotation =
                        Quaternion.LookRotation(jpos - ipos);
                    connection[i][j - i - 1].transform.localScale =
                        new Vector3(0.5f, 0.5f, (dis / 2f) + 10f);
                }
                else
                {
                    connection[i][j - i - 1].SetActive(false);
                }

            }
        }
    }

    /* クラスター（viosion_space未満の集団）を可視化*/
    private void ShowClusterBall()
    {

        for (int i = 0; i < pop; i++)
        {
            cball[i].SetActive(false);
        }

        for (int g = 0; g < ana.csum; g++)
        {
            cball[g].transform.position = ana.cog[g];
            float d = ana.rad[g] * 2f;
            cball[g].transform.localScale = new Vector3(d, d, d);
            cball[g].SetActive(true);
        }

    }


    private void ResetActive()
    {

        for (int i = 0; i < pop; i++)
        {
            cball[i].SetActive(false);
        }


        for (int i = 0; i < pop; i++)
        {
            for (int j = i + 1; j < pop; j++)
            {
                connection[i][j - i - 1].SetActive(false);
            }
        }


    }


}
