import sys, re, os.path, glob, shutil, xml.etree.ElementTree as ET

structBaseText = """<!--This Document is generated by GameMaker, if you edit it by hand then you do so at your own risk!-->
<object>
  <spriteName>&lt;undefined&gt;</spriteName>
  <solid>0</solid>
  <visible>0</visible>
  <depth>0</depth>
  <persistent>-1</persistent>
  <parentName>&lt;undefined&gt;</parentName>
  <maskName>&lt;undefined&gt;</maskName>
  <events/>
  <PhysicsObject>0</PhysicsObject>
  <PhysicsObjectSensor>0</PhysicsObjectSensor>
  <PhysicsObjectShape>0</PhysicsObjectShape>
  <PhysicsObjectDensity>0.5</PhysicsObjectDensity>
  <PhysicsObjectRestitution>0.100000001490116</PhysicsObjectRestitution>
  <PhysicsObjectGroup>0</PhysicsObjectGroup>
  <PhysicsObjectLinearDamping>0.100000001490116</PhysicsObjectLinearDamping>
  <PhysicsObjectAngularDamping>0.100000001490116</PhysicsObjectAngularDamping>
  <PhysicsObjectFriction>0.200000002980232</PhysicsObjectFriction>
  <PhysicsObjectAwake>-1</PhysicsObjectAwake>
  <PhysicsObjectKinematic>0</PhysicsObjectKinematic>
  <PhysicsShapePoints/>
</object>
"""

def parse_gmlx(text):
	text = text.strip("\t \n")
	text = re.sub(r"//.*\n","\n",text)
	if text == '':
		return []
	else:
		(text, block) = parse_block(text)
		blocks = parse_gmlx(text)
		return [block] + blocks
		

def parse_block(text):
	text = text.strip("\t \n")
	if(text.startswith("struct")):
		return parse_struct(text)
	elif(text.startswith("function")):
		return parse_function(text)
	else:
		raise Exception("failed to parse block starting: "+text[0:40])
	
	
def parse_struct(text):
	result = text.split(None, 1)
	blockIndex = result[1].index("{")
	NAME = result[1][0:blockIndex].strip()
	rest = result[1][blockIndex:]
	(body, rest) = getBetweenBalanced(rest,"{","}")
	return (rest, ("struct",NAME,body))

def parse_function(text):
	result = text.split(None, 1)
	argIndex = result[1].index("(")
	NAME = result[1][0:argIndex].strip()
	text = result[1][argIndex:]
	if(text.startswith("(")):
		argEndIndex = text.find(")");
		if(argEndIndex == -1):
			raise Exception("No Function Argument List ) for:"+text[:40]);
		arguments = [x.strip() for x in text[1:argEndIndex].split(",")];
		if(arguments[0] == ''):
			arguments = []
		(body, text) = getBetweenBalanced(text[argEndIndex+1:],"{","}")
		return (text, ("function",NAME,arguments,body))
	else:
		raise Exception("No Function Argument List ( for:"+text[:40]);

def getBetweenBalanced(text, leftCharacter, rightCharacter):
	text = text.lstrip().lstrip(leftCharacter)
	count = 1
	index = 0
	while(count):
		nextrb = text.find(rightCharacter,index)
		if(nextrb == -1):
			raise Exception("no ending to block starting: "+text[:40])
		nextlb = text.find(leftCharacter,index,nextrb)
		if(nextlb != -1):
			index = nextlb + 1
			count += 1
		else:
			count -= 1
			index = nextrb+1
			if(count == 0):
				return (text[:index-1],text[index:])

def translate(text):
	patternNew = re.compile(r"new ([a-zA-Z][a-zA-Z0-9]*)\s*\(\s*\)")
	out = ""
	index = 0
	while 1:
		m = patternNew.search(text,index)
		if(m == None):
			out += text[index:]
			break
		substitute = m.group(1)+"__ctor()"
		out += text[index:m.start()] + substitute
		index = m.end()
	return out

def generateFiles(blocks):
	files = []
	for block in blocks:
		if(block[0] == "function"):
			fileName = block[1]+".gml"
			text = ""
			argCount = 0
			for arg in block[2]:
				text +="var "+arg+" = argument"+str(argCount)+";\n"
				argCount += 1
			text+= translate(block[3])
			files.append((fileName, text))
		elif(block[0] == "struct"):
			fileName = block[1]+"__ctor.gml"
			text = "var l"+block[1]+" = instance_create(-100,-100,oStructBase);\n";
			text += "l"+block[1]+".type = \""+block[1]+"\"\n"
			text += "with(l"+block[1]+"){"
			text += translate(block[2])
			text += "}\n"
			text += "return l"+block[1]+";\n"
			files.append((fileName, text))
	return files

def getParentProjectDirectory(directory,count):
	if(count == 0):
		raise Exception("No Project File found")
	projects = glob.glob(os.path.join(directory,"*.project.gmx"))
	if(len(projects) > 0):
		return (directory, projects[0])
	else:
		return getParentProjectDirectory(os.path.abspath(os.path.join(directory,"..")),count-1)

def installFiles(files, path):
	directory = os.path.dirname(path)
	name = os.path.basename(path)
	try:
		(projectdir, projectFile) = getParentProjectDirectory(directory,10);
	except Exception:
		projectdir = "./"
		projectFile = None
		 
	# create scripts in scripts folder
	scriptsPath = os.path.join(projectdir,"scripts")
	if not os.path.exists(scriptsPath):
		os.makedirs(scriptsPath)
	for file in files:
		filePath = os.path.join(scriptsPath,file[0])
		if(os.path.exists(filePath)):
			shutil.copy(filePath,filePath+".bak");
		f = open(filePath,"w")
		f.write(file[1])
		f.close()
	
	if projectFile is None:
		return
		
	# create struct base file
	oStructBasePath = os.path.join(projectdir, "objects","oStructBase.object.gmx")
	f = open(oStructBasePath, "w")
	f.write(structBaseText);
	f.close()
	
	# edit the project file
	xmlDoc = ET.parse(projectFile)
	#base object
	objectsRoot = xmlDoc.find(".//objects/objects[@name='gmlx']")
	if objectsRoot is None:
		objNode = ET.SubElement(ET.SubElement(xmlDoc.find("objects"),"objects", {"name":"gmlx"}), "object")
		objNode.text = "objects\\oStructBase"
	#scripts
	scriptsGMLXRoot = xmlDoc.find(".//scripts/scripts[@name='gmlx']")
	if scriptsGMLXRoot is None:
		scriptRoot = xmlDoc.find("scripts")
		scriptsGMLXRoot = ET.SubElement(scriptRoot, "scripts", {"name": "gmlx"})
	elem = scriptsGMLXRoot.find("./scripts[@name='"+name+"']")
	if not elem is None:
		scriptsGMLXRoot.remove(elem)
	elem = ET.SubElement(scriptsGMLXRoot,"scripts",{"name" : name})
	for file in files:
		node = ET.SubElement(elem,"script",{})
		node.text = "scripts\\"+file[0]
	xmlDoc.write(projectFile)

def compileAndInstall(string, path):
	installFiles(generateFiles(parse_gmlx(string)), path)
	
for arg in sys.argv[1:]:
	print("Parsing "+ arg)
	f = open(arg)
	text = f.read()
	f.close()
	compileAndInstall(text,arg)
