using DataClient.Struct;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using NLog;

namespace DataClient
{
  public static class ByteConverter
  {
    private static Logger log = LogManager.GetCurrentClassLogger();
    public static TechCycle[] BytesToTechcycles(byte[] arr)
    {
      var res = new List<TechCycle>();
      try
      {
        var str = new List<byte>();
        for (var i = 0; i < arr.Length; i++)
          if (arr[i] == 0x0a)
          {
            res.Add(BytesToTechcycle(str.ToArray()));
            str.Clear();
          }
          else
            str.Add(arr[i]);
      } catch { throw; }
      return res.ToArray();
    }
    public static TechCycle BytesToTechcycle(byte[] arr)
    {
      Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
      var enc = Encoding.GetEncoding(866);
      var exc = new ArgumentException("Wrong structure in byte array.");
      var flag = 0;
      var str = new List<byte>();
      var res = new TechCycle();
      try
      {
        for (var i = 0; i < arr.Length; i++)
          if (arr[i] == 0x09)
            switch (flag)
            {
              case 0:
                var idxStr = enc.GetString(str.ToArray());
                if (idxStr == "00") res.Operation = TechCycle.Oper.end_cycle_manual;
                else if (int.TryParse(idxStr, out int idx)) res.index = (sbyte?)idx;
                str.Clear();
                flag++;
                break;
              case 1:
                var timestr = enc.GetString(str.ToArray());
                var rgx = new Regex(@"^([0-1]\d{1}|[2][0-3]):([0-5]\d{1}):([0-5]\d{1})$");
                if (!rgx.IsMatch(timestr)) throw exc;
                str.Clear();
                flag++;
                break;
              default: throw exc;
            }
          else str.Add(arr[i]);
        if (flag != 2) throw exc;
        var unixTimeStr = enc.GetString(str.ToArray());
        if (!int.TryParse(unixTimeStr, out int unixTime)) throw exc;
        res.start = (new DateTime(1970, 1, 1, 0, 0, 0, 0)).AddSeconds(unixTime).ToLocalTime();
        return res;
      }
      catch { throw; }
    }
    public static (DateTime date, byte?[] bytes)[] BytesToTimeLine(byte[] arr, DateTime date)
    {
      var res = new List<(DateTime date, byte?[] bytes)>();
      var matrix = new List<byte?>();
      var currdate = new DateTime(date.Year, date.Month, date.Day, 0, 0, 0);
      var cursor = 0;
      var halfseconds = 0;
      try
      {
        while (cursor < arr.Length)
        {
          switch (arr[cursor])
          {
            case 0xfb:
              halfseconds = BitConverter.ToInt32(arr, ++cursor);
              cursor += 4;
              break;
            case 0xfc:
              for (var i = 0; i < matrix.Count; i++) matrix[i] = null;
              res.Add((currdate.AddSeconds(++halfseconds / 2D), matrix.ToArray()));
              halfseconds = BitConverter.ToInt32(arr, ++cursor);
              cursor += 4;
              break;
            case 0xfe:
              halfseconds += BitConverter.ToInt16(arr, ++cursor);
              cursor += 2;
              break;
            case 0xff:
              res.Add((currdate.AddSeconds(halfseconds / 2D), matrix.ToArray()));
              cursor++;
              halfseconds++;
              break;
            default:
              while (matrix.Count < arr[cursor]) matrix.Add(null);
              matrix[arr[cursor]] = arr[cursor + 1];
              cursor += 2;
              break;
          }
        }
      }
			catch(Exception e)
			{
        log.Warn(e, "Error in structure AD file.");
        return res.ToArray();
      }
      return res.ToArray();
    }

  }
}
