#!/usr/bin/env python3

# ----------------------------------------------------------------------------
# Copyright 2023 CEA*
# *Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA)
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#[END OF HEADER]
# ----------------------------------------------------------------------------


import sys
import json
import argparse
import os

#######################################################
################## OUTPUT FILE HEADER #################
#######################################################
header = [
"//Copyright 2023 CEA*",
"//*Commissariat a l'Energie Atomique et aux Energies Alternatives (CEA)",
"//",
"//Licensed under the Apache License, Version 2.0 (the \"License\");",
"//you may not use this file except in compliance with the License.",
"//You may obtain a copy of the License at",
"//",
"//    http://www.apache.org/licenses/LICENSE-2.0",
"//",
"//Unless required by applicable law or agreed to in writing, software",
"//distributed under the License is distributed on an \"AS IS\" BASIS,",
"//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.",
"//See the License for the specific language governing permissions and",
"//limitations under the License.",
"// ",
"// ",
"// ",
"// ",
"//  File	: output_example_class.svh",
"//",
"//  Description : Example for param module",
"//",
"//  Author	   : Generated by the script "+sys.argv[0]+"",
"//",
"//  Id	   	: $Id$",
"//  Date	: $Date$",
"//  Revision  	: $Rev$",
"//",
"// ----------------------------------------------------------------------------",
"\n"]

#######################################################
################## Argument parsing ###################
#######################################################
parser = argparse.ArgumentParser(description='Input/Output options')
parser.add_argument('--json', dest='input_json', type=str, help='Name of input json file')
parser.add_argument('--i', dest='input_template', type=str, action='append',  nargs='*',help='Name of input_template')
parser.add_argument('--o', dest='output_file_prefix', type=str, action='append',  nargs='*', help='Name of output_file_prefix')
args = parser.parse_args()

def list_to_string_f(input_list):
	tmp_list_index = []
	tmp_list = []
	
	for j in range (0,len(str(input_list))):
		if str(input_list)[j] == ",":
			tmp_list_index.append(''.join(tmp_list))
			tmp_list.clear()				
		else:
			tmp_list.append(str(input_list)[j])
				
		if j == len(str(input_list))-1:
			tmp_list_index.append(''.join(tmp_list))
			tmp_list.clear()				
	
	return(tmp_list_index)


if(len(list_to_string_f(args.input_template)) != len(list_to_string_f(args.output_file_prefix))):
	sys.stderr.write("[ERROR] --i and --o options must be the same size [ERROR]\n")	 


#######################################################
#################### FILE HANDLING ####################
#######################################################
number_of_parameter = 0
number_of_contraint = 0
json_data = []
working_list = []
working_index1 = []
working_index2 = []

# Input file JSON file: list of parameters and associated constraints #
filename_json = args.input_json
file_param=open(filename_json,"r")

# Template file #
filename = args.input_template

# output SV file: Generated SV class #
file_out=open("output_example_class.svh","w")


# Parsing json input file #
json_data = json.load(file_param)

# Determine the numbers of parameters and the numbers of constraints #
number_of_parameter = len(json_data['PARAMETERS'])
if 'CROSS_CONSTRAINTS' in json_data:
	number_of_contraint = len(json_data['CROSS_CONSTRAINTS'])
else:
	number_of_contraint = 0


#######################################################
#### PRE-PROCESSING FOR ENUM AND MIN/MAX MANAGEMENT ###
#######################################################
def enum_index_f(data, index):
	tmp_enum_index = []
	tmp_enum = []

	for j in range (0,len(data['PARAMETERS'][index]['Attributes'][0]['VALUES'])):
		if data['PARAMETERS'][index]['Attributes'][0]['VALUES'][j] == ",":
			tmp_enum_index.append(''.join(tmp_enum))
			tmp_enum.clear()				
		else:
			tmp_enum.append(data['PARAMETERS'][index]['Attributes'][0]['VALUES'][j])
				
		if j == len(data['PARAMETERS'][index]['Attributes'][0]['VALUES'])-1:
			tmp_enum_index.append(''.join(tmp_enum))
			tmp_enum.clear()				
	
	return(tmp_enum_index)


