' Copyright (c) 2013-2014 Bruce A Henderson
'
' Based on the public domain Monkey "trans" by Mark Sibly
'
' This software is provided 'as-is', without any express or implied
' warranty. In no event will the authors be held liable for any damages
' arising from the use of this software.
'
' Permission is granted to anyone to use this software for any purpose,
' including commercial applications, and to alter it and redistribute it
' freely, subject to the following restrictions:
'
'    1. The origin of this software must not be misrepresented; you must not
'    claim that you wrote the original software. If you use this software
'    in a product, an acknowledgment in the product documentation would be
'    appreciated but is not required.
'
'    2. Altered source versions must be plainly marked as such, and must not be
'    misrepresented as being the original software.
'
'    3. This notice may not be removed or altered from any source
'    distribution.
'
SuperStrict

Import BRL.LinkedList
Import BRL.Map
Import BRL.FileSystem

Import "options.bmx"
Import "base.stringhelper.bmx"


' debugging help
Const DEBUG:Int = False
Const ABORT_ON_NULL:Int = True
Const PROFILER:Int = False

Global ENV_LANG$

Global _errInfo$
Global _errStack:TList = New TList

' bytes offset to the first field
Global OBJECT_BASE_OFFSET:Int = 8
' 4 bytes on 32-bit, 8 bytes on 64-bit
Global POINTER_SIZE:Int = 4

Function PushErr( errInfo$ )
	_errStack.AddLast _errInfo
	_errInfo=errInfo
End Function

Function PopErr()
	_errInfo=String(_errStack.RemoveLast())
End Function

Function Err( err$ )
	'WriteStderr "Compile Error: "+err + "~n"
	'WriteStderr _errInfo + "~n"
'	Print _errInfo+" "+err
DebugStop ' useful for debugging!
	Throw "Compile Error: "+err + "~n" + _errInfo + "~n"
End Function

Function FormatError:String(path:String, line:Int, char:Int)
	Return "[" + path + ";" + line + ";" + char + "]"
End Function

Function InternalErr()
	Print _errInfo+" : Error : Internal error."
	DebugStop
	'Error _errInfo+" : Error : Internal error."
End Function

Function IsSpace:Int( ch:Int )
	Return ch<=Asc(" ") Or ch=$A0 ' NO-BREAK SPACE (U+00A0)
End Function

Function IsDigit:Int( ch:Int )
	Return ch>=Asc("0") And ch<=Asc("9")
End Function

Function IsAlpha:Int( ch:Int )
	Return (ch>=Asc("A") And ch<=Asc("Z")) Or (ch>=Asc("a") And ch<=Asc("z"))
End Function

Function IsBinDigit:Int( ch:Int )
	Return ch=Asc("0") Or ch=Asc("1")
End Function

Function IsHexDigit:Int( ch:Int )
	Return IsDigit(ch) Or (ch>=Asc("A") And ch<=Asc("F")) Or (ch>=Asc("a") And ch<=Asc("f"))
End Function

Function Todo() 
	Err "TODO!"
End Function

Function IsStandardFunc:Int(func:String)
	func = func.ToLower()
	
	Global funcs:String = ";isalnum;isalpha;isascii;isblank;iscntrl;isdigit;isgraph;islower;isprint;ispunct;isspace;isupper;isxdigit;" + ..
		"strlen;"
	
	Return funcs.Find(func) > 0
End Function

'enquote depending on ENV_LANG
'
Function LangEnquote$( str$ )
	str=EscapeString(str)
'	str=str.Replace( "~0","\0" )	'Fix me?
	For Local i:Int=0 Until str.Length
		If str[i]>=32 And str[i]<128 Continue
		Local t$,n:Int=str[i]
		While n
			Local c:Int=(n&15)+48
			If c>=58 c:+97-58
			t=Chr( c )+t
			n=(n Shr 4) & $0fffffff
		Wend
		If Not t t="0"
		If ENV_LANG = "cpp" Then
		'Case "cpp"
			t="~q~q\x"+t+"~q~q"
		Else
			t="\u"+("0000"+t)[-4..]
		End If
		str=str[..i]+t+str[i+1..]
		i:+t.Length-1
	Next
	str="~q"+str+"~q"
	If ENV_LANG="cpp" str="L"+str
	Return str
End Function

