using System;
using System.IO;
using System.Text;

namespace AssetStudio
{
	internal class Emitter
	{
		public Emitter(TextWriter writer, bool formatKeys)
		{
			if (writer == null)
			{
				throw new ArgumentNullException(nameof(writer));
			}
			m_stream = writer;
			IsFormatKeys = formatKeys;
			if (formatKeys)
			{
				m_sb = new StringBuilder();
			}
		}

		public Emitter IncreaseIndent()
		{
			m_indent++;
			return this;
		}

		public Emitter DecreaseIndent()
		{
			if (m_indent == 0)
			{
				throw new Exception($"Increase/decrease indent mismatch");
			}
			m_indent--;
			return this;
		}

		public Emitter Write(char value)
		{
			WriteDelayed();
			m_stream.Write(value);
			return this;
		}

		public Emitter WriteRaw(char value)
		{
			m_stream.Write(value);
			return this;
		}

		public Emitter Write(byte value)
		{
			WriteDelayed();
			m_stream.Write(value);
			return this;
		}

		public Emitter Write(ushort value)
		{
			WriteDelayed();
			m_stream.Write(value);
			return this;
		}

		public Emitter Write(short value)
		{
			WriteDelayed();
			m_stream.Write(value);
			return this;
		}

		public Emitter Write(uint value)
		{
			WriteDelayed();
			m_stream.Write(value);
			return this;
		}

		public Emitter Write(int value)
		{
			WriteDelayed();
			m_stream.Write(value);
			return this;
		}

		public Emitter Write(ulong value)
		{
			WriteDelayed();
			m_stream.Write(value);
			return this;
		}

		public Emitter Write(long value)
		{
			WriteDelayed();
			m_stream.Write(value);
			return this;
		}

		public Emitter Write(float value)
		{
			WriteDelayed();
			m_stream.Write(value);
			return this;
		}

		public Emitter Write(double value)
		{
			WriteDelayed();
			m_stream.Write(value);
			return this;
		}

		public Emitter Write(string value)
		{
			if (value.Length > 0)
			{
				WriteDelayed();
				m_stream.Write(value);
			}
			return this;
		}

		public Emitter WriteFormat(string value)
		{
			if (value.Length > 0)
			{
				WriteDelayed();
				if (value.Length > 2 && value.StartsWith("m_", StringComparison.Ordinal))
				{
					m_sb.Append(value, 2, value.Length - 2);
					if (char.IsUpper(m_sb[0]))
					{
						m_sb[0] = char.ToLower(m_sb[0]);
					}
					value = m_sb.ToString();
					m_sb.Clear();
				}
				m_stream.Write(value);
			}
			return this;
		}

		public Emitter WriteRaw(string value)
		{
			m_stream.Write(value);
			return this;
		}

		public Emitter WriteClose(char @char)
		{
			m_isNeedSeparator = false;
			m_isNeedWhitespace = false;
			m_isNeedLineBreak = false;
			return Write(@char);
		}

		public Emitter WriteClose(string @string)
		{
			m_isNeedSeparator = false;
			m_isNeedWhitespace = false;
			return Write(@string);
		}

		public Emitter WriteWhitespace()
		{
			m_isNeedWhitespace = true;
			return this;
		}

		public Emitter WriteSeparator()
		{
			m_isNeedSeparator = true;
			return this;
		}

		public Emitter WriteLine()
		{
			m_isNeedLineBreak = true;
			return this;
		}

		public void WriteMeta(MetaType type, string value)
		{
			Write('%').Write(type.ToString()).WriteWhitespace();
			Write(value).WriteLine();
		}

		public void WriteDelayed()
		{
			if (m_isNeedLineBreak)
			{
				m_stream.Write('\n');
				m_isNeedSeparator = false;
				m_isNeedWhitespace = false;
				m_isNeedLineBreak = false;
				WriteIndent();
			}
			if (m_isNeedSeparator)
			{
				m_stream.Write(',');
				m_isNeedSeparator = false;
			}
			if (m_isNeedWhitespace)
			{
				m_stream.Write(' ');
				m_isNeedWhitespace = false;
			}
		}

		private void WriteIndent()
		{
			for (int i = 0; i < m_indent * 2; i++)
			{
				m_stream.Write(' ');
			}
		}

		public bool IsFormatKeys { get; }
		public bool IsKey { get; set; }

		private readonly TextWriter m_stream;
		private readonly StringBuilder m_sb;

		private int m_indent = 0;
		private bool m_isNeedWhitespace = false;
		private bool m_isNeedSeparator = false;
		private bool m_isNeedLineBreak = false;
	}
}
