#! /usr/bin/env python3

import sys
import socket
import json
import hashlib
import re
import pprint

if len(sys.argv) < 2:
   print("usage: genproto file")
   sys.exit(1)

filename = sys.argv[1]

proto = {}
proto['pkts'] = {}
ops_used = set()
max_op = 0
consts = []
with open(filename, "r") as file:
   pktname = None
   for line in file:
      line = re.sub(re.compile("#.*" ) ,"" , line).strip()
      words = re.split ("\s+", line)
      if len(words) == 0:
         continue
      elif words[0] == "":
         continue
      elif words[0] == "const":
         if len(words) != 3:
            print("invalid const:", words)
            sys.exit(1)
         name = words[1]
         val = int(words[2])
         consts.append((name, val))
         proto[name] = val;
      elif words[0] == "pkt":
         if len(words) != 3:
            print("invalid pkt:", words)
            sys.exit(1)
         pktname = words[1]
         op = int(words[2])
         if op != 0 and op in ops_used:
            print("duplicate op", words)
            sys.exit(1)
         ops_used.add(op)
         if op > max_op:
            max_op = op
         pkt = {}
         pkt['op'] = op
         pkt['fields'] = []
         proto['pkts'][pktname] = pkt
      else:
         bits = int(words[0])
         field = words[1]
         proto['pkts'][pktname]['fields'].append(dict (field=field, bits=bits))

print("max op", max_op)

basename = "proto-gen"

with open(basename+".json", "w") as file:
   json.dump(proto, file, indent=4)
   file.write ("\n")

with open(basename+".h", "w") as file:
   file.write ('#include "proto-common.h"\n')
   file.write("\n")
   for c in consts:
      file.write("#define {} {}\n".format(c[0], c[1]))
   file.write("\n")
   for pktname, pdata in proto['pkts'].items():
      struct_name = "proto_"+pktname
      file.write("#define OP_"+pktname.upper()+" "+str(pdata['op'])+"\n")
      file.write ("struct "+struct_name+" {\n")
      total_bits = 0
      for fdata in pdata['fields']:
         field = fdata['field']
         bits = fdata['bits']
         total_bits += bits
         if bits <= 8:
            type = "uint8_t"
         elif bits <= 16:
            type = "uint16_t"
         else:
            type = "uint32_t"
      
         file.write ("\t"+type+" "+field+";\n")
      file.write ("};\n")
      file.write ("extern struct proto_desc "+struct_name+"_desc;\n")
      file.write ("#define PROTO_{}_NBITS {}\n".format(pktname.upper(), 
                                                        total_bits))
      file.write ("\n")

with open(basename+".c", "w") as file:
   file.write('#include <stdio.h>\n')
   file.write('#include <stdint.h>\n')
   file.write('#include <stddef.h>\n')
   file.write('#include "proto-gen.h"\n')
   for pktname, pdata in proto['pkts'].items():
      struct_name = "proto_"+pktname
      desc_name = struct_name+"_desc"
      fields_name = struct_name+"_fields"
      fields = pdata['fields']

      file.write("struct field_desc "+fields_name+"[] = {\n")
      for fdata in fields:
         field = fdata['field']
         bits = fdata['bits']
         file.write("\t{\n")
         file.write('\t\t"'+field+'",\n')
         file.write('\t\t'+str(bits)+',\n')
         file.write('\t\toffsetof(struct '+struct_name+', '+field+'),\n')
         file.write("\t},\n")
      file.write("};\n")

      file.write("struct proto_desc "+desc_name+" = {\n")
      file.write("\t")
      file.write('"'+pktname+'", ')
      file.write(str(len(fields)) + ", ")
      file.write(fields_name + ", ")
      file.write('sizeof (struct '+struct_name+'),')
      file.write("\n")
      file.write ("};\n\n")
      