def enum_val_f(index):
	tmp_enum = []
	tmp_enum_val = []
	
	for i in range (0, len(index)):
		tmp_enum=index[i].split('=')
		tmp_enum_val.append(tmp_enum[0])
		tmp_enum.clear()				
	
	return(tmp_enum_val)

# function to produce the list of MIN and MAX #
def min_max_f(data, index):
	working_list.clear()
	for j in range (int(data['PARAMETERS'][index]['Attributes'][0]['MIN']), int(data['PARAMETERS'][index]['Attributes'][0]['MAX'])+1):
		working_list.append(j)	
	return(str(working_list).translate({ord(k): None for k in '[]'}))



int_index = []
int_tmp = []
int_val = []
def first_last_f(data, index):
	int_index.clear()
	int_tmp.clear()
	int_val.clear()	
	if 'VALUES' in data['PARAMETERS'][index]['Attributes'][0]:
		for j in range (0,len(data['PARAMETERS'][index]['Attributes'][0]['VALUES'])):
			if data['PARAMETERS'][index]['Attributes'][0]['VALUES'][j] == ",":
				int_index.append(''.join(int_tmp))
				int_tmp.clear()				
				
			else:
				int_tmp.append(data['PARAMETERS'][index]['Attributes'][0]['VALUES'][j])
				
			if j == len(data['PARAMETERS'][index]['Attributes'][0]['VALUES'])-1:
				int_index.append(''.join(int_tmp))
				int_tmp.clear()				

	return(int_index)

		
#######################################################
######### CLASS DECLARATION / CONSTRUCTOR #############
#######################################################
# write_out_file will contain the text which will be written into the output file #
write_out_file = ["class Design_params extends uvm_object;"]
write_out_file = write_out_file + ["\n"]		

write_out_file = write_out_file + ["\t//Constructor"]
write_out_file = write_out_file + ["\tfunction new(string name=\"Design_params\",uvm_component parent = null );"]
write_out_file = write_out_file + ["\t\tsuper.new(name);"]
write_out_file = write_out_file + ["\tendfunction"]
write_out_file = write_out_file + ["\n"]		



for i in range (0,number_of_parameter):
	if 'VALUES' in json_data['PARAMETERS'][i]['Attributes'][0]:
		if json_data['PARAMETERS'][i]['Attributes'][0]['TYPE'] == "enum":
			write_out_file = write_out_file + ["\ttypedef enum int {"]
			for p in range (0, len(enum_index_f(json_data,i))-1):
				write_out_file = write_out_file + ["\t\t"+enum_index_f(json_data,i)[p]+","]		
			write_out_file = write_out_file + ["\t\t"+enum_index_f(json_data,i)[len(enum_index_f(json_data,i))-1]+""]		
			write_out_file = write_out_file + ["\t\t} "+json_data['PARAMETERS'][i]['Name']+"_t;"]		
			write_out_file = write_out_file + ["\n"]		

write_out_file = write_out_file + ["\ttemplate_management	template_writer;"]		
write_out_file = write_out_file + ["\n"]		


#######################################################
############# PARAMETERS DECLARATION ##################
#######################################################
for i in range (0,number_of_parameter):
	if json_data['PARAMETERS'][i]['Attributes'][0]['TYPE'] == "enum":
		write_out_file = write_out_file + ["\trand "+ json_data['PARAMETERS'][i]['Name']+ "_t " + json_data['PARAMETERS'][i]['Name']+";"]		
	else:
		write_out_file = write_out_file + ["\trand "+ json_data['PARAMETERS'][i]['Attributes'][0]['TYPE']+ " " + json_data['PARAMETERS'][i]['Name']+";"]		
write_out_file = write_out_file + ["\n"]		

