#!/usr/bin/python

import os, re
import pickle
from alin import *

#DEFAULT_PATH = "/home/projects/alba-em/deviceslib/"

class Generator():
	def __init__(self):
		self.wboutput = {}
		
		self.default_path = os.path.dirname(alin.__file__)+"/deviceslib/"
		if not os.path.exists(self.default_path):
			os.makedirs(self.default_path)

	def processWBData(self, devname='', vendorID='', product='', wb_filename='', output_filename =''):
		self.wboutput = {}
		
		fread = []
		try:
			with open(wb_filename) as f:
				fread = [" ".join(l.split()) for l in f]
		except Exception, e:
			print "Error while opening in wb file: %s"%str(e)
			return
		
		self.wboutput = {}
		self.wboutput["product"] = product
		self.wboutput["vendorid"] = vendorID
		self.wboutput["devname"] = devname
		
		offset = 0
		l = 0
		while l<len(fread):
			# Read device description
			if "name = " in fread[l] or "description =" in fread[l]:
				if fread[l].split()[0] not in self.wboutput.keys():
					self.wboutput[fread[l].split()[0]] = re.search('"(.*)"',fread[l].split("=")[1]).group(1)
				else:
					self.wboutput[fread[l].split()[0]] += re.search('"(.*)"',fread[l].split("=")[1]).group(1)
					
			# Read registers				
			elif "reg {" in fread[l]:
				reg_name = ""
				bit_position = 0
				l += 1
				while "};" not in fread[l]:

					if "align" in fread[l]:
						offset += int(fread[l].split("=")[1].replace(";",""))
					elif "prefix" in fread[l]:
						reg_name += re.search('"(.*)"',fread[l].split("=",1)[1]).group(1)
					elif "field {" in fread[l]:
						l += 1

						size = 0
						type = ""
						description = ""
						field_name = ""
						store_reg = True
						while "};" not in fread[l]:
							if "prefix =" in fread[l]:
								if "reserved" in fread[l].lower():
									store_reg = False							
								else:
									field_name = re.search('"(.*)"',fread[l].split("=",1)[1]).group(1)
									store_reg = True
							elif "access_bus =" in fread[l]:
								access = fread[l].split("=")[1].replace(";","")
							elif "type = " in fread[l] and "BIT" in fread[l]:
								size = 1
							elif "size = " in fread[l]:
								size = int(fread[l].split("=")[1].replace(";",""))
							elif "description = " in fread[l]:
								description = re.search('"(.*)"',fread[l].split("=",1)[1]).group(1)
							elif "align" in fread[l]:
								bit_offset = int(fread[l].split("=")[1].replace(";",""))
								if bit_position % bit_offset: 
									ad = bit_offset - (bit_position % bit_offset)
									bit_position += ad
							l +=1

						if store_reg:
							data = {}
							if field_name != "":
								reg = reg_name+"_"+field_name
							else:
								reg = reg_name
							data[reg]={}
							data[reg]["bit_position"] = bit_position
							data[reg]["size"] = size
							data[reg]["access"] = access
							data[reg]["description"] = description
							bit_position += size
							data[reg]["address"] = offset

							if "regs" not in self.wboutput.keys():
								self.wboutput["regs"]=[data]
							else:
								self.wboutput["regs"].append(data)
							
					l+=1
				
				offset +=1
			# Increment line counter
			l += 1
		
		# Write output to file
		file_generated = True
		try:
			with open(self.default_path+output_filename,"w") as f:
				pickle.dump(self.wboutput, f)
		except Exception, e:
			print str(e)
			file_generated = False
			
		return file_generated

	def removeDevice(self, devname=''):
		if os.path.isfile(self.default_path+devname):
			os.remove(self.default_path+devname)
			return True
		return False


def getInputVendorID():
	vendorID = None
	while not vendorID:
		vendorID = raw_input ("\nEnter VENDOR ID code (hex format, max. 8 bytes)" \
			"(i.e: a1ba for Alba or ce42 for CERN) or enter 'Q' to quit: ")
		if vendorID.lower() == "q":
			print "\nProgram aborted!!!\n"
			return None
		try:
			vendorID = int(vendorID.lower(),16)
			if vendorID > 0xffffffffffffffff:
				print "Number exceeds maximum value. Please enter hexadecimal " \
					"(max. 8 bytes) vendor ID value!!\n "
				vendorID = None
			else:
				return vendorID
		except:
			print "Incorrect number format!!! Please enter hexadecimal vendor ID value!!\n"
	
	return vendorID

