// The CMLeonOS Project (https://github.com/Leonmmcoset/CMLeonOS)
// Copyright (C) 2025-present LeonOS 2 Developer Team
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#define API_CHECK
#define UNILUA_ASSERT

using System;

namespace UniLua
{
	using DebugS = System.Diagnostics.Debug;
	using NumberStyles = System.Globalization.NumberStyles;

	internal static class Utl
	{
		private static void Throw( params string[] msgs )
		{
			throw new Exception(String.Join("", msgs));
		}

		public static void Assert( bool condition )
		{
#if UNILUA_ASSERT
			if( !condition )
				Throw("assert failed!" );
			//DebugS.Assert( condition );
#endif
		}

		public static void Assert( bool condition, string message )
		{
#if UNILUA_ASSERT
			if( !condition )
				Throw( "assert failed! ", message );
			//DebugS.Assert( condition, message );
#endif
		}

		public static void Assert( bool condition, string message, string detailMessage )
		{
#if UNILUA_ASSERT
			if( !condition )
				Throw( "assert failed! ", message, "\n", detailMessage );
			//DebugS.Assert( condition, message, detailMessage );
#endif
		}

		public static void ApiCheck( bool condition, string message )
		{
#if UNILUA_ASSERT
#if API_CHECK
			Assert( condition, message );
#endif
#endif
		}

		public static void ApiCheckNumElems( LuaState lua, int n )
		{
#if UNILUA_ASSERT
			Assert( n < (lua.Top.Index - lua.CI.FuncIndex), "not enough elements in the stack" );
#endif
		}

		public static void InvalidIndex()
		{
#if UNILUA_ASSERT
			Assert( false, "invalid index" );
#endif
		}

		private static bool IsNegative( string s, ref int pos )
		{
			if( pos >= s.Length )
				return false;

			char c = s[pos];
			if( c == '-' )
			{
				++pos;
				return true;
			}
			else if( c == '+' )
			{
				++pos;
			}
			return false;
		}

		private static bool IsXDigit( char c )
		{
			if( Char.IsDigit( c ) )
				return true;

			if( 'a' <= c && c <= 'f' )
				return true;

			if( 'A' <= c && c <= 'F' )
				return true;

			return false;
		}

		private static double ReadHexa( string s, ref int pos, double r, out int count )
		{
			count = 0;
			while( pos < s.Length && IsXDigit( s[pos] ) )
			{
				r = (r * 16.0) + Int32.Parse( s[pos].ToString(), NumberStyles.HexNumber );
				++pos;
				++count;
			}
			return r;
		}

		private static double ReadDecimal( string s, ref int pos, double r, out int count )
		{
			count = 0;
			while( pos < s.Length && Char.IsDigit( s[pos] ) )
			{
				r = (r * 10.0) + Int32.Parse( s[pos].ToString() );
				++pos;
				++count;
			}
			return r;
		}

		// following C99 specification for 'strtod'
		public static double StrX2Number( string s, ref int curpos )
		{
			int pos = curpos;
			while( pos < s.Length && Char.IsWhiteSpace( s[pos] )) ++pos;
			bool negative = IsNegative( s, ref pos );

			// check `0x'
			if( pos >= s.Length || !(s[pos] == '0' && (s[pos+1] == 'x' || s[pos+1] == 'X')) )
				return 0.0;

			pos += 2; // skip `0x'

			double r = 0.0;
			int i = 0;
			int e = 0;
			r = ReadHexa( s, ref pos, r, out i );
			if( pos < s.Length && s[pos] == '.' )
			{
				++pos; // skip `.'
				r = ReadHexa( s, ref pos, r, out e );
			}
			if( i == 0 && e == 0 )
				return 0.0; // invalid format (no digit)

			// each fractional digit divides value by 2^-4
			e *= -4;
			curpos = pos;

			// exponent part
			if( pos < s.Length && (s[pos] == 'p' || s[pos] == 'P') )
			{
				++pos; // skip `p'
				bool expNegative = IsNegative( s, ref pos );
				if( pos >= s.Length || !Char.IsDigit( s[pos] ) )
					goto ret;

				int exp1 = 0;
				while( pos < s.Length && Char.IsDigit( s[pos] ) )
				{
					exp1 = exp1 * 10 + Int32.Parse( s[pos].ToString() );
					++pos;
				}
				if( expNegative )
					exp1 = -exp1;
				e += exp1;
			}
			curpos = pos;

ret:
			if( negative ) r = -r;

			return r * Math.Pow(2.0, e);
		}

		public static double Str2Number( string s, ref int curpos )
		{
			int pos = curpos;
			while( pos < s.Length && Char.IsWhiteSpace( s[pos] )) ++pos;
			bool negative = IsNegative( s, ref pos );

			double r = 0.0;
			int i = 0;
			int f = 0;
			r = ReadDecimal( s, ref pos, r, out i );
			if( pos < s.Length && s[pos] == '.' )
			{
				++pos;
				r = ReadDecimal( s, ref pos, r, out f );
			}
			if( i == 0 && f == 0 )
				return 0.0;

			f = -f;
			curpos = pos;

			// exponent part
			double e = 0.0;
			if( pos < s.Length && (s[pos] == 'e' || s[pos] == 'E') )
			{
				++pos;
				bool expNegative = IsNegative( s, ref pos );
				if( pos >= s.Length || !Char.IsDigit( s[pos] ) )
					goto ret;

				int n;
				e = ReadDecimal( s, ref pos, e, out n );
				if( expNegative )
					e = -e;
				f += (int)e;
			}
			curpos = pos;

ret:
			if( negative ) r = -r;

			return r * Math.Pow(10, f);
		}

		public static string TrimWhiteSpace( string str )
		{
			int s = 0;
			int e = str.Length;

			while( s < str.Length && Char.IsWhiteSpace( str[s] ) ) ++s;
			if( s >= e )
				return "";

			while( e >= 0 && Char.IsWhiteSpace( str[e-1] ) ) --e;
			return str.Substring( s, e-s );
		}
	}

}

