using Assets.Scripts.Models;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class PlayerController : MonoBehaviour
{
    public static Dictionary<uint, List<Structure>> Workers = new Dictionary<uint, List<Structure>>();
    public static List<Structure> TestStructures = new List<Structure>();
    public static List<Beacon> Beacons = new List<Beacon>();

    public GameObject WorkersList; // scroll content
    public Dropdown DropdownCompany;
    public Dropdown DropdownLocation;
    public Dropdown DropdownData;
    public GameObject inputField_date;
    //public InputField inputField_location;
    public GameObject TimeStart;
    public GameObject TimeEnd;
    public GameObject StartHour;
    public GameObject StartMin;
    public GameObject StartSec;
    public GameObject EndHour;
    public GameObject EndMin;
    public GameObject EndSec;
    public GameObject StartStopButton;
    public GameObject ErrorDialog;
    // public GameObject ButtonPlay;
    public Dropdown DropdownMode;
    public Toggle ToggleLine;
    public Toggle ToggleDisappearance;
    public GameObject DatePicker;
    Camera camera;
    public Toggle projection;
    public GameObject Locations;
    public GameObject Dialog;
    public GameObject ZonesScroll;
    public GameObject Location;

    public enum Mode { RealTime = 0, History = 1, Stop = 2 };
    public static Mode active_mode = Mode.Stop;
    int mode = 0;
    //public GameObject Time;

    Client client;
    static EditorController editor;

    DateTimePicker Date;

    //public List<GameObject> maps;
    static bool starting = false; // флаг остановки
    public static Dictionary<uint, bool> end_send = new Dictionary<uint, bool>(); // флаги завершения загрузки
    Dictionary<uint, MarkerMoving> moving = new Dictionary<uint, MarkerMoving>(); // флаги начала движения
    public static Dictionary<uint, Load> load_location_elements = new Dictionary<uint, Load>(); // флаги загрузки элементов локации
    public static bool load_location = false;
    float interpolationPeriod = 0.1f;
    //float time_realtime = 0;
    float time_draw = 0;
    public static List<User> users = new List<User>();
    public static bool users_load = false;
    public static bool beacons_load = false;
    public static List<Location> locations;
    public static List<Marker> markers;

    //List<bool> StartStop = new List<bool>();

    // Start is called before the first frame update
    void Start()
    {
        DebugHelper.ActivateConsole();
        client = Client.instance;
        editor = GameObject.Find("Canvas").transform.GetChild(3).GetComponent<EditorController>();
        camera = Camera.main;
        markers = new List<Marker>();
        locations = new List<Location>();

        Date = DatePicker.GetComponent<DateTimePicker>();
        //locations.Add(new Location { id = 1, name = "1 Братск" });
        //locations.Add(new Location { id = 22, name = "22 Офис Ижевск" });
        //locations.Add(new Location { id = 25, name = "25" });
        //locations.Add(new Location { id = 26, name = "26 Братское (вагрант)" });
        //foreach (var l in locations)
        //    DropdownLocation.options.Add(new Dropdown.OptionData(l.name));

        //for (int i = 0; i < Locations.transform.childCount; i++)
        //    maps.Add(Locations.transform.GetChild(i).gameObject);

        DropdownCompany.options.Add(new Dropdown.OptionData("Тайшет"));
        DropdownCompany.options.Add(new Dropdown.OptionData("Тестовая"));
        DropdownCompany.options.Add(new Dropdown.OptionData("Братское"));

        //Client.LocationsRequest();
    }    

    public static int active_location = -1;
    public static int active_company = -1;
    // Update is called once per frame
    void Update()
    {
        if (load_location)
        {
            load_location = false;
            foreach (var l in locations)
                DropdownLocation.options.Add(new Dropdown.OptionData($"{l.id} {l.name}"));
            DropdownLocation.value = 1;
        }


        if (DropdownMode.value != mode)
            СhangeMode();

        if (active_company != DropdownCompany.value && AuthorizationController.success)
            ChangeCompany();

        time_draw += Time.deltaTime;
        if (time_draw >= interpolationPeriod)
        {
            MarkerMove();
            time_draw -= interpolationPeriod;
        }

        if (active_mode == Mode.RealTime && !starting)
        {
            foreach (var m in markers)
            {
                if (m.toggle.isOn)
                {
                    client.CoordinatesRequest(0, 0, 1, (uint)locations[active_location].id, m.acc_id);
                    end_send[m.acc_id] = false;
                }
            }
            starting = true;
        }

        if (ToggleLine.isOn)
        {
            foreach (var m in markers)
                m.marker_line.gameObject.SetActive(true);
            ToggleDisappearance.interactable = true;
        }
        else
        {
            foreach (var m in markers)
            {
                m.marker_line.gameObject.SetActive(false);
                m.marker_line.positionCount = 0;
            }
            ToggleDisappearance.interactable = false;
        }

        if (projection.isOn)
        {
            camera.orthographic = true;
        }
        else
        {
            camera.orthographic = false;
        }

        if (users_load)
        {
            users_load = false;
            foreach (var u in users.OrderBy(u => u.id))
                WorkerMarker(u, Color.green);
        }

        LoadMaps(DropdownLocation, Locations);

        //if (locations.Any() && AuthorizationController.success)
        //{
        //    active_location = DropdownLocation.value;
        //    LoadLocation(locations[active_location]);
        //}
    }


    /// <summary>
    /// Создание маркера для сотрудника
    /// </summary>
    /// <param name="acc_id"></param>
    void WorkerMarker(User user, Color color)
    {
        var toggle = Instantiate(Resources.Load("GameObjects/Toggle", typeof(Toggle))) as Toggle;
        toggle.name = $"{user.id}";
        toggle.transform.GetChild(1).gameObject.GetComponent<Text>().text = $"{user.id} {user.name}";
        toggle.transform.SetParent(WorkersList.transform);
        toggle.isOn = false;

        var marker = Instantiate(Resources.Load("GameObjects/Capsule", typeof(GameObject))) as GameObject;
        marker.name = $"marker_{user.id}";
        marker.transform.GetChild(0).transform.GetChild(0).transform.GetChild(0).GetComponent<Text>().text = $"{user.id}";
        marker.transform.SetParent(GameObject.Find("Markers").transform);
        marker.GetComponent<Renderer>().material.color = color; // UnityEngine.Random.ColorHSV(0f, 1f, 1f, 1f, 0.5f, 1f);

        var marker_line = Instantiate(Resources.Load("GameObjects/Line", typeof(LineRenderer))) as LineRenderer;
        marker_line.name = $"marker_line_{user.id}";
        marker_line.startColor = color;
        marker_line.endColor = Color.white;
        marker_line.material.color = color;
        marker_line.transform.SetParent(GameObject.Find("Markers").transform);

        markers.Add(new Marker { acc_id = user.id, marker = marker, toggle = toggle, marker_line = marker_line });
    }

    /// <summary>
    /// Смена положения маркера
    /// </summary>
    /// <param name="step">Шаг до нового положения</param>
    /// <param name="start_pos">Начальное положение</param>
    /// <param name="end_pos">Финальное положение</param>
    /// <param name="marker">Маркер сотрудника</param>
    /// <param name="w">Данные из БД</param>
    /// <param name="progress">Номер записи из БД</param>
    /// <returns></returns>
    void Position(float step, Vector3 start_pos, Vector3 end_pos, Marker marker, Structure w, string progress)
    {
        if (marker.marker.activeSelf == true)
        {
            var x_position = Mathf.Lerp(start_pos.x, end_pos.x, step);
            var y_position = Mathf.Lerp(start_pos.z, end_pos.z, step);
            //Debug.Log($"marker name={worker_marker.name} time={pause} step={step} sec x={x_position} y={y_position} x0={start_pos.x} y0={start_pos.z} x1={end_pos.x} y1={end_pos.z}");
            var new_position = new Vector3(x_position, 0.5f, y_position);
            if (marker.marker.transform.position != new_position)
            {
                marker.marker.transform.position = new Vector3(x_position, 0.5f, y_position);
                marker.marker_line.SetPosition(marker.marker_line.positionCount++, new Vector3(x_position, 0, y_position));
                if (ToggleDisappearance.isOn) marker.marker_line = LineDisappearance(marker.marker_line);
            }
            marker.marker.transform.GetChild(0).transform.GetChild(0).transform.GetChild(0).GetComponent<Text>().text = $"{w.acc_id} x={end_pos.x} y={end_pos.z}";
            marker.toggle.transform.GetChild(1).gameObject.GetComponent<Text>().text = $"{w.acc_id} {w.ts:hh:mm:ss}{progress}";
        }
    }

    /// <summary>
    /// Запуск отрисовки
    /// </summary>
    public void ButtonStart()
    {
        if (active_mode == Mode.Stop)
            foreach (var m in markers)
                m.marker_line.positionCount = 0;

        var button_text = StartStopButton.transform.GetChild(0);
        switch (DropdownMode.value)
        {
            case 0:
                //real_time = !real_time;
                if (active_mode == Mode.Stop/*real_time*/)
                {
                    active_mode = Mode.RealTime;
                    button_text.GetComponent<Text>().text = "Остановить";
                }
                else
                {
                    active_mode = Mode.Stop;
                    button_text.GetComponent<Text>().text = "Отобразить";
                }
                break;
            case 1:
                if (int.Parse(StartHour.GetComponent<InputField>().text) == 99) SceneManager.LoadScene("Editor3D");
                else
                {
                    if (active_mode == Mode.Stop/*real_time*/) active_mode = Mode.History;
                    else active_mode = Mode.Stop;
                }//history_start = !history_start;
                if (active_mode == Mode.History/*history_start*/)
                {
                    starting = true;
                    var date = Date.Date;

                    var time_start = date; //new DateTime(dates.ElementAt(DropdownData.value).Ticks);
                    time_start = time_start.AddHours(int.Parse(StartHour.GetComponent<InputField>().text));
                    time_start = time_start.AddMinutes(int.Parse(StartMin.GetComponent<InputField>().text));
                    time_start = time_start.AddSeconds(int.Parse(StartSec.GetComponent<InputField>().text));
                    Debug.Log(time_start);
                    var time_end = date; //new DateTime(dates.ElementAt(DropdownData.value).Ticks);
                    time_end = time_end.AddHours(int.Parse(EndHour.GetComponent<InputField>().text));
                    time_end = time_end.AddMinutes(int.Parse(EndMin.GetComponent<InputField>().text));
                    time_end = time_end.AddSeconds(int.Parse(EndSec.GetComponent<InputField>().text));
                    foreach (var m in markers)
                    {
                        if (m.toggle.isOn)
                        {
                            client.CoordinatesRequest(time_start.Ticks, time_end.Ticks, 1, (uint)locations[active_location].id, m.acc_id);
                            end_send[m.acc_id] = false;
                        }
                    }
                    button_text.GetComponent<Text>().text = "Остановить";
                }
                else
                {
                    StopProgress();
                }
                break;
        }
    }

    public void ErrorDialogClose()
    {
        ErrorDialog.SetActive(false);
    }

    public void StopProgress()
    {
        starting = false;
        StartStopButton.transform.GetChild(0).GetComponent<Text>().text = "Отобразить";
        active_mode = Mode.Stop;
        foreach (var m in markers)
        {
            end_send[m.acc_id] = false;
            moving.Remove(m.acc_id);
        }
    }

    /// <summary>
    /// Затухание траектории
    /// </summary>
    /// <param name="lineRenderer">Траектория</param>
    /// <returns></returns>
    LineRenderer LineDisappearance(LineRenderer lineRenderer)
    {
        //LineRenderer lineRenderer = GetComponent<LineRenderer>();
        int newPositionCount = lineRenderer.positionCount - 1;
        Vector3[] newPositions = new Vector3[newPositionCount];

        for (int i = 0; i < newPositionCount; i++)
        {
            newPositions[i] = lineRenderer.GetPosition(i + 1);
        }

        lineRenderer.SetPositions(newPositions);
        return lineRenderer;
    }

    /// <summary>
    /// Смена положения по интервалу
    /// Шаги для плавного перемещения
    /// </summary>
    /// <param name="m">Данные о маркере</param>
    void StartingAccPositiong(Marker m)
    {
        int i = 0;
        float step = 0.1f;
        if (moving.ContainsKey(m.acc_id))
        {
            if (moving[m.acc_id].index < Workers[m.acc_id].Count) i = moving[m.acc_id].index;
            if (moving[m.acc_id].step < 1.1f) step = moving[m.acc_id].step;
        }
        else moving[m.acc_id] = new MarkerMoving();

        var worker = Workers[m.acc_id].OrderBy(wr => wr.ts).ToList();
        if (worker.Any())
        {
            var w = worker.ElementAt(i);
            var start_pos = m.marker.transform.position;
            if (i != 0) start_pos = new Vector3(worker.ElementAt(i - 1).coord_x / 100f, 0.5f, worker.ElementAt(i - 1).coord_y / 100f);
            if (active_mode == Mode.History && i == 0) start_pos = new Vector3(w.coord_x / 100f, 0.5f, w.coord_y / 100f);

            var end_pos = new Vector3(w.coord_x / 100f, 0.5f, w.coord_y / 100f);

            var progress = "";
            if (active_mode == Mode.History) progress = $"\n{i + 1} из {Workers[m.acc_id].Count}";
            if (starting) Position(step, start_pos, end_pos, m, w, progress);
            // StartingAccPositiong(m.value, time_step);

        }

        if (step >= 1) i++;
        step += interpolationPeriod; // 0.1f;
        moving[m.acc_id].index = i;
        moving[m.acc_id].step = step;
    }

    /// <summary>
    /// Отображение маяков
    /// </summary>
    /// <param name="b"></param>
    public static void AddBeacon(Beacon b)
    {
        var beacon = Instantiate(Resources.Load("GameObjects/Beacon", typeof(GameObject))) as GameObject;
        beacon.transform.position = new Vector3(b.x / 100, 0.5f, b.y / 100);
        beacon.name = $"BeaconButton_{b.id}";
        beacon.GetComponent<BeaconController>().info = false;
        beacon.transform.SetParent(GameObject.Find("Beacons").transform);
        beacon.transform.GetChild(0).GetChild(0).GetChild(0).GetComponent<Text>().text = $"{b.id}\nuuid={b.uuid}\nmajor={b.major}\nminor={b.minor}";

        b.beacon = beacon;
    }

    void OnGUI()
    {
        //if (Input.GetKeyDown(KeyCode.BackQuote)) 
        DebugHelper.DrawConsole();
    }

    public static void LoadMaps(Dropdown DropdownLocation, GameObject Locations)
    {
        if (active_location != DropdownLocation.value && AuthorizationController.success && locations.Any())
        {
            if (active_location >= 0) Locations.transform.GetChild(active_location).gameObject.SetActive(false);
            Locations.transform.GetChild(DropdownLocation.value).gameObject.SetActive(true);
            active_location = DropdownLocation.value;

            //if (locations[active_location].walls == null) locations[active_location].Load();

            foreach (var b in Beacons)
            {
                Destroy(b.beacon);
                Destroy(b.panel_button);
            }
            Beacons = new List<Beacon>();

            if (ModeController.editor == true && DropdownLocation.value != DropdownLocation.options.Count - 1)
                Client.BeaconsRequest((uint)locations[DropdownLocation.value].id);
            if (ModeController.editor == false)
                Client.BeaconsRequest((uint)locations[DropdownLocation.value].id);
        }

        if (beacons_load)
        {
            beacons_load = false;
            foreach (var b in Beacons)
            {
                //if (ModeController.editor == false) AddBeacon(b);
                //if (ModeController.editor == true) 
                editor.AddBeacon(b);
            }
        }
    }

    /// <summary>
    /// Остановка отрисовки при смене режима история/онлайн
    /// </summary>
    public void СhangeMode()
    {
        StopProgress();

        active_mode = Mode.Stop;
        mode = DropdownMode.value;
        switch (DropdownMode.value)
        {
            case 0:
                DatePicker.SetActive(false);
                TimeStart.SetActive(false);
                TimeEnd.SetActive(false);
                break;
            case 1:
                DatePicker.SetActive(true);
                TimeStart.SetActive(true);
                TimeEnd.SetActive(true);
                break;
        }
    }

    /// <summary>
    /// Удаление сотрудников из-за смены компании
    /// </summary>
    public void ChangeCompany()
    {
        active_company = DropdownCompany.value;
        foreach (var m in markers)
        {
            Destroy(m.marker.gameObject);
            Destroy(m.marker_line.gameObject);
            Destroy(m.toggle.gameObject);
        }
        markers = new List<Marker>();
        client.SendGetUsers((uint)active_company + 1);
    }

    /// <summary>
    /// Смена позиции маркера
    /// </summary>
    public void MarkerMove()
    {
        foreach (var m in markers.Select((value, i) => (value, i)))
        {
            m.value.marker.SetActive(m.value.toggle.isOn);
            if (m.value.toggle.isOn && Workers.ContainsKey(m.value.acc_id) && starting && end_send[m.value.acc_id])
            {
                //end_send[m.value.acc_id] = false;
                // moving.Add(true);

                //if (markers.Count - 1 == m.i)                   

                StartingAccPositiong(m.value);

                var temp = Workers.Values.Max(v => v.Count);
                if (active_mode == Mode.History && m.i == temp - 1 && moving[moving.Keys.Last()].step >= 1f)
                {
                    StopProgress();
                }
                if (active_mode == Mode.RealTime && moving[moving.Keys.Last()].index == 1)
                {
                    starting = false;
                }
            }
        }
    }

    /// <summary>
    /// Загрузка объектов локации
    /// </summary>
    /// <param name="location"></param>
    public void LoadLocation(Location location)
    {
        if (active_location != DropdownLocation.value)
        {
            var Location = new GameObject();
            Location.name = location.name;
            Location.transform.SetParent(Locations.transform);

            location.Load();
        }

        if (load_location_elements[location.id].walls)
        {
            var walls = new GameObject();
            walls.name = "Walls";
            walls.transform.SetParent(Location.transform);

            load_location_elements[location.id].walls = false;
            foreach (var w in location.walls)
            {
                var wall = WallCreate.AddWall(new Vector3(w.start_height, WallCreate.WallHeight / 2, w.start_width), new Vector3(w.end_height, WallCreate.WallHeight, w.end_width));
                wall.transform.SetParent(walls.transform);
            }
        }
        if (load_location_elements[location.id].beacons)
        {
            var beacons = new GameObject();
            beacons.name = "Beacons";
            beacons.transform.SetParent(Location.transform);

            load_location_elements[location.id].walls = false;

        }
        if (load_location_elements[location.id].zones)
        {
            var zones = new GameObject();
            zones.name = "Zones";
            zones.transform.SetParent(Location.transform);

            load_location_elements[location.id].walls = false;
            WallCreate.AddZone(Dialog, gameObject, ZonesScroll);
        }
    }
}