#######################################################
################# PARAMETERS CONSTRAINTS ##############
#######################################################
for i in range (0,number_of_parameter):
	if 'VALUES' in json_data['PARAMETERS'][i]['Attributes'][0]:
		if json_data['PARAMETERS'][i]['Attributes'][0]['TYPE'] == "enum":
			write_out_file = write_out_file + ["\tconstraint C_VALUES_" + json_data['PARAMETERS'][i]['Name']+ " {" + json_data['PARAMETERS'][i]['Name']+ " inside {" +str(enum_val_f(enum_index_f(json_data,i))).translate({ord(k): None for k in '[]\''})+ "};};"]
		else:
			write_out_file = write_out_file + ["\tconstraint C_VALUES_" + json_data['PARAMETERS'][i]['Name']+ " {" + json_data['PARAMETERS'][i]['Name']+ " inside {" +  json_data['PARAMETERS'][i]['Attributes'][0]['VALUES'] + "};};"]
	elif ('MIN' in json_data['PARAMETERS'][i]['Attributes'][0]) and ('MAX' in json_data['PARAMETERS'][i]['Attributes'][0]):
		write_out_file = write_out_file + ["\tconstraint C_VALUES_" + json_data['PARAMETERS'][i]['Name']+ " {" + json_data['PARAMETERS'][i]['Name']+  " inside {" +  min_max_f(json_data,i) + "};};"]

write_out_file = write_out_file + ["\n"]		

for i in range (0,number_of_contraint):
	write_out_file = write_out_file + ["\tconstraint " + json_data['CROSS_CONSTRAINTS'][i]['Name']+ " {" +  json_data['CROSS_CONSTRAINTS'][i]['Constraint'] + ";};"]
write_out_file = write_out_file + ["\n"]		


#######################################################
#################### FUNCTIONS ########################
#######################################################

# function RAMDOM #
write_out_file = write_out_file + ["\tfunction void RAMDOM(DEBUG = 0);"]
write_out_file = write_out_file + ["\t\tstring Param_array[string];"]
write_out_file = write_out_file + ["\t\tstring tmp_val;"]
write_out_file = write_out_file + ["\t\tstring tmp_file_name;"]
write_out_file = write_out_file + ["\t\ttemplate_writer=new();"]
write_out_file = write_out_file + ["\n"]		

write_out_file = write_out_file + ["\t\twhile(!this.randomize()) begin"]
write_out_file = write_out_file + ["\t\t\t$display(\"DEBUG_RAMDOM: randomize FAILED\\n\");"]
write_out_file = write_out_file + ["\t\tend"]

tabs = "\t\t"
for j in range (0,number_of_parameter):
	if json_data['PARAMETERS'][j]['Attributes'][0]['TYPE'] == "enum":
		write_out_file = write_out_file + [tabs+"Param_array[\"$"+json_data['PARAMETERS'][j]['Name']+"\"] = "+json_data['PARAMETERS'][j]['Name']+".name();"]
	else:
		write_out_file = write_out_file + [tabs+"tmp_val.itoa("+json_data['PARAMETERS'][j]['Name']+");"]
		write_out_file = write_out_file + [tabs+"Param_array[\"$"+json_data['PARAMETERS'][j]['Name']+"\"] = tmp_val;"]

write_out_file = write_out_file + [tabs+"if(DEBUG) begin"]		
for j in range (0,number_of_parameter):
	if json_data['PARAMETERS'][j]['Attributes'][0]['TYPE'] == "enum":
		write_out_file = 	write_out_file + [tabs+"\t$display(\"DEBUG"+str(j)+":"+json_data['PARAMETERS'][j]['Name']+":%s\\n\","+json_data['PARAMETERS'][j]['Name']+");"]
	else:
		write_out_file = 	write_out_file + [tabs+"\t$display(\"DEBUG"+str(j)+":"+json_data['PARAMETERS'][j]['Name']+":%d\\n\","+json_data['PARAMETERS'][j]['Name']+");"]	
write_out_file = write_out_file + [tabs+"end"]		

for j in range(0,len(list_to_string_f(args.input_template))):
	if not os.path.exists(list_to_string_f(args.output_file_prefix)[j].translate({ord(k): None for k in '[]\'\" '})):
		os.mkdir(list_to_string_f(args.output_file_prefix)[j].translate({ord(k): None for k in '[]\'\" '}))
	write_out_file = write_out_file + [tabs+"tmp_file_name = $sformatf(\""+list_to_string_f(args.output_file_prefix)[j].translate({ord(k): None for k in '[]\'\" '})+"/"+list_to_string_f(args.output_file_prefix)[j].translate({ord(k): None for k in '[]\'\" '})+"_OUTPUT_RAMDOM.txt\");"]

	write_out_file = write_out_file + [tabs+"template_writer.write(Param_array, \""+list_to_string_f(args.input_template)[j].translate({ord(k): None for k in '[]\'\" '})+"\",tmp_file_name);"]

