using skyscraper5.Skyscraper.IO;
using skyscraper8.Skyscraper.Plugins;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using skyscraper8.SophtainerIO;

namespace skyscraper8.Skyscraper.Drawing
{
	public class IqChartData
	{
		public const int DESIRABLE_AMOUNT_OF_POINTS = 100000;
		private static PluginLogger logger = PluginLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

		private IqChartData()
		{
			IQ = new byte[256][];
			for (int i = 0; i < IQ.Length; i++)
			{
				IQ[i] = new byte[256];
			}

			packets = new List<byte[]>();
		}

		public byte[][] IQ;
		private List<byte[]> packets;
		private byte progress;
		private long totalSamples;

		public static IqChartData Create()
		{
			IqChartData child = new IqChartData();
			return child;
		}

		public static IqChartData LoadFrom(Stream indata)
		{
			if (indata.ReadUInt8() != 0x41)
				throw new ArgumentException("That stream isn't IQ Data!", nameof(indata));

			IqChartData result = null;
			while (indata.GetAvailableBytes() >= 4)
			{
				if (result == null)
					result = new IqChartData();

				int packSize = indata.ReadInt32LE();
				sbyte[] sbytes = indata.ReadSBytes(packSize);
				result.PushPacket(sbytes);
			}

			return result;
		}

		public bool IsComplete
		{
			get
			{
				return totalSamples >= DESIRABLE_AMOUNT_OF_POINTS;
			}
		}


		public void PushPacket(sbyte[] buffer)
		{
			for (long l = 0; l < buffer.Length; l += 2)
			{
				byte i = (byte)(buffer[l] + sbyte.MaxValue);
				byte q = (byte)(buffer[l + 1] + sbyte.MaxValue);

				if (IQ[i][q] != 255)
				{
					IQ[i][q]++;
					if (IQ[i][q] > progress)
					{
						progress = IQ[i][q];
						logger.Log(PluginLogLevel.Debug, "IQ Z-Axis now at {0}", progress);
					}
				}
			}

			byte[] writableBuffer = Array.ConvertAll(buffer, x => (byte)x);
			packets.Add(writableBuffer);
			totalSamples += (buffer.Length / 2);
		}

		public void SaveTo(Stream fs)
		{
			Sophtainer sophtainer = Sophtainer.CreateSophtainer(fs, 538988873, new Version(1, 1, 0, 0));
			OffsetStream offsetStream = sophtainer.GetStream();
			foreach (byte[] packet in packets)
			{
				offsetStream.WriteInt32LE(packet.Length);
				offsetStream.Write(packet, 0, packet.Length);
			}
		}

		public byte[] SaveToBytes()
		{
			MemoryStream ms = new MemoryStream();
			SaveTo(ms);
			return ms.GetBuffer();
		}

		private bool scaled;
		public void AutoScale()
		{
			if (scaled)
				return;
			if (progress == 255)
				return;

			double maxByte = byte.MaxValue;
			double maxProgress = progress;
			double scaleFactor = maxByte / maxProgress;

			for (int x = 0; x < 256; x++)
			{
				for (int y = 0; y < 256; y++)
				{
					double currentValue = IQ[x][y];
					currentValue *= scaleFactor;
					if (currentValue > byte.MaxValue)
						currentValue = byte.MaxValue;
					IQ[x][y] = (byte)currentValue;
				}
			}

			scaled = true;
		}

		public float ProgressFraction
		{
			get
			{
				float l = totalSamples;
				float r = DESIRABLE_AMOUNT_OF_POINTS;
				float x = l / r;
				return x;
			}
		}

		public double ProgressHumanReadable
		{
			get
			{
				double frac = ((double)(totalSamples) * 100.0) / (double)(DESIRABLE_AMOUNT_OF_POINTS);
				return frac;
			}
		}

		public byte ZAxis => progress;
	}
}
