using System;
using System.IO;
using System.Collections.Generic;
using NLog;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using DataClient.Struct;
using System.Net;

namespace DataClient
{
  /// <summary>
  /// Класс для связи с СТП. Содержит все инструменты, для получения данных.
  /// </summary>
  public class STPClient
  {
    //Переменные
    Logger log = LogManager.GetCurrentClassLogger();
    string confDir = Path.Combine(Directory.GetCurrentDirectory(), "Config", "config.json");
    private List<server> servers = new List<server>();
    int serverDefault = -1;
    //Геттеры и сеттеры
    /// <summary>Номер сервера, используемого по умолчанию.</summary>
    public int UseServer
		{
      get { return serverDefault; }
      set {
        serverDefault =
          (servers.Count == 0) ? -1 :
          (value < 0) ? 0 :
          (value >= servers.Count) ? serverDefault = servers.Count - 1 :
          value;
      }
		}
    /// <summary>Массив серверов, инициализированных в экземпляре класса.</summary>
    public server[] ListServers { get { return servers.ToArray(); } }

    //Функции
    /// <summary>Получение номера сервера по его названию.</summary>
    /// <param name="name">Название сервера.</param>
    /// <returns>Номер сервера в списке серверов.</returns>
    public int GetServerIdByName(string name)
		{
      return servers.FindIndex(x => x.name == name);
		}
    /// <summary>Получение название сервера по его номеру.</summary>
    /// <param name="id">Номер сервера в списке серверов.</param>
    /// <returns>Название сервера.</returns>
    public string GetServerNameById(int id)
    {
      var res = (id >= servers.Count || id < 0) ? null : servers[id].name;
      return res;
    }
    /// <summary>Проверка параметров серевера перед их добавлением в список.</summary>
    /// <param name="ip">IP адресс сервера.</param>
    /// <param name="port">Порт сервера.</param>
    /// <param name="dir">Путь к архиву.</param>
    /// <returns>Результат выполнения функции.</returns>
    private bool CheckDataServer(string ip, int port, string dir)
    {
      if (!IPAddress.TryParse(ip, out _))
      {
        log.Warn("Ip address incorrect: " + ip + " .");
        return false;
      }
      if (port < 1 && port > 65535)
      {
        log.Warn("Port incorrect: " + port.ToString() + ".");
        return false;
      }
      if (!Directory.Exists(dir))
      {
        log.Warn("Directory incorrect or not exist: " + dir + " .");
        return false;
      }
      return true;
    }
    /// <summary>Добавление сервера в список серверов.</summary>
    /// <param name="name">Название сервера</param>
    /// <param name="ip">IP адресс сервера.</param>
    /// <param name="port">Порт сервера.</param>
    /// <param name="dir">Путь к архиву.</param>
    /// <returns>Результат выполнения функции.</returns>
    public bool AddServer(string name, string ip, int port, string dir)
    {
      if (!CheckDataServer(ip, port, dir))
      {
        log.Warn("Server not added: Data incorrect.");
        return false;
      }
      if (string.IsNullOrEmpty(name))
      {
        log.Warn("Server not added: Incorrect name server.");
        return false;
      }
      if (servers.FindIndex(x => x.name == name) != -1)
      {
        log.Warn("Server not added: Server with this name exist.");
        return false;
      }
      servers.Add(new server(name, ip, port, dir));
      UseServer = 0;
      log.Trace("Server (name=" + name + ";ip=" + ip + ";port=" + port + ";dir=" + dir + ") added.");
      return true;
    }
    /// <summary>Изменение сервера в списке серверов по его номеру.</summary>
    /// <param name="id">Номер сервера в списке серверов.</param>
    /// <param name="name">Название сервера.</param>
    /// <param name="ip">IP адресс сервера.</param>
    /// <param name="port">Порт сервера.</param>
    /// <param name="dir">Путь к архиву.</param>
    /// <returns>Результат выполнения функции.</returns>
    public bool ChangeServerById(int id, string name, string ip, int port, string dir)
    {
      if (servers.Count == 0 || id >= servers.Count || id < 0)
			{
        log.Warn("Server not changed: Id incorrect.");
        return false;
      }
      if (!CheckDataServer(ip, port, dir))
      {
        log.Warn("Server not changed: Data incorrect.");
        return false;
      }
      var tmpId = servers.FindIndex(x => x.name == name);
      if (string.IsNullOrEmpty(name) || (tmpId != -1 && tmpId != id))
			{
        log.Warn("Server not changed: Name incorrect.");
        return false;
      }
      var tmp = new server(name, ip, port, dir);
      log.Trace("Server id=" + id + " changed: name=" + name + ";ip=" + ip + ";port=" + port.ToString() + ";dir=" + dir + ";.");
      servers[id] = tmp;
      return true;
    }
    /// <summary>Изменение сервера в списке серверов по его названию.</summary>
    /// <param name="name">Название сервера.</param>
    /// <param name="ip">IP адресс сервера.</param>
    /// <param name="port">Порт сервера.</param>
    /// <param name="dir">Путь к архиву.</param>
    /// <returns>Результат выполнения функции.</returns>
    public bool ChangeServerByName(string name, string ip, int port, string dir)
    {
      var id = servers.FindIndex(x => x.name == name);
      if (id == -1)
      {
        log.Warn("Server not changed: Name incorrect or server not exist.");
        return false;
      }
      return ChangeServerById(id, name, ip, port, dir);
    }
    /// <summary>Удаление сервера из списка серверов по его номеру.</summary>
    /// <param name="id">Номер сервера в списке серверов.</param>
    /// <returns>Результат выполнения функции.</returns>
    public bool RemoveServerById(int id)
		{
      if (servers.Count == 0 || id >= servers.Count || id < 0)
      {
        log.Warn("Server not removed: Id incorrect or list server are empty.");
        return false;
      }
      log.Trace("Server id=" + id.ToString() + ";name=" + servers[id].name + ";ip=" + servers[id].ip + ";port=" + servers[id].port.ToString() + ";dir=" + servers[id].dir + "; removed.");
      servers.RemoveAt(id);
      UseServer = 0;
      return true;
    }
    /// <summary>Удаление сервера из списка серверов по его названию.</summary>
    /// <param name="name">Название сервера.</param>
    /// <returns>Результат выполнения функции.</returns>
    public bool RemoveServerByName(string name)
    {
      var id = servers.FindIndex(x => x.name == name);
      if (id == -1)
      {
        log.Warn("Server not removed: Name incorrect or server not exist.");
        return false;
      }
      return RemoveServerById(id);
    }
    /// <summary>Чтение конфигурационнного файла и установка параметров по умолчанию.</summary>
    private void Config()
    {
      string jsonString;
      try
      {
        jsonString = File.ReadAllText(confDir);
      }
      catch (Exception e)
      {
        log.Warn(e, "Can't read config file from " + confDir + ".");
        return;
      }
      var conf = (JObject)JsonConvert.DeserializeObject(jsonString);
      if (!conf.HasValues && !conf["servers"].HasValues && conf["servers"].Type != JTokenType.Array)
      {
        log.Warn("Doesn't exist \"servers\" object in config file.");
        return;
      }
      var count = -1;
      foreach (var s in conf["servers"])
      {
        count++;
        var name = (s["name"].Type == JTokenType.String) ? (string)s["name"] : "default";
        var ip = (s["ip"].Type == JTokenType.String) ? (string)s["ip"] : "";
        var port = (s["port"].Type == JTokenType.Integer) ? (int)s["port"] : -1;
        var dir = (s["dir"].Type == JTokenType.String) ? (string)s["dir"] : "";
        var res = false;
        if (GetServerIdByName(name) == -1)
          res = AddServer(name, ip, port, dir);
        else
          res = ChangeServerByName(name, ip, port, dir);
        if (!res)
          log.Trace("Can't add or change server #" + count + " from conf file.");
      }
    }
    /// <summary>Создание экземпляра класса.</summary>
    public STPClient()
    {
      log.Trace("Create instance class.");
      Config();
    }
    /// <summary>Создание экземпляра класса с указанием конфигурационного файла.</summary>
    /// <param name="confDir">Путь к файлу конфигурации.</param>
    public STPClient(string confDir)
    {
      log.Trace("Create instance class with dir:" + confDir + ".");
      this.confDir = confDir;
      Config();
    }




  }

}