// マップマネージャー
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System;

// 外部公開用定義
public enum RBUTTON // ボタンの種別
{
    NOP,
    ROTATE,
    MOVE,
    BUMP,
    ALTITUDE,
    DISTANCE,
    SUN,
    CLOCK,
};

public class TimeKeeper // 時間管理用クラス
{
    public bool change;
    public int year;
    public int month;
    public int day;
    public int hour;
    public int minute;
    public int minuteStep;
    public int length;
    private int _now;
    private float nowF;
    public int now
    {
        get
        {
            return _now;
        }
        set
        {
            int old = _now;
            if (value >= length)
            {
                _now = length - 1;
            } else
            if (value< 0)
            {
                _now = 0;
            } else
            {
                _now = value;
            }
            if (old != _now) change = true;
            nowF = (float)_now;
        }
    }

    public TimeKeeper() // コンストラクタ
    {
        change = false;
    }
    public void ClockUpdate()
    {
        if ((length > 0) && (change == true))
        {
            change = false;
            GameObject.Find("Clock").GetComponent<Text>().text = GetDayStr() + "\n" + GetTimeStr(now);
        }
    }
    public void Shift(int step)
    {
        now = now + step;
    }
    public void Shift(float step)
    {
        nowF += step;
        if (now != (int)nowF) now = (int)nowF;
    }
    public string GetTimeStr(int index) // 時間文字列取得
    {
        if ((index < 0) || (index >= length)) return "";
        int min = minute + minuteStep * index;
        int hou = hour + min / 60;
        min = min % 60;
        return string.Format("{0}:{1:D2}", hou, min);
    }
    public string GetDayStr()           // 日付文字列取得
    {
        return string.Format("{0}/{1}/{2}", year, month, day);
    }
}

public class Runner // ランナー管理用クラス
{
    public string name;
    public float[] x;
    public float[] y;
    public GameObject obj;
    public Human scr;
    public Runner()
    {
    }
}

public class MapManager : MonoBehaviour // マップマネージャ制御クラス
{
    public enum RUNNERMODE  // ランナーの動作状態定義
    {
        NON,        // 動作なし
        READY,      // データ構築済み
        GENERATE,   // 生成
        ACTIVE,     // 生成済み
    };

    public enum MAPMODE // 各マップの動作状態定義
    {
        NON,        // 
        AUTO,       // 自動処理
        FREE,       // 開放状態
        OBJECT,     // マップ土台生成
        LOADING,    // ローディング中
        BUILD,      // 地形データの構築
        ACTIVE,     // 地形生成済み
    };

    public float tapRotate = 2.0f;      // カメラ回転の倍率補正
    public float tapMove = 0.005f;      // カメラ移動の倍率補正
    public float tapBump = 0.01f;       // 隆起変化の倍率補正
    public float tapAltitude = 1.4f;    // カメラ高度の倍率補正
    public float tapDistance = 10.0f;   // カメラ距離の倍率補正
    public float tapSun = 10.0f;        // 太陽移動の倍率補正
    public float tapTime = 1.0f;        // 時間推移の倍率補正

    public float lookLatitude = 0;      // 注視点の緯度
    public float lookLongitude = 0;     // 注視点の経度
    public float lookAltitude = 0;      // 注視点の高度
    public float lookDistance = 100;    // 注視点までの距離
    public float lookDirection = 0;     // 注視方角
    public float lookAngle = 45;        // 注視仰角

    public float sunAngleEW;        // 太陽の東西向き（自転）
    public float sunAngleNS;        // 太陽の南北向き（地軸の傾き）
    public float sunMag = 300.0f;   // 太陽のベクトル重み
    public Color sunColor;          // 太陽光
    public Vector3 sunPos;          // 太陽位置
    public Vector3 sunVec;          // 太陽ベクトル

    public RBUTTON selectButton = RBUTTON.NOP;  // ラジオボタン

    public MAPMODE mapMode;             // マップ動作モード
    public GameObject mapObj;           // マップデータ
    public float magBump = 0.08f;   // 地形隆起率

    // マップデータのダウンロード元
    public string baseUrl = "http://www.gameover.ne.jp/gml/";
    public string mapUrl = "outjj";
    public string runnerUrl = "runner84.csv";

    TimeKeeper timeKeeper;  // 時間管理用

