using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;


namespace DataClients
{
	public class STPClient
	{
		private NetClient netClient;
		private FileClient fileClient;

		public STPClient()
		{
			netClient = new NetClient();
			fileClient = new FileClient();
		}
		public STPClient(NetClient nc, FileClient fc)
		{
			netClient = nc;
			fileClient = fc;
		}

		public bool CheckConnection()
		{
			var byteStr = netClient.SocketWork(NetClient.Cmd.version);
			if (byteStr.Length < 1) return false;
			Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
			var enc = Encoding.GetEncoding(866);
			var version = enc.GetString(byteStr[0]);
			if (version.Contains("STP TCP Server"))
				return true;
			return false; 
		}

		public Pasport GetPasport(string link)
		{
			var result = new Pasport();
			var arr = new List<byte>();
			var count_error = 0;
			var flag_err = false;
			do
			{
				flag_err = false;
				result = new Pasport();
				arr = new List<byte>();
				try
				{
					{
						var tmp = netClient.SocketWork(NetClient.Cmd.pasp_download, link);
						foreach (var e in tmp) arr.AddRange(e);
					}
					result.byteArr = arr.ToArray();
				}
				catch
				{
					flag_err = true;
					count_error++;
					Thread.Sleep(3000);
				}
			} while (flag_err && count_error < 3);
			return result;
		}
		public (string name, string dir)[] GetListPasport(DateTime date)
		{
			var result = new List<(string name, string dir)>();
			var str = date.ToString(@"yyyy\/MM\/dd");
			var e = netClient.SocketWork(NetClient.Cmd.dir_browse, str);

			Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
			var enc = Encoding.GetEncoding(866);
			foreach(var t in e)
			{
				var subres = "";
				var r1 = enc.GetString(t);
				var r2 = r1.Split('-');
				if (r2.Length > 1)
					for (var i = 1; i < r2.Length; i++) {
						if (subres.Length > 0) subres = subres + "-";
						subres = subres + r2[i].Split('.')[0];
					}

				if (!String.IsNullOrEmpty(subres))
					result.Add((subres, str + '/' + r1));
			}
			return result.ToArray();
		}
		public ADresult GetAnalogDiscret(DateTime start, DateTime end, ushort vdp)
		{
			var cursor = start;
			byte[] subRes = new byte[0];
			var resByteMatrix = new List<ByteMatrix>(); 
			while (cursor.AddDays(-1) <= end)
			{
				subRes = GetFile(cursor, vdp, 1);
				var arr = STPConverter.GetADByteMatrix(cursor, subRes);
				resByteMatrix.AddRange(arr);
				cursor = cursor.AddDays(1);
			}
			return STPConverter.AnalogDiscret(resByteMatrix,vdp);
		}
		public List<IshData> GetIshData(DateTime start, DateTime end, ushort vdp)
		{
			var result = new List<IshData>();
			var cursor = start.AddDays(-1);
			byte[] subRes = new byte[0];

			do
			{
				cursor = cursor.AddDays(1);
				subRes = GetFile(cursor, vdp, 0);
				var arr = STPConverter.IshData(subRes);
				foreach (var e in arr)
					if (start <= e.time && e.time <= end)
						result.Add(e);
			} while (cursor <= end);
			return result;
		}
		public List<TechCycle> GetTechCycle(DateTime start, DateTime end, ushort vdp)
		{
			var result = new List<TechCycle>();
			var cursor = start;
			byte[] subRes = new byte[0];
			TechCycle currTechCycle = new TechCycle()
			{
				start = start,
				index = TechCycle.Operation.unloading_loading
			};

			do
			{
				cursor = cursor.AddDays(-1);
				subRes = GetFile(cursor, vdp, 3);
			} while (subRes.Length == 0 && cursor > start.AddDays(-10));

			{
				if (subRes.Length > 0)
				{
					var b = STPConverter.TechCycle(subRes);
					if (b.Length > 0) currTechCycle = b.Last();
				}
			}

			do
			{
				cursor = cursor.AddDays(1);
				subRes = GetFile(cursor, vdp, 3);
				var arr = STPConverter.TechCycle(subRes);
				foreach(var e in arr)
				{
					if (e.start <= start)
					{ 
						currTechCycle = e;
						continue;
					}
					if (e.start >= end)
					{
						currTechCycle.end = e.start;
						result.Add(currTechCycle);
						currTechCycle = e;
						break;
					}
					if (e.start > start && e.start < end)
					{
						currTechCycle.end = e.start;
						if (result.Count > 0 && result[result.Count - 1].index == currTechCycle.index)
							result[result.Count - 1].end = currTechCycle.end;
						else
							result.Add(currTechCycle);
						currTechCycle = e;
					}
				}

			} while (cursor <= end);

			{
				if (result.Count == 0)
				{
					currTechCycle.start = start;
					currTechCycle.end = end;
					result.Add(currTechCycle);
				}
				else if (result[result.Count - 1].end < end)
				{
					currTechCycle.end = end;
					result.Add(currTechCycle);
				}
			}
			return result;
		}
		public List<Protect> GetProtectData(DateTime start, DateTime end, ushort vdp)
		{
			var result = new List<Protect>();
			var cursor = start.AddDays(-1);
			byte[] subRes = new byte[0];
			do
			{
				cursor = cursor.AddDays(1);
				subRes = GetFile(cursor, vdp, 4);

				var arr = STPConverter.ProtectData(subRes);
				foreach (var e in arr)
					if (start <= e.time && e.time <= end)
						result.Add(e);
			} while (cursor <= end);
			return result;
		}
		private byte[] GetFile(DateTime time, ushort vdp, ushort index)
		{
			var result = new List<byte>();
			var name = time.ToString("yyyyMMdd") + "." + vdp.ToString("D2") + index.ToString("D1");
			var temp = new List<byte>();
			temp.AddRange(fileClient.GetFile(time, vdp, index));
			if (temp.Count == 0)
			{
				var subTemp = netClient.SocketWork(NetClient.Cmd.download_nh, name);
				foreach (var e in subTemp)
					temp.AddRange(e);
			}
			return temp.ToArray();
		}
	}

