# steal some ideas from config_crunch.py
import subprocess
import zlib
import struct
import hashlib

def chunk(l, flag=1):
	'''
	l: list of 16-bit ints
	flag: type
	Each chunk has an ID that preceeds it
	ID is 16-bit {2 bits: type, 14 bits: length of the chunk}
	'''
	chunk_size = len(l)
	if chunk_size >= (1 << 14):
		raise Exception
	else:
		return [chunk_size + (flag << 14)] + l

def sixteen(ss):
	if len(ss) % 2:  # need an even number of octets
		ss += "\0"
	return list(struct.unpack("!"+"H"*(len(ss)/2), ss))

def compress_file(fname):
	sha = hashlib.sha1()
	fv = open(fname).read()
	sha.update(fv)
	file_zip = zlib.compress(fv, 9)
	return sha.hexdigest(), sixteen(file_zip)

def create_array(descrip, json_file):
	git_sha = subprocess.check_output(['git', 'rev-parse', 'HEAD'])
	git_binary = [int(git_sha[ix*4+0:ix*4+4], 16) for ix in range(10)]
	sha1sum, regmap = compress_file(json_file)
	json_sha1_binary = [int(sha1sum[ix*4+0:ix*4+4], 16) for ix in range(10)]
	descrip_binary = sixteen(descrip)
	final = (chunk(json_sha1_binary, flag=2) + chunk(git_binary, flag=2) +
		chunk(descrip_binary) + chunk(regmap, flag=3))
	return final

def decode_array(a):
	rec_num = 0
	result = []
	while len(a):
		clen = a[0]
		flag = clen >> 14
		clen = clen & 0x3fff
		data = a[1:clen+1]
		a = a[clen+1:]
		# print clen, flag
		if flag == 0:
			break
		print("Record %d type %d length %d" % (rec_num, flag, clen))
		if flag == 1:
			# print struct.pack("!"+"H"*len(data), *data)
			result += [struct.pack("!"+"H"*len(data), *data)]
		elif flag == 2:
			# print "".join([format(x, "04x") for x in data])
			result += ["".join([format(x, "04x") for x in data])]
		elif flag == 3:
			zipped = struct.pack("!"+"H"*len(data), *data)
			# print zlib.decompress(zipped)
			result += [zlib.decompress(zipped)]
		rec_num += 1
	return result

def verilog_rom(a):
	print("%d/%d ROM entries used" % (len(a), 2048))
	if len(a) > 2048:
		return ""
	config_case = '\n'.join(['''\t11'h%3.3x: dxx <= 16'h%4.4x;''' % (ix, a[ix]) for ix in range(len(a))])
	return '''// 2K x 16 ROM machine generated by python verilog_rom()
module config_romx(
	input clk,
	input [10:0] address,
	output [15:0] data
);
reg [15:0] dxx = 0;
assign data = dxx;
always @(posedge clk) case(address)
''' + config_case + '''
	default: dxx = 0;
endcase
endmodule
'''

def read_live_array(prc):
	foo = prc.reg_read_alist(range(0x800, 0xfff))
	uuu = [struct.unpack('!HH', x[2]) for x in foo]
	return [x[1] for x in uuu]

if __name__ == "__main__":
	import argparse
	parser = argparse.ArgumentParser(description='Read/Write from FPGA memory')
	parser.add_argument('--loopback', dest='loopback',
		help='Encode and decode config_rom array',
		action='store_true', default=False)
	parser.add_argument('--live', help='Read config_rom from device,'
		'decode and print', dest='live',
		action='store_true', default=False)
	parser.add_argument('-a', '--ip', help='ip_address', dest='ip',
		type=str, default='192.168.21.11')
	parser.add_argument('-p', '--port', help='port', dest='port', type=int,
		default=50006)
	parser.add_argument('-v', '--verilog_file', dest='verilog_file',
		help='Destination config_rom filename',
		type=str, default='')
	parser.add_argument('-j', '--json', dest='json',
		help='Register map filename',
		type=str, default='prc_regmap.json')
	args = parser.parse_args()
	if args.live:
		from prc import c_prc
		ip_addr = args.ip
		port = args.port
		prc = c_prc(ip_addr, port)
		a = read_live_array(prc)
		# print " ".join(["%4.4x"%x for x in a])
		r = decode_array(a)
		for rr in r: print(rr)
	else:
		a = create_array("LBNL LCLS-II LLRF Test stand support", args.json)
		if args.loopback:
			r = decode_array(a)
			for rr in r: print(rr)
		elif args.verilog_file == '':
			print(len(a))
			print(a)
		else:
			with open(args.verilog_file, 'w') as f:
				f.write(verilog_rom(a))
