import os, sys

# Nice labels for algorithms of all kinds
ALG_LABEL = {
	"AES_128_GCM_SHA256": "AES128",
	"AES_256_GCM_SHA384": "AES256",
	"CHACHA20_POLY1305_SHA256": "CHACHA20",
	"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256": "AES128",
	"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384": "AES256",
	"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": "CHACHA20",
	"prime256v1": "prime256v1",
	"rsa2048": "rsa2048",
	"rsa3072": "rsa3072",
	"rsa4096": "rsa4096",
	"X25519": "X25519",
	"SECP256R1": "SECP256R1",
	"SECP384R1": "SECP384R1"
}

# Nice labels for TLS versions using ciphers
VER_LABEL = {
	"AES_128_GCM_SHA256": "1.3",
	"AES_256_GCM_SHA384": "1.3",
	"CHACHA20_POLY1305_SHA256": "1.3",
	"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,ECDHE_RSA_WITH_AES_128_GCM_SHA256": "1.2",
	"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,ECDHE_RSA_WITH_AES_256_GCM_SHA384": "1.2",
	"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": "1.2"
}

# Titles for measured quantities
OBJ_TITLE = {
	"cpu": "CPU time",
	"energy": "energy consumption"
}
# Logfile column names
COL = {
	"cpu": "cpu",
	"energy": "Wh",
	"cipher": "cipher",
	"cert": "alg",
	"kex": "kex"
}
# Physical units by object
UNIT = {
	"cpu": "s",
	"energy": "W"
}
# Titles for criteria
CRITERION_TITLE = {
	"cipher": "cipher",
	"cert": "signature algorithm",
	"kex": "key exchange"
}

# Where gnuplot files, data files and images are output
PLOTS_DIR = "/dev/shm/plots"

def gnuplot_histogram(**kwargs):
	cluster = ""
	for i in range(kwargs["nb_impls"]-1):
		cluster += """, "" using {}:xticlabels(1) title col""".format(i+4)
	f = open("{plots_dir}/{object}_by_{criterion}_{side}.gnuplot".format(plots_dir=PLOTS_DIR, **kwargs), "w")
	f.write("""\
set terminal pngcairo enhanced font "CMU Sans Serif,11" fontscale 1.0 size 800, 600
set output "{plots_dir}/{object}_by_{criterion}_{side}.png"
set boxwidth 0.9 absolute
set style fill solid 1.0 border lt -1
set style histogram clustered gap 1 title textcolor lt -1
set style data histograms
set title font "CMU Sans Serif,12" "{object_title} by {criterion_title} ({side} side) ({unit})"
#set xtics border in scale 0,0 nomirror rotate by -45 autojustify
set xtics border in scale 0,0 nomirror autojustify
#set key fixed right top vertical Right noreverse noenhanced autotitle nobox
set key fixed left top vertical Left reverse noenhanced autotitle nobox
set colorbox vertical origin screen 0.9, 0.2 size screen 0.05, 0.6 front noinvert bdefault
set xrange [ * : * ] noreverse writeback
set yrange [ 0 : * ]
set grid y lt 1 lw .75 lc "gray"
plot \
	newhistogram "", "{plots_dir}/{object}_by_{criterion}_{side}.dat" using 2:xticlabels(1) notitle col, \
	newhistogram "", "{plots_dir}/{object}_by_{criterion}_{side}.dat" using 3:xticlabels(1) title col{cluster}
""".format(plots_dir=PLOTS_DIR, cluster=cluster, **kwargs))
	f.close()
	os.system("gnuplot {plots_dir}/{object}_by_{criterion}_{side}.gnuplot".format(plots_dir=PLOTS_DIR, **kwargs))

def make_plot(records, exp, criterion, side, obj):
	f = open(f"/dev/shm/plots/{obj}_by_{criterion}_{side}.dat", "w")
	ciphers = {}
	impls = []
	plain_line = None
	idle_val = None
	
	for record in records:
		if record["exp"] == "idle":
			idle_val = float(record[COL[obj]]) / float(record["time"])
		if record["exp"] != exp:
			continue
		if record["setup"] == "none":
			plain_line = "plain {}".format(float(record[COL[obj]]) - idle_val * float(record["time"]))
	
	if plain_line == None:
		return
	
	for record in records:
		if record["exp"] != exp:
			continue
		elif record["setup"] == side:
			if record[COL[criterion]] not in ciphers:
				ciphers[record[COL[criterion]]] = {}
			ciphers[record[COL[criterion]]][record["impl"]] = float(record[COL[obj]]) - idle_val * float(record["time"])
			if record["impl"] not in impls:
				impls.append(record["impl"])
	impls.sort()
	f.write("{} none {}\n".format(criterion, " ".join(impls)))
	f.write(plain_line+" -"*len(impls)+"\n")
	for cipher in ciphers:
		f.write("{}({}) - {}\n".format(
			ALG_LABEL[cipher],
			VER_LABEL[record["cipher"]],
			" ".join([
				str(ciphers[cipher][impl])
				for impl in impls
			]),
		))
	f.close()
	gnuplot_histogram(object=obj, criterion=criterion, side=side, object_title=OBJ_TITLE[obj], criterion_title=CRITERION_TITLE[criterion], unit=UNIT[obj], nb_impls=len(impls))

if __name__ == "__main__":
	logfile_name = sys.argv[1]
	logfile = open(logfile_name, "r")
	lines = logfile.readlines()
	logfile.close()

	colnames = lines[0].removesuffix("\n").split(" ")

	records = []
	for line in lines[1:]:
		cols = line.removesuffix("\n").split(" ")
		record = {}
		for col in range(len(cols)):
			record[colnames[col]] = cols[col]
		records.append(record)
	
	os.makedirs("/dev/shm/plots", exist_ok=True)

	for side in ["client", "server"]:
		make_plot(records, "impl-cipher-ver", "cipher", side, "cpu")
		make_plot(records, "impl-cipher-ver", "cipher", side, "energy")
		make_plot(records, "impl-cert-ver", "cert", side, "cpu")
		make_plot(records, "impl-cert-ver", "cert", side, "energy")
		make_plot(records, "impl-kex-ver", "kex", side, "cpu")
		make_plot(records, "impl-kex-ver", "kex", side, "energy")