    Runner[] runnerDat;     // ランナーデータ
    RUNNERMODE runnerMode;  // ランナー動作

    void Start()    // 起動後の処理
    {
        mapMode = MAPMODE.AUTO;
        runnerMode = RUNNERMODE.NON;
        StartCoroutine(GetRunnerData());
    }

    void Update()   // 通常処理
    {
        MouseAction();  // マウス処理
        SolarUpdate();  // 太陽更新
        MapUpdate();    // 地形更新
    }

    private IEnumerator GetRunnerData() // ランナーデータの取得
    {
        var www = new WWW(baseUrl + runnerUrl);
        yield return www;
        System.StringSplitOptions option = StringSplitOptions.RemoveEmptyEntries;   // カンマと空データを無効にする

        string[] lines = www.text.Split(new char[] { '\r', '\n' }, option);         // 行に分ける

        char[] spliter = new char[1] { ',' };                                       // カンマ分けの準備(区分けする文字を設定する)

        // 行数設定
        int h = lines.Length;
        int r = 0;
        if (h > 3)
        {
            timeKeeper = new TimeKeeper();
            for (int i = 0; i < h; i++)
            {
                // 列数設定
                int w = lines[i].Split(spliter, option).Length;
                string[] cel = lines[i].Split(spliter, option);
                if ((i == 0) && (w >= 6)) // 時間系データ取得
                {
                    timeKeeper.year = int.Parse(cel[0]);
                    timeKeeper.month = int.Parse(cel[1]);
                    timeKeeper.day = int.Parse(cel[2]);
                    timeKeeper.hour = int.Parse(cel[3]);
                    timeKeeper.minute = int.Parse(cel[4]);
                    timeKeeper.minuteStep = int.Parse(cel[5]);
                    timeKeeper.length = h - 2;
                    timeKeeper.now = 0;
                    timeKeeper.ClockUpdate();
                } else if ((i == 1) && (w >= 1))  // 名前取得
                {
                    r = w;
                    runnerDat = new Runner[w];
                    for (int l = 0; l < w; l++)
                    {
                        runnerDat[l] = new Runner();
                        runnerDat[l].name = cel[l];
                        runnerDat[l].x = new float[h - 2];
                        runnerDat[l].y = new float[h - 2];
                    }
                } else
                {
                    for (int k = 0; k < r; k++)
                    {
                        runnerDat[k].x[i - 2] = float.Parse(cel[k * 2 + 0]);
                        runnerDat[k].y[i - 2] = float.Parse(cel[k * 2 + 1]);
                    }
                }
            }
            runnerMode = RUNNERMODE.READY;  // 準備完了の通知
        }
    }

    void MouseAction()  // マウスの処理
    {
        if (Input.GetMouseButton(0))    // マウス（スマホ画面）が押された時のみ
        {
            switch (selectButton)       // 選択されたボタン別
            {
                case RBUTTON.ROTATE:    // カメラ回転
                    lookDirection -= Input.GetAxis("Mouse X") * tapRotate;
                    lookAngle -= Input.GetAxis("Mouse Y") * tapRotate;
                    break;
                case RBUTTON.MOVE:      // カメラ移動
                    float mx = Input.GetAxis("Mouse X") * tapMove;
                    float my = Input.GetAxis("Mouse Y") * tapMove;
                    Vector2 h = Quaternion.Euler(0, 0, lookDirection) * new Vector2(mx, my);
                    lookLongitude -= h.x;
                    lookLatitude -= h.y;
                    break;
                case RBUTTON.BUMP:      // 地形隆起
                    magBump = Mathf.Clamp(magBump += Input.GetAxis("Mouse Y") * tapBump, 0.001f, 0.15f);
                    break;
                case RBUTTON.ALTITUDE:  // カメラ高度
                    lookAltitude = Mathf.Clamp(lookAltitude += Input.GetAxis("Mouse Y") * tapAltitude, 0.0f, 100.0f);
                    break;
                case RBUTTON.DISTANCE:  // カメラ距離
                    lookDistance = Mathf.Clamp(lookDistance - Input.GetAxis("Mouse Y") * tapDistance, 30.0f, 900.0f);
                    break;
                case RBUTTON.SUN:       // 太陽
                    sunAngleEW = Mathf.Clamp(sunAngleEW - Input.GetAxis("Mouse X") * tapSun, -90.0f, 90.0f);
                    sunAngleNS = Mathf.Clamp(sunAngleNS + Input.GetAxis("Mouse Y") * tapSun, -90.0f, 90.0f);
                    break;
                case RBUTTON.CLOCK:     // 時間
                    timeKeeper.Shift(Input.GetAxis("Mouse X") * tapTime);
                    break;
                default:
                    break;
            }
        }
    }

