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

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

    public GameObject WorkersList; // scroll content

    public GameObject DateTimePanel;
    public GameObject StartHour;
    public GameObject StartMin;
    public GameObject StartSec;
    public GameObject EndHour;
    public GameObject EndMin;
    public GameObject EndSec;
    public GameObject StartStopButton;
    public LocationZones locationZones;
    public Dropdown DropdownMode;
    public Toggle ToggleLine;
    public Toggle ToggleDisappearance;
    Toggle ToggleCoordinate;

    public GameObject DatePicker;
    Camera camera;

    public GameObject UserInfo;

    public GameObject Editor;

    public uint? user_broadcast = null;
    public bool broadcast = false;
    public Dictionary<uint, User> users;
    public enum Mode { RealTime = 0, History = 1, Stop = 2 };
    public Mode active_mode = Mode.Stop;
    //int mode = 0;

    Client client;
    CompanyController company;
    EditorController editor;

    DateTimePicker Date;

    public Dictionary<uint, bool> end_send = new Dictionary<uint, bool>(); // флаги завершения загрузки
    Dictionary<uint, MarkerMoving> moving = new Dictionary<uint, MarkerMoving>(); // флаги начала движения

    const float INTERPOLATION_PERIOD = 0.1f;
    float time_draw = 0;
    public bool users_load = false; // ожидание массива пользователей
    public bool beacons_load = false;


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

    private void Awake()
    {
        instance = this;
    }

    // Start is called before the first frame update
    void Start()
    {
        DebugHelper.ActivateConsole();
        client = Client.instance;
        company = CompanyController.instance;
        editor = Editor.GetComponent<EditorController>();
        camera = Camera.main;
        //users = new Dictionary<uint, User>();

        Date = DatePicker.GetComponent<DateTimePicker>();
        //Client.LocationsRequest();

        var broadcast_stop = UserInfo.transform.GetChild(1).GetChild(1).GetComponent<Button>();
        broadcast_stop.onClick.AddListener(() => BroadcastStop());

        ToggleCoordinate = GameObject.Find("ToggleCoordinate").GetComponent<Toggle>();
    }

    // Update is called once per frame
    void Update()
    {
        //if (DropdownMode.value != mode)
        //    СhangeMode();

        DropdownMode.onValueChanged.AddListener(delegate
        {
            СhangeMode();
        });

        if (active_mode != Mode.Stop)
        {
            time_draw += Time.deltaTime;
            if (time_draw >= INTERPOLATION_PERIOD)
            {
                MarkerMove();
                time_draw -= INTERPOLATION_PERIOD;
            }
        }

        if (users != null)
        {
            if (users_load == false)
            {
                foreach (var u in users.Values)
                {
                    if (u.marker_line != null)
                    {
                        u.marker_line.gameObject.SetActive(ToggleLine.isOn);
                        if (ToggleLine.isOn == false) u.marker_line.positionCount = 0;
                    }
                }
            }  

            if (users_load)
            {
                users_load = false;
                foreach (var u in users.OrderBy(u => u.Value.id))
                {
                    u.Value.SetParam(BroadcastStart);

                    //if (DropdownMode.value == 0) u.Value.toggle_user.SetActive(u.Value.online);
                    //else u.Value.toggle_user.SetActive(true);
                }
            }
        }

        ToggleDisappearance.interactable = ToggleLine.isOn;
    }

    /// <summary>
    /// Смена положения маркера
    /// </summary>
    /// <param name="step">Шаг до нового положения</param>
    /// <param name="start_pos">Начальное положение</param>
    /// <param name="end_pos">Финальное положение</param>
    /// <param name="user">Маркер сотрудника</param>
    /// <param name="w">Данные из БД</param>
    /// <param name="progress">Номер записи из БД</param>
    /// <returns></returns>
    void Position(float step, Vector3 start_pos, Vector3 end_pos, User user, Structure w, string progress)
    {
        Debug.LogWarning($"move user {user.id} {end_pos} {w.ts} {w.zone_id}");
        if (user.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 (user.marker.transform.position != new_position)
            {
                user.marker.transform.position = new Vector3(x_position, 0.5f, y_position);
                user.marker_line.SetPosition(user.marker_line.positionCount++, new Vector3(x_position, 0, y_position));
                if (ToggleDisappearance.isOn) user.marker_line = LineDisappearance(user.marker_line);
            }
            var t = user.marker.transform.GetChild(0).transform.GetChild(0).transform.GetChild(0).GetComponent<Text>();
            if (ToggleCoordinate.isOn) t.text = $"{w.acc_id} x={end_pos.x} y={end_pos.z}";
            else t.text = $"{w.acc_id}\n{user.name_user}";
            user.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 u in users)
                u.Value.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 (active_mode == Mode.Stop/*real_time*/) active_mode = Mode.History;
                else active_mode = Mode.Stop;

                if (active_mode == Mode.History/*history_start*/)
                {
                    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 u in users)
                    {
                        if (u.Value.toggle.isOn)
                        {
                            client.CoordinatesRequest(time_start.Ticks, time_end.Ticks, 1, company.locations_index[company.active_location], u.Value.id);
                            end_send[u.Value.id] = false;
                        }
                    }
                    button_text.GetComponent<Text>().text = "Остановить";
                }
                else
                {
                    StopProgress();
                }
                break;
        }
    }

    public void StopProgress()
    {
        StartStopButton.transform.GetChild(0).GetComponent<Text>().text = "Отобразить";
        active_mode = Mode.Stop;
        if(users != null)
        foreach (var u in users)
        {
            end_send[u.Value.id] = false;
            moving.Remove(u.Value.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="u">Данные о маркере</param>
    void StartingAccPositiong(User u)
    {
        int i = 0;
        float step = 0.1f;
        if (moving.ContainsKey(u.id))
        {
            if (moving[u.id].index < Workers[u.id].Count) i = moving[u.id].index;
            if (moving[u.id].step < 1.1f) step = moving[u.id].step;
        }
        else moving[u.id] = new MarkerMoving();

        var worker = Workers[u.id];
        Debug.LogWarning($"worker coord count {u.id} {worker.Count}");      

        if (active_mode == Mode.RealTime) worker = worker.OrderBy(wr => wr.ts).ToList();
        if (worker.Any())
        {
            var w = worker[i];
           
            locationZones.UserPosition(u, w);           

            var start_pos = u.marker.transform.position;
            if (i != 0) start_pos = new Vector3(worker[i - 1].coord_x, 0.5f, worker[i - 1].coord_y);
            if (active_mode == Mode.History && i == 0) start_pos = new Vector3(w.coord_x, 0.5f, w.coord_y);

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

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

        }

        step += INTERPOLATION_PERIOD; // 0.1f;        
        moving[u.id].step = step;
        if (Math.Round(step, 1) > 1)
        {
            Debug.Log($"step:{step}");
            i++;
            moving[u.id].index = i;
        }

        if (active_mode == Mode.RealTime)
            if (Math.Round(step, 1) > 1)
            {
                Debug.Log($"Mode.RealTime step:{step}");
                Workers.Remove(u.id);
                Debug.LogWarning($"clear coord user {u.id}");
            }
        if (active_mode == Mode.History)
            if (i == Workers[u.id].Count)
            {
                //StopProgress();
                Workers.Remove(u.id);
            }
    }

    /// <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, 0.5f, b.y);
    //    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;
    //}   

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

        active_mode = Mode.Stop;
        foreach (var u in users) u.Value.UserCompany();
        //mode = DropdownMode.value;
        //switch (DropdownMode.value)
        //{            
        //    case 0:
        //        DateTimePanel.SetActive(false);
        //        if (users != null)
        //            foreach (var u in users)
        //            {
        //                if(!u.Value.online) u.Value.toggle.isOn = false;
        //                u.Value.toggle_user.SetActive(u.Value.online);
        //            }
        //        break;
        //    case 1:
        //        DateTimePanel.SetActive(true);
        //        if (users != null)
        //            foreach (var u in users)
        //            u.Value.toggle_user.SetActive(true);
        //        break;
        //}
    }

    /// <summary>
    /// Смена позиции маркера
    /// </summary>
    public void MarkerMove()
    {
        if(users != null)
        foreach (var u in users)
        {
            if (u.Value != null)
            {
                //if (u.Value.marker.activeSelf != u.Value.toggle.isOn) u.Value.marker.SetActive(u.Value.toggle.isOn);

                if (active_mode == Mode.RealTime && u.Value.toggle.isOn && !Workers.ContainsKey(u.Value.id))
                {
                    Debug.LogWarning($"send coord user {u.Value.id}");
                    var index = company.locations_index[company.active_location];
                    client.CoordinatesRequest(0, 0, 1, company.locations[index].id, u.Value.id);

                    u.Value.toggle_user.SetActive(u.Value.online);
                }

                if (u.Value.toggle.isOn && Workers.ContainsKey(u.Value.id) && active_mode != Mode.Stop/*&& starting*/ /*&& end_send[m.value.acc_id]*/)
                {
                    StartingAccPositiong(u.Value);

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

    public void BroadcastStart(uint id)
    {
        if (user_broadcast.HasValue)
        {
            client.ImageStreamStopSend(user_broadcast.Value);
            var camera_button = users[user_broadcast.Value].toggle_user.transform.GetChild(1).GetComponent<Button>();
            camera_button.GetComponent<Image>().color = Color.white;
            broadcast = false;
        }
        if (user_broadcast != id)
        {
            client.ImageStreamStartSend(id);
            user_broadcast = id;
            var name = UserInfo.transform.GetChild(1).GetChild(0).GetComponent<Text>();
            name.text = $"{id} {users[id].name_user}";
            name.GetComponent<RectTransform>().sizeDelta = new Vector2(name.preferredWidth, 30);

            var camera_button = users[id].toggle_user.transform.GetChild(1).GetComponent<Button>();
            camera_button.GetComponent<Image>().color = Color.green;

            UserInfo.SetActive(false);
            broadcast = true;
        }
        else
        {
            broadcast = false;
            user_broadcast = null;
            UserInfo.SetActive(false);
        }
    }

    public void BroadcastStop()
    {
        if (user_broadcast.HasValue)
        {
            client.ImageStreamStopSend(user_broadcast.Value);
            var camera_button = users[user_broadcast.Value].toggle_user.transform.GetChild(1).GetComponent<Button>();
            camera_button.GetComponent<Image>().color = Color.white;
        }
        broadcast = false;
        user_broadcast = null;
        UserInfo.SetActive(false);
    }
}