def getInputProduct():
	product = None
	while not product:
		product = raw_input ("\nEnter PRODUCT (hex format, max 4 bytes)" \
			"or enter 'Q' to quit: ")
		if product.lower() == "q":
			print "\nProgram aborted!!!\n"
			return None
		try:
			product = int(product.lower(),16)
			if product > 0xffffffff:
				print "Number exceeds maximum value. Please enter hexadecimal "\
					"(max. 4 bytes) product value!!\n"
				product = None
			else:
				return product
		except:
			print "Incorrect number format!!! Please enter hexadecimal product value!!\n"
	
	return product

def getInputName():
	devname = None
	while not devname:
		devname = raw_input ("\nEnter assigned DEVICE_NAME (max. lenght 10 chars) or enter 'Q' to quit: ")
		if devname.lower() == "q":
			print "\nProgram aborted!!!\n"
			return None
		devname = devname.replace(" ","_")
		if len(devname)>10:
			print "Name exceeds maximum lenght allowed.\nPlease enter name with a lenght lower than 10 characters\n"
			devname = None
		else:
			return devname.upper()

def generate_file(wb_filename):

	if not wb_filename.endswith(".wb"):
		print "\nIncorrect filename selected: %s"%wb_filename
	else:
		gen = Generator()
		
		# 1.- Build the devine name 
		try:
			vend =  getInputVendorID()
			if vend is None:
				sys.exit()
			prod = getInputProduct()
			if prod is None:
				sys.exit()
			name = getInputName()
			if name is None:
				sys.exit()
			#output_file = "%016x:%08x"%(vend,prod)
			output_file = name
		except Exception, e:
			output_file = None
			print str(e)
			
		if output_file is not None:
			# 2. Read the wb file
			if gen.processWBData(name, vend, prod, wb_filename, output_file):
				print "\nDevice file generated succesfully!!"			
				print "* Generated device name ==>\t%s\n"%name
			else:
				print "\nPROCESS ABORTED: Error while writing the output file!!\n"
		else:
			print "\nPROCESS ABORTED: Incorrect filename generated for ", wb_filename,"\n"

def remove_file(devname):
	gen = Generator()
	if gen.removeDevice(devname):
		print "\nFile %s succesfully deleted"%devname
	else:
		print "\nNot possible to delete %s. File does not exits"%devname

def help():
	BOLD = '\033[1m'
	END = '\033[0m'
	
	print "ALINGEN is a tool that generates a pickle file for a device that contains a dict with the device information extracted from the wishbone file."
	print "This wishbone file, passed as argument and used to generate the FPGA code block, contains the register mapping and detailed infor for a"
	print "particular FPGA block.\nIt should be used as follows\n"
	print BOLD+"\t# alingen <command_1> <wb_file>"+END
	print "\nList of available commands are:\n"
	print BOLD+"\t-g <wb_filename> or ---generate=<wb_filename>:"+END
	print "\t\tGenerates te picklefile for the specified wb file.\n"
	print BOLD+"\t--help:"+END
	print "\t\tShows this help\n"
	print BOLD+"\t-d <devname> or --delete=<devname>:"+END
	print "\t\tDeletes an specific device pickle file."
	print "\n"

if __name__ == "__main__":
	import sys, os.path
	import getopt
	
	try:
		short_cmds = "d:hg:"
		long_cmds = ["help","delete=","generate="]
		opts, args = getopt.getopt(sys.argv[1:], short_cmds, long_cmds)
	except getopt.GetoptError as err:
		print (err) # will print something like "option -a not recognized"
		print "\nFor detailed command help use: alin --help\n" 
		sys.exit(2)
		
	opts_dict = dict(opts)

	# 0- show help
	if "-h" in opts_dict.keys() or "--help" in opts_dict.keys():
		help()
		sys.exit()
	
	# 1- delete a device
	devname = None
	if "-d" in opts_dict.keys():
		devname = opts_dict["-d"]
	elif "--delete=" in opts_dict.keys():
		devname = opts_dict["--delete="]
	if devname is not None:
		remove_file(devname)
		sys.exit()

	#2- generate a device file
	wb_file = None
	if "-g" in opts_dict.keys():
		wb_file = opts_dict["-g"]
	elif "--generate=" in opts_dict.keys():
		wb_file = opts_dict["--generate="]
	if wb_file is not None:
		generate_file(wb_file)

	sys.exit()
