using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace skyscraper8.Skyscraper.Math
{
	public class EntropyCalculatorStream : Stream
	{
		public override void Flush()
		{
			//no actual writes done, so nothing to flush.
		}

		public override int Read(byte[] buffer, int offset, int count)
		{
			//this is a sink, so nothing to read here.
			throw new NotSupportedException();
		}

		public override long Seek(long offset, SeekOrigin origin)
		{
			if (offset == 0)
				return 0;

			//this is a sink, so we can't do actual seeks
			throw new NotSupportedException();
		}

		public override void SetLength(long value)
		{
			//TODO: allow this later, although I have no idea why one would actually want this.
			throw new NotSupportedException();
		}

		private float _entropy;
		private long[] freq;
		private long internalPosition;
		private byte[] usedBytes;
		public override void Write(byte[] buffer, int offset, int count)
		{
			
			//Count the bytes
			if (freq == null)
				freq = new long[byte.MaxValue + 1];
			for (int i = 0; i < count; i++)
				freq[buffer[i + offset]]++;
			internalPosition += count;


			//Actually calculate the entropy here.
			float newEntropy = 0.0f;
			for (int i = 0; i < freq.Length; i++)
			{
				float p_i = (float)freq[i] / (float)internalPosition;
				if (p_i > 0)
				{
					newEntropy -= p_i * FastMath.FastLog2(p_i);
				}
			}

			_entropy = newEntropy;
			if (_entropy > 8)
			{
				_entropy = 8;
			}
		}

		public override bool CanRead => false;
		public override bool CanSeek => false;
		public override bool CanWrite => true;
		public override long Length => internalPosition;
		public override long Position
		{
			get
			{
				return internalPosition;
			}
			set
			{
				throw new NotSupportedException();
			}
		}
		public float Entropy
		{
			get
			{
				return _entropy;
			}
		}

		public double Percentage
		{
			get
			{
				return (Entropy / 8.0) * 100.0;
			}
		}

		public void Cheat(float newValue)
		{
			_entropy = newValue;
		}
	}
}