write_out_file = write_out_file + ["\tendfunction:RAMDOM"]
write_out_file = write_out_file + ["\n"]		


# function Iterate_EXHAUSTIVE_or_CORNER #
write_out_file = write_out_file + ["\tfunction void Iterate_EXHAUSTIVE_or_CORNER(int EXHAUST_not_CORNER, DEBUG = 0);"]
write_out_file = write_out_file + ["\t\tstring Param_array[string];"]

for i in range (0,number_of_parameter):
	if 'VALUES' in json_data['PARAMETERS'][i]['Attributes'][0]:
		write_out_file = write_out_file + ["\t\tint " + json_data['PARAMETERS'][i]['Name']+ "_val_list[$];"]		
	elif ('MIN' in json_data['PARAMETERS'][i]['Attributes'][0]) and ('MAX' in json_data['PARAMETERS'][i]['Attributes'][0]):
		write_out_file = write_out_file + ["\t\tint " + json_data['PARAMETERS'][i]['Name']+ "_val_list[$];"]
write_out_file = write_out_file + ["\t\tstring tmp_val;"]
write_out_file = write_out_file + ["\t\tstring tmp_file_name;"]
write_out_file = write_out_file + ["\t\ttemplate_writer=new();"]
write_out_file = write_out_file + ["\n"]		

write_out_file = write_out_file + ["\t\tif(EXHAUST_not_CORNER) begin // EXHAUSTIVE mode"]
for i in range (0,number_of_parameter):
	if 'VALUES' in json_data['PARAMETERS'][i]['Attributes'][0]:
		if json_data['PARAMETERS'][i]['Attributes'][0]['TYPE'] == "enum":
			write_out_file = write_out_file + ["\t\t\t" +json_data['PARAMETERS'][i]['Name']+ "_val_list = {" +str(enum_val_f(enum_index_f(json_data,i))).translate({ord(k): None for k in '[]\''})+ "};"]				
		else:
			write_out_file = write_out_file + ["\t\t\t" +json_data['PARAMETERS'][i]['Name']+ "_val_list = {" +json_data['PARAMETERS'][i]['Attributes'][0]['VALUES']+ "};"]		
	elif ('MIN' in json_data['PARAMETERS'][i]['Attributes'][0]) and ('MAX' in json_data['PARAMETERS'][i]['Attributes'][0]):
		write_out_file = write_out_file + ["\t\t\t" + json_data['PARAMETERS'][i]['Name']+ "_val_list = {" +   min_max_f(json_data,i) + "};"]
write_out_file = write_out_file + ["\t\tend"]		

write_out_file = write_out_file + ["\t\telse begin // CORNER mode"]		
for i in range (0,number_of_parameter):
	if 'VALUES' in json_data['PARAMETERS'][i]['Attributes'][0]:
		if json_data['PARAMETERS'][i]['Attributes'][0]['TYPE'] == "enum":
			write_out_file = write_out_file + ["\t\t\t" +json_data['PARAMETERS'][i]['Name']+ "_val_list = {" +enum_val_f(enum_index_f(json_data,i))[0]+ ", "+enum_val_f(enum_index_f(json_data,i))[-1]+"};"]			
		else:
			write_out_file = write_out_file + ["\t\t\t" +json_data['PARAMETERS'][i]['Name']+ "_val_list = {" +first_last_f(json_data,i)[0]+ ", "+first_last_f(json_data,i)[-1]+"};"]		
	elif ('MIN' in json_data['PARAMETERS'][i]['Attributes'][0]) and ('MAX' in json_data['PARAMETERS'][i]['Attributes'][0]):
		write_out_file = write_out_file + ["\t\t\t" +json_data['PARAMETERS'][i]['Name']+ "_val_list = {" +json_data['PARAMETERS'][i]['Attributes'][0]['MIN']+ ", "+json_data['PARAMETERS'][i]['Attributes'][0]['MAX']+"};"]		
write_out_file = write_out_file + ["\t\tend"]		
write_out_file = write_out_file + ["\n"]		

