# SPDX-License-Identifier: MPL-2.0
import os
import sys
import subprocess
class VirtualRegion:
  def __init__(self,start,size):
    self.start=start
    self.size=size
  def compute_end(self):
    self.end=self.start+self.size
args=sys.argv
if len(args)!=2:
  print("[VMLC] Error: not enough arguments")
  exit(-1)
hfile=args[1]
if not os.path.exists(hfile):
  print("[VMLC] Error: provided .h file doesn't exist")
  exit(-1)
hfile=os.path.abspath(hfile)
print("[VMLC] Provided file: "+hfile)
incomplete_region_va=[]
incomplete_region_size_bytes=[]
region_name=[]
fileslines=open(hfile,"r").readlines()
macros=[]
for i in range(len(fileslines)):
  if fileslines[i].startswith("#define"):
    macros.append(fileslines[i])
for i in range(len(macros)):
  macroname=str(str(macros[i]).strip(" ").split(" ")[1])
  if macroname.endswith("_VA"):
    if not macroname in incomplete_region_va:
      incomplete_region_va.append(macroname)
    else:
      print("[VMLC] Error: found duplicate macro: "+macroname)
      exit(-1)
  elif macroname.endswith("_SIZE_BYTES"):
    if not macroname in incomplete_region_size_bytes:
      incomplete_region_size_bytes.append(macroname)
    else:
      print("[VMLC] Error: found duplicate macro: "+macroname)
      exit(-1)
for i in range(len(incomplete_region_va)):
  macronamesize=str(incomplete_region_va[i]).removesuffix("_VA")+"_SIZE_BYTES"
  if not macronamesize in incomplete_region_size_bytes:
    print("[VMLC] Error: for region \""+str(incomplete_region_va[i]).removesuffix("_VA")+"\", corresponding size wasn't found.")
    exit(-1)
for i in range(len(incomplete_region_size_bytes)):
  macronameva=str(incomplete_region_size_bytes[i]).removesuffix("_SIZE_BYTES")+"_VA"
  if not macronameva in incomplete_region_va:
    print("[VMLC] Warning: for region \""+str(incomplete_region_size_bytes[i]).removesuffix("_SIZE_BYTES")+"\", corresponding VA wasn't found. Skipping")
print("[VMLC] Found "+str(len(incomplete_region_va))+" valid virtual regions: ",end="")
for i in range(len(incomplete_region_va)):
  if i!=len(incomplete_region_va)-1:
    print(incomplete_region_va[i].removesuffix("_VA")+", ",end="")
  else:
    print(incomplete_region_va[i].removesuffix("_VA"))
  region_name.append(incomplete_region_va[i].removesuffix("_VA"))
os.chdir(os.path.dirname(__file__))
try:
  os.remove("cfile.c")
except FileNotFoundError:
  pass
tmpcfile=open("cfile.c","w")
tmpcfile.write("#include <string.h>\n#include <stdio.h>\n#include <inttypes.h>\n#include \""+hfile+"\"\n")
tmpcfile.write("int main() {\n  char buf[256];\n  FILE *f=fopen(\"reg.txt\",\"w\");\n")
tmpcfile.write("  if (!f) return -1;\n")
for i in range(len(region_name)):
  tmpcfile.write("  snprintf(buf,256,\"%\" PRIu64 \" %\" PRIu64 \"\\n\",(uint64_t)"+region_name[i]+"_VA,(uint64_t)"+region_name[i]+"_SIZE_BYTES);\n")
  tmpcfile.write("  fwrite(buf,1,strlen(buf),f);\n")
tmpcfile.write("  fclose(f);\n  return 0;\n}")
tmpcfile.close()
try:
  os.remove("cfile")
  os.remove("reg.txt")
except FileNotFoundError:
  pass
r=subprocess.run(["gcc","cfile.c","-o","cfile"])
if r.returncode!=0:
  print("[VMLC] Error: can't compile generated cfile.c")
  exit(-1)
r=subprocess.run(["./cfile"])
if r.returncode!=0:
  print("[VMLC] Error: can't run compiled cfile")
  exit(-1)
if not os.path.exists("reg.txt"):
  print("[VMLC] Error: reg.txt doesn't exist")
  exit(-1)
reglist={}
regstart=[]
with open("reg.txt") as f:
  for line in f:
    va,size=line.strip().split()
    vr=VirtualRegion(int(va),int(size))
    vr.compute_end()
    reglist[int(va)]=vr
    if not vr.start in regstart:
      regstart.append(vr.start)
    else:
      print("[VMLC] Error: found two regions starting at the same VA: "+str(vr.start)+" / 0x"+str(hex(vr.start)))
      exit(-1)
print("[VMLC] Obtained region decimal VA and size.")
regstart.sort()
regboundaries=[]
for i in range(len(regstart)):
  regboundaries.append(reglist[regstart[i]].start)
  regboundaries.append(reglist[regstart[i]].end)
print("[VMLC] Checking for overlaps...")
for i in range(1,len(regboundaries)-1,2):
  if regboundaries[i]>regboundaries[i+1]:
    print("[VMLC] Error: found overlap between region ending at "+str(regboundaries[i])+" / 0x"+str(hex(regboundaries[i]))+" and region starting at "+str(regboundaries[i+1])+" / 0x"+str(hex(regboundaries[i+1])))
    exit(-1)
print("[VMLC] No overlaps found.")
try:
  os.remove("reg.txt")
  os.remove("cfile.c")
  os.remove("cfile")
except FileNotFoundError:
  pass