    void SolarUpdate()  // 太陽更新
    {
        Vector3 pos = Quaternion.Euler(0, 0, Mathf.Clamp(sunAngleEW, -90, 90)) * new Vector3(0, 1, 0);
        pos = Quaternion.Euler(Mathf.Clamp(sunAngleNS, -90, 90), 0, 0) * pos;
        sunPos = pos;   // 中央からの太陽の位置（ベクトル）を更新
        float ew = Mathf.Abs(sunAngleEW);
        float ns = Mathf.Abs(sunAngleNS);
        float sunReduce = Mathf.Abs(Mathf.Sin(Mathf.PI * 2.0f * (ew > ns ? ew : ns) / 360) / 2.0f);
        sunColor = new Color(1.0f, 1.0f - sunReduce, 1.0f - sunReduce); // 太陽角度による光の減衰更新
    }

    void MapUpdate()    // マップ更新
    {
        MAPMODE req = MAPMODE.NON;
        switch (mapMode)
        {
            case MAPMODE.AUTO:  // 自動処理起動
                req = MAPMODE.OBJECT;
                break;
            case MAPMODE.OBJECT:    // 地形処理の生成とダウンロード
                mapObj = Instantiate(Resources.Load("Geo")) as GameObject;
                mapObj.GetComponent<Geo>().Load(baseUrl + mapUrl);
                req = MAPMODE.LOADING;
                break;
            case MAPMODE.LOADING:   // ロード中
                if (mapObj.GetComponent<Geo>().geoData.size != 0) req = MAPMODE.BUILD;
                break;
            case MAPMODE.BUILD: // 地形生成
                mapObj.GetComponent<Geo>().Build();
                req = MAPMODE.ACTIVE;
                lookLongitude = mapObj.GetComponent<Geo>().centerLL.x;
                lookLatitude = mapObj.GetComponent<Geo>().centerLL.y;
                break;
            case MAPMODE.ACTIVE:    // 通常動作
                RunnerUpdate(); // ランナーの更新
                break;
        }
        if (req != MAPMODE.NON)
        {
            mapMode = req;
            req = MAPMODE.NON;
        }
    }
    
    void RunnerUpdate() // ランナーの更新
    {
        if (runnerMode == RUNNERMODE.READY)
        {
            for (int r = 0; r < runnerDat.Length; r++)
            {
                GenerateRunner(r, 0);
            }

            runnerMode = RUNNERMODE.ACTIVE;
            timeKeeper.change = true;   // 初回の強制指令
        }
        else
        if (runnerMode == RUNNERMODE.ACTIVE)
        {
            if (timeKeeper.change)
            {
                for (int r = 0; r < runnerDat.Length; r++)
                {
                    MoveRunner(r, timeKeeper.now);
                }
            }
        }
        timeKeeper.ClockUpdate();
    }

    public bool GenerateRunner(int num, int index)  // ランナーの生成
    {
        GameObject hObj;
        Human hObjScr;
        if ((index < 0) || (index >= runnerDat[num].x.Length)) return false;
        hObj = Instantiate(Resources.Load("human")) as GameObject;
        if (hObj == null) return false;
        hObj.transform.rotation = Quaternion.Euler(0, 90, 0);
        hObjScr = hObj.GetComponent<Human>();
        if (hObjScr == null) return false;
        hObjScr.lat = runnerDat[num].y[index];
        hObjScr.lot = runnerDat[num].x[index];
        hObjScr.reqRun = true;
        hObjScr.dParent = mapObj;
        hObjScr.name = runnerDat[num].name;
        runnerDat[num].obj = hObj;
        runnerDat[num].scr = hObjScr;
        return true;
    }

    public bool MoveRunner(int num, int index)  // ランナーの移動
    {
        if ((index < 0) || (index >= runnerDat[num].x.Length)) return false;
        runnerDat[num].scr.lat = runnerDat[num].y[index];
        runnerDat[num].scr.lot = runnerDat[num].x[index];
        return true;
    }
}