for i in range (0,number_of_parameter):
	tabs = "\t"*(i) + "\t\t"
	write_out_file = write_out_file + [tabs+"foreach("+json_data['PARAMETERS'][i]['Name']+ "_val_list[index_"+str(i)+"]) begin"]
	working_index1 = working_index1 + ["%0d_"]	
	working_index2 = working_index2 + ["index_"+str(i)+""]
tabs = tabs + "\t"
write_out_file = write_out_file + [tabs+"if(this.randomize() with {"]
tabs = tabs + "\t"
for j in range (0,number_of_parameter):
	write_out_file = write_out_file + [tabs+json_data['PARAMETERS'][j]['Name']+" == "+ json_data['PARAMETERS'][j]['Name']+ "_val_list[index_"+str(j)+"];"]
write_out_file = write_out_file + [tabs+"}) begin"]

tabs = tabs + "\t"
for j in range (0,number_of_parameter):
	if json_data['PARAMETERS'][j]['Attributes'][0]['TYPE'] == "enum":
		write_out_file = write_out_file + [tabs+"Param_array[\"$"+json_data['PARAMETERS'][j]['Name']+"\"] = "+json_data['PARAMETERS'][j]['Name']+".name();"]
	else:
		write_out_file = write_out_file + [tabs+"tmp_val.itoa("+json_data['PARAMETERS'][j]['Name']+");"]
		write_out_file = write_out_file + [tabs+"Param_array[\"$"+json_data['PARAMETERS'][j]['Name']+"\"] = tmp_val;"]
	

write_out_file = write_out_file + [tabs+"if(DEBUG) begin"]		
for j in range (0,number_of_parameter):
	if json_data['PARAMETERS'][j]['Attributes'][0]['TYPE'] == "enum":
		write_out_file = write_out_file + [tabs+"\t$display(\"DEBUG"+str(j)+":"+json_data['PARAMETERS'][j]['Name']+":%s\\n\","+json_data['PARAMETERS'][j]['Name']+");"]
	else:
		write_out_file = write_out_file + [tabs+"\t$display(\"DEBUG"+str(j)+":"+json_data['PARAMETERS'][j]['Name']+":%d\\n\","+json_data['PARAMETERS'][j]['Name']+");"]		
write_out_file = write_out_file + [tabs+"end"]		

for j in range(0,len(list_to_string_f(args.input_template))):
	if not os.path.exists(list_to_string_f(args.output_file_prefix)[j].translate({ord(k): None for k in '[]\'\" '})):
		os.mkdir(list_to_string_f(args.output_file_prefix)[j].translate({ord(k): None for k in '[]\'\" '}))
	write_out_file = write_out_file + [tabs+"tmp_file_name = $sformatf(\""+list_to_string_f(args.output_file_prefix)[j].translate({ord(k): None for k in '[]\'\" '})+"/"+list_to_string_f(args.output_file_prefix)[j].translate({ord(k): None for k in '[]\'\" '})+"_OUTPUT_"+str(working_index1).translate({ord(k): None for k in '[]\', '})+".txt\","+str(working_index2).translate({ord(k): None for k in '[]\''})+");"]

	write_out_file = write_out_file + [tabs+"template_writer.write(Param_array, \""+list_to_string_f(args.input_template)[j].translate({ord(k): None for k in '[]\'\" '})+"\",tmp_file_name);"]


tabs=tabs[:-1]
write_out_file = write_out_file + [tabs+"end"]


for i in range (0,number_of_parameter):
	tabs = "\t"*((number_of_parameter-1)-i+2)
	write_out_file = write_out_file + [tabs+"end"]
write_out_file = write_out_file + ["\n"]		

		
write_out_file = write_out_file + ["\tendfunction:Iterate_EXHAUSTIVE_or_CORNER"]
write_out_file = write_out_file + ["\n"]		


#######################################################
################### END OF CLASS ######################
#######################################################
write_out_file = write_out_file + ["endclass"]		


#######################################################
############### WRITE INTO OUTPUT FILE ################
#######################################################
for index in header:
	file_out.writelines(index)
	file_out.write("\n")

for index in write_out_file:
	file_out.writelines(index)
	file_out.write("\n")