Function EscapeString$(str$)
	str=str.Replace( "\","\\" )
	str=str.Replace( "~q","\~q" )
	str=str.Replace( "~n","\n" )
	str=str.Replace( "~r","\r" )
	str=str.Replace( "~t","\t" )
	Return str
End Function

Function BmxEnquote$( str$ )
	str=str.Replace( "~~","~~~~" )
	str=str.Replace( "~q","~~q" )
	str=str.Replace( "~n","~~n" )
	str=str.Replace( "~r","~~r" )
	str=str.Replace( "~t","~~t" )
	str=str.Replace( "~0","~~0" )
	str="~q"+str+"~q"
	Return str
End Function

Function BmxUnquote$( str$ )
	str=str[1..str.Length-1]
	str=str.Replace( "~~~~","~~z" )	'a bit dodgy - uses bad esc sequence ~z 
	str=str.Replace( "~~q","~q" )
	str=str.Replace( "~~n","~n" )
	str=str.Replace( "~~r","~r" )
	str=str.Replace( "~~t","~t" )
	str=str.Replace( "~~0","~0" )
	str=str.Replace( "~~z","~~" )
	Return str
End Function

Type TStack Extends TList

	Method Push(obj:Object)
		AddFirst(obj)
	End Method

	Method Length:Int()
		Return count()
	End Method
	
	Method Get:Object(index:Int)
		Return ValueAtIndex(index)
	End Method
	
	Method Pop:Object()
		Return RemoveFirst()
	End Method
	
End Type

Type TStringList Extends TList
	Method Join:String(s:String)
		Local arr:String[] = New String[count()]
		Local index:Int
		For Local t:String = EachIn Self
			arr[index] = t
			index :+ 1
		Next
		
		Return s.Join(arr)
	End Method
End Type

Type TKeyValue
	Field key:Object
	Field value:Object
	
	Method Create:TKeyValue(key:Object,value:Object)
		Self.key = key
		Self.value = value
		Return Self
	End Method
	
	Method Compare:Int(other:Object)
		If Not TKeyValue(other) Return 0
		Return key.Compare(TKeyValue(other).key)
	End Method
	
End Type

Type TUnorderedMap

	Field list:TList = New TList
	Field map:TMap = New TMap

	Method Insert( key:Object,value:Object )
		list.AddLAst(New TKeyValue.Create(key, value))
		map.Insert(key, value)
	End Method
	
	Method Keys:TList()
		Local klist:TList = New TList
		For Local kv:TKeyValue = EachIn list
			klist.AddLast(kv.key)
		Next
		Return klist
	End Method
	
	Method Values:TList()
		Local vlist:TList = New TList
		For Local kv:TKeyValue = EachIn list
			vlist.AddLast(kv.value)
		Next
		Return vlist
	End Method
	
	Method Contains:Int( key:Object )
		Return map.Contains(key)
	End Method
	
	Method ValueForKey:Object( key:Object )
		Return map.ValueForKey(key)
	End Method
End Type

Function MakeKeywords:String()
	Local keywords:String
	
	keywords :+ "import brl.classes~n"
	keywords :+ "Asc%(v$)=~qbrl_blitz_keywords_asc~q~n"
	keywords :+ "Sgn#(v#)=~qbrl_blitz_keywords_sgn~q~n"
	keywords :+ "Chr$(v%)=~qbrl_blitz_keywords_chr~q~n"
	keywords :+ "Len%(v:Object)=~qbrl_blitz_keywords_len~q~n"
	keywords :+ "Min%(v1%,v2%)=~qbrl_blitz_keywords_min~q~n"
	keywords :+ "Max%(v1%,v2%)=~qbrl_blitz_keywords_max~q~n"
	'keywords :+ "SizeOf%(v%)=~qbrl_blitz_keywords_sizeof~q~n"
	'keywords :+ "Incbin(v$)=~qbrl_blitz_keywords_incbin~q~n"
	keywords :+ "IncbinPtr@*(v$)=~qbbIncbinPtr~q~n"
	keywords :+ "IncbinLen%(v$)=~qbbIncbinLen~q~n"
 
	Return keywords
End Function

Function FilePath:String(path:String)
	Local baseDir:String = ExtractDir(path)
	Local bmxDir:String = baseDir + "/.bmx"
	
	If FileType(bmxDir) <> FILETYPE_DIR Then
		Throw "Missing : " + bmxDir
	End If
	
	Return bmxDir
End Function

Function BuildHeaderName:String(path:String)
	If opt_buildtype = BUILDTYPE_MODULE Then
		path = opt_modulename + "_" + StripDir(path)
	Else
		Local dir:String = ExtractDir(path).ToLower().Replace("/.bmx","")
		dir = dir[dir.findLast("/") + 1..]
		If dir.EndsWith(".mod") Then
			dir = dir.Replace(".mod", "")
		End If
		Local file:String = StripDir(path).ToLower()
		path = dir + "_" + file
	End If
	
	Return TStringHelper.Sanitize(path)
End Function

Rem
bbdoc: Get the header file name from a given module ident, optionally with include path.
End Rem
Function ModuleHeaderFromIdent:String(ident:String, includePath:Int = False)
	Local ns:String = ident[..ident.find(".")]
	Local name:String = ident[ident.find(".") + 1..]
	
	Local file:String = name + ".bmx" + FileMung() + ".h"
	
	If includePath Then
		file = ns + ".mod/" + name + ".mod/.bmx/" + file
	End If
	
	Return file
End Function

Function HeaderFile:String(path:String, mung:String)
	Local fileDir:String = FilePath(path)
	Local file:String = StripDir(path)
	
	Return fileDir + "/" + file + mung + ".h"
End Function

Function OutputFilePath:String(path:String, mung:String, suffix:String, bmxDir:Int = False)
	Local fileDir:String = FilePath(path)
	If bmxDir Then
		fileDir :+ "/.bmx"
	End If
	Local file:String = StripDir(path)
	
	Return fileDir + "/" + file + mung + "." + suffix
End Function

Function FileMung:String(makeApp:Int = False)
	Local m:String = "."
	
	If makeApp Then
		Select opt_apptype
			Case APPTYPE_CONSOLE
				m :+ "console."
			Case APPTYPE_GUI
				m :+ "gui."
		End Select
	End If
	
	If opt_release Then
		m :+ "release"
	Else
		m :+ "debug"
	End If
	
	If opt_threaded Then
		m :+ ".mt"
	End If
	
	m :+ "." + opt_platform
	
	m :+ "." + opt_arch
	
	Return m
End Function

Function HeaderComment:String()
	' TODO
End Function