using System;
using System.Collections.Generic;
using System.IO;
using common;
using TMPro;
using UnityEngine;
using UnityGoDiceInterface;

public class DiceConfigurationPanelController : MonoBehaviour {
    private DiceInterface _diceInterface;

    public EventBasedTable diceRollGroup;
    public PickerRowController pickerRowPrefab;
    public TMP_Text instructionsLabel;

    public delegate void ConfigurationCallback(DieInfo? tensDieInfo, DieInfo? onesDieInfo);

    private ConfigurationCallback _configurationCallback;

    private DieInfo? _tensDieInfo = null;
    private DieInfo? _onesDieInfo = null;

    private List<DieInfo> _knownDice = new List<DieInfo>();
    private HashSet<string> _connectingIdentifiers = new HashSet<string>();

    void Awake() {}

    public void GetConfiguration(ConfigurationCallback cb) {
        if (_diceInterface == null) { _diceInterface = GetComponentInParent<DiceInterface>(); }
        _diceInterface.deviceFoundCallback = DeviceFoundCallback;
        _diceInterface.connectionCallback = ConnectionCallback;
        _diceInterface.connectionFailedCallback = ConnectionFailedCallback;
        _diceInterface.disconnectionCallback = DisconnectionCallback;
        _diceInterface.listenerStoppedCallback = ListenerStoppedCallback;
        _diceInterface.colorCallback = ReceiveColorCallback;

        string currentPath = NewLogLocation();
        Directory.CreateDirectory(Path.GetDirectoryName(currentPath));
        _diceInterface.logger = log => {
            MainQueue.Q.Enqueue(() => {
                Debug.Log(log);
                File.AppendAllText(currentPath, log);
            });
        };

        _diceInterface.StartListening();

        _configurationCallback = cb;
        _tensDieInfo = null;
        _onesDieInfo = null;
        instructionsLabel.text = "Looking for dice...";
        gameObject.SetActive(true);
    }

    string NewLogLocation() {
        return Path.Combine(
                Application.persistentDataPath,
                "eagle0",
                "Resources",
                "DiceConfigurationLogs",
                DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + ".log");
    }

    public void RowClicked(int rowIndex) {
        if (_tensDieInfo == null) {
            _tensDieInfo = _knownDice[rowIndex];
            _knownDice.RemoveAt(rowIndex);
            SetUpTable();
        } else {
            _diceInterface.StopListening();

            _onesDieInfo = _knownDice[rowIndex];
            _knownDice.RemoveAt(rowIndex);
            _knownDice.ForEach(dieInfo => _diceInterface.Disconnect(dieInfo.identifier));

            // _diceInterface.deviceFoundCallback = null;
            // _diceInterface.connectionCallback = null;
            // _diceInterface.disconnectionCallback = null;
            // _diceInterface.listenerStoppedCallback = null;
            // _diceInterface.rollCallback = null;
            // _diceInterface.colorCallback = null;

            _configurationCallback(_tensDieInfo, _onesDieInfo);
            gameObject.SetActive(false);
        }
    }

    private void OnEnable() {}

    private void OnDisable() { diceRollGroup.RowCount = 0; }

    void ListenerStoppedCallback() { Debug.Log("Listener stopped!"); }

    void SetUpTable() {
        diceRollGroup.RowCount = _knownDice.Count;

        if (_knownDice.Count == 0) {
            instructionsLabel.text = "Looking for dice...";
        } else if (_tensDieInfo == null) {
            instructionsLabel.text = "Select a TENS die:";
        } else {
            instructionsLabel.text = "Select an ONES die:";
        }

        for (int i = 0; i < _knownDice.Count; i++) {
            var row = diceRollGroup.ComponentAt<PickerRowController>(i);

            row.Text = _knownDice[i].deviceName;
            row.TextColor = UnityDieColors.ColorFromDieColor(_knownDice[i].color);
        }
    }

    void ReceiveColorCallback(string identifier, DieColor dieColor) {
        MainQueue.Q.Enqueue(() => {
            var existingIndex =
                    _knownDice.FindIndex(dieInfo => dieInfo.identifier.Equals(identifier));
            if (existingIndex >= 0) {
                var colorName = dieColor.ToString();
                _knownDice[existingIndex] = new DieInfo(
                        identifier: _knownDice[existingIndex].identifier,
                        deviceName: colorName,
                        color: dieColor,
                        connected: true);
            } else {
                Debug.Log("This shouldn't happen");
            }

            SetUpTable();
        });
    }

    void DeviceFoundCallback(string identifier) {
        MainQueue.Q.Enqueue(() => {
            if (_connectingIdentifiers.Contains(identifier)) { return; }

            _connectingIdentifiers.Add(identifier);

            var existingIndex =
                    _knownDice.FindIndex(dieInfo => dieInfo.identifier.Equals(identifier));
            if (existingIndex >= 0) {
                _knownDice[existingIndex] =
                        new DieInfo(identifier, "Unknown", DieColor.DieColorBlack);
            } else {
                _knownDice.Add(new DieInfo(identifier, "Unknown", DieColor.DieColorBlack));
            }

            SetUpTable();

            _diceInterface.Connect(identifier);
        });
    }

    void ConnectionCallback(string identifier, string deviceName) {
        MainQueue.Q.Enqueue(() => {
            _connectingIdentifiers.Remove(identifier);

            if (_tensDieInfo != null && _tensDieInfo.Value.identifier.Equals(identifier)) {
                return;
            }
            if (_onesDieInfo != null && _onesDieInfo.Value.identifier.Equals(identifier)) {
                return;
            }

            var shortenedName = deviceName.Split('_')[1];
            var existingIndex =
                    _knownDice.FindIndex(dieInfo => dieInfo.identifier.Equals(identifier));
            if (existingIndex >= 0) {
                _knownDice[existingIndex] =
                        new DieInfo(identifier, shortenedName, DieColor.DieColorBlack);
            } else {
                _knownDice.Add(new DieInfo(identifier, shortenedName, DieColor.DieColorBlack));
            }

            SetUpTable();

            _diceInterface.RequestColor(identifier);
        });
    }

    void ConnectionFailedCallback(string identifier) {
        MainQueue.Q.Enqueue(() => {
            _connectingIdentifiers.Remove(identifier);

            var existingIndex =
                    _knownDice.FindIndex(dieInfo => dieInfo.identifier.Equals(identifier));
            if (existingIndex >= 0) {
                _knownDice.RemoveAt(existingIndex);

                SetUpTable();
            }
        });
    }

    void DisconnectionCallback(string identifier) {
        MainQueue.Q.Enqueue(() => {
            _connectingIdentifiers.Remove(identifier);

            if (_tensDieInfo is {} tensInfo && tensInfo.identifier.Equals(identifier)) {
                _tensDieInfo = null;
                _onesDieInfo = null;
            } else if (_onesDieInfo is {} onesInfo && onesInfo.identifier.Equals(identifier)) {
                _onesDieInfo = null;
            }

            var existingIndex =
                    _knownDice.FindIndex(dieInfo => dieInfo.identifier.Equals(identifier));
            if (existingIndex >= 0) { _knownDice.RemoveAt(existingIndex); }

            SetUpTable();
            _diceInterface.Connect(identifier);
        });
    }

    public void DismissButtonClicked() {
        gameObject.SetActive(false);
        _configurationCallback(null, null);
    }

    // Update is called once per frame
    void Update() {}
}