	public static class STPConverter
	{
		public static IshData[] IshData(byte[] arr)
		{
			var result = new List<IshData>();
			try
			{
				Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
				var enc = Encoding.GetEncoding(866);
				var strs = enc.GetString(arr).Split('\n');
				foreach (var e in strs)
				{
					var substr = e.Split('\t');
					if (substr.Length < 3) continue;
					var time = Converter.ConvertUnixTimeToDateTime(Int32.Parse(substr[1]));
					for(var i = 2; i < substr.Length; i++)
					{
						if (String.IsNullOrEmpty(substr[i])) continue;
						var tmp = substr[i].Split(' ');
						var c = new IshData() { time = time, id = Convert.ToUInt16(tmp[0]) };
						var val = "";
						for (var j = 1; j < tmp.Length; j++)
							val = j < (tmp.Length - 1) ? val + tmp[j] + ' ' : val + tmp[j];
						c.value = val;
						result.Add(c);
					}
					
				}
				return result.ToArray();
			}
			catch (Exception e)
			{
				Console.WriteLine(e.Message);
				Console.WriteLine(e.StackTrace);
				return result.ToArray();
			}
		}
		public static TechCycle[] TechCycle(byte[] arr)
		{
			var result = new List<TechCycle>();
			try
			{
				Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
				var enc = Encoding.GetEncoding(866);
				var strs = enc.GetString(arr).Split('\n');
				foreach(var e in strs)
				{
					var substr = e.Split('\t');
					if (substr.Length != 3) continue;

					var cycle = new TechCycle();
					cycle.index = (DataClients.TechCycle.Operation)UInt32.Parse(substr[0]);
					cycle.start = Converter.ConvertUnixTimeToDateTime(Int32.Parse(substr[2]));
					if (result.Count > 0) result[result.Count - 1].end = cycle.start;
					result.Add(cycle);
				}
				return result.ToArray();
			}
			catch (Exception e)
			{
				Console.WriteLine(e.Message);
				Console.WriteLine(e.StackTrace);
				return result.ToArray();
			}
		}
		public static Protect[] ProtectData(byte[] arr)
		{
			var result = new List<Protect>();
			try
			{
				var cursor = 0;
				var time = new DateTime();
				while (cursor < arr.Length)
				{
					if (cursor + 4 >= arr.Length) break;
					time = Converter.ConvertUnixTimeToDateTime(BitConverter.ToInt32(arr, cursor));
					cursor += 4;

					if (cursor + 4 >= arr.Length) break;
					time = time.AddMilliseconds((double)BitConverter.ToInt32(arr, cursor) / 1000000);
					cursor += 4;

					while (true)
					{
						if (cursor >= arr.Length) break;
						if (arr[cursor] == 0xff)
						{
							cursor++;
							break;
						}

						var id = (int)arr[cursor];
						cursor++;

						if (cursor >= arr.Length) break;
						var value = (int)arr[cursor];
						cursor++;

						result.Add(new Protect()
						{
							time = time,
							id = id,
							value = value
						});
					}
				}
				return result.ToArray();
			}
			catch (Exception e)
			{
				Console.WriteLine(e.Message);
				Console.WriteLine(e.StackTrace);
				return result.ToArray();
			}
		}
		public static ADresult AnalogDiscret(List<ByteMatrix> adBytes, int vdp)
		{
			var result = new ADresult();
			try
			{
				var aMatrix = new AnalogsMatrix(vdp);
				foreach(var an in aMatrix.matrix)
				{
					var subRes = new List<Tuple<DateTime, double?>>();
					foreach(var b in adBytes)
					{
						var c = new byte?[2];
						for(var i = 0; i < 2; i++)
							c[i] = b.matrix.Length > an.Value.byteId[i] ?
								b.matrix[an.Value.byteId[i]] : null;
						double? d = c[0].HasValue && c[1].HasValue ?
							(double?)BitConverter.ToInt16(new byte[] { c[0].Value, c[1].Value }, 0) * an.Value.mul : null;
						d = !d.HasValue ? null : d.Value < an.Value.min ? an.Value.min : d.Value > an.Value.max ? an.Value.max : d;
						var e = new Tuple<DateTime, double?>(b.time, d);
						if (subRes.Count > 1 && subRes[subRes.Count - 1].Item2 == e.Item2 && subRes[subRes.Count - 2].Item2 == e.Item2)
							subRes.RemoveAt(subRes.Count - 1);
						subRes.Add(e);
					}
					result.an.Add(an.Key, subRes.ToArray());
				}
				
				var dMatrix = new DiscretsMatrix(vdp);
				foreach (var di in dMatrix.matrix)
				{
					var subRes = new List<Tuple<DateTime, bool?>>();
					foreach (var b in adBytes)
					{
						bool? c = b.matrix.Length <= di.Value.byteId ? null :
							!b.matrix[di.Value.byteId].HasValue ? null :
							(bool?)((b.matrix[di.Value.byteId] & (1 << di.Value.bitId)) != 0);
						c = !c.HasValue ? null : di.Value.invert ? !c : c;
						var e = new Tuple<DateTime, bool?>(b.time, c);
						if ((subRes.Count == 0) || (subRes.Count > 0 && subRes[subRes.Count - 1].Item2 != e.Item2))
							subRes.Add(e);
					}
					result.di.Add(di.Key, subRes.ToArray());
				}
			}
			catch (Exception e)
			{
				Console.WriteLine(e.Message);
				Console.WriteLine(e.StackTrace);
				return result;
			}
			return result;
		}
		public static List<ByteMatrix> GetADByteMatrix(DateTime time, byte[] arr)
		{
			var result = new List<ByteMatrix>();
			var byteArr = new List<byte?>();
			var currTime = new DateTime(time.Year, time.Month, time.Day, 0, 0, 0);
			var halfsec = 0;
			var cursor = 0;
			try
			{
				while (cursor < arr.Length)
				{
					var id = arr[cursor];
					cursor++;

					switch (id)
					{
						case 0xfb:
							halfsec = BitConverter.ToInt32(arr, cursor);
							cursor = cursor + 4;
							break;
						case 0xfc:
							byteArr = new List<byte?>();
							result.Add(
								new ByteMatrix()
								{
									time = currTime.AddSeconds((double)(halfsec + 1) / 2),
									matrix = byteArr.ToArray()
								}
							);
							halfsec = BitConverter.ToInt32(arr, cursor);
							cursor = cursor + 4;
							break;
						case 0xfe:
							halfsec = halfsec + BitConverter.ToInt16(arr, cursor);
							break;
						case 0xff:
							result.Add(
								new ByteMatrix()
								{
									time = currTime.AddSeconds((double)halfsec / 2),
									matrix = byteArr.ToArray()
								}
							);
							halfsec++;
							break;
						default:
							var a = Convert.ToInt32(id);
							while (byteArr.Count <= a) byteArr.Add(null);
							byteArr[a] = arr[cursor];
							cursor++;
							break;
					}
				}
				return result;
			}
			catch (Exception e)
			{
				Console.WriteLine(e.Message);
				Console.WriteLine(e.StackTrace);
				return result;
			}
		}
	}

}

