/*****************************************************************************
 *
 * Copyright (c) 2018-19, Eduard Gerlits, Paula Herber, Josef Treus
 * Westfälische Wilhelms-Universität Münster, AG Embedded Systems 
 * All rights reserved.
 *
 * This file is part of SysCSec
 *
 * SysCSec is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * SysCSec is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SysCSec.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 *  Please report any problems or bugs to: sysc.sec@uni-muenster.de
 *
 ****************************************************************************/

#include <iostream>
#include <systemc.h>
#include <math.h>
#include "commons.h"


SC_MODULE(InputFWModule){

	// out
	
	sc_fifo_out<sc_uint<4> > ecc_control;
	sc_fifo_out<sc_biguint<160> > data_in0;
	sc_fifo_out<sc_biguint<160> > data_in1;
	sc_fifo_out<sc_biguint<160> > data_in2;
	sc_fifo_out<sc_biguint<160> > data_in3;
	sc_fifo_out<sc_biguint<160> > data_in4;
	sc_fifo_out<sc_biguint<160> > data_in5;
	sc_fifo_out<sc_biguint<160> > data_in6;
	sc_fifo_out<sc_biguint<160> > data_in7;
	sc_fifo_out<sc_biguint<160> > data_in8;
	sc_fifo_out<sc_biguint<160> > data_in9;

	// in
	sc_fifo_in<sc_uint<2> > ecc_status;	
	sc_fifo_in<sc_biguint<160> > data_out0;
	sc_fifo_in<sc_biguint<160> > data_out1;
	sc_fifo_in<sc_biguint<160> > data_out2;
	sc_fifo_in<sc_biguint<160> > data_out3;
	sc_fifo_in<sc_biguint<160> > data_out4;
	sc_fifo_in<sc_biguint<160> > data_out5;

	void set_input(sc_uint<4> p_ecc_control, 
					sc_biguint<160> p_data_in0, 
					sc_biguint<160> p_data_in1, 
					sc_biguint<160> p_data_in2, 
					sc_biguint<160> p_data_in3,
					sc_biguint<160> p_data_in4,
					sc_biguint<160> p_data_in5,
					sc_biguint<160> p_data_in6,
					sc_biguint<160> p_data_in7,
					sc_biguint<160> p_data_in8,
					sc_biguint<160> p_data_in9)
	{
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "Write ecc_control: " << p_ecc_control << std::endl;
		ecc_control->write(p_ecc_control);		
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "Write data_in0: " << p_data_in0 << std::endl;
		data_in0->write(p_data_in0);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "Write data_in1: " << p_data_in1 << std::endl;
		data_in1->write(p_data_in1);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "Write data_in2: " << p_data_in2 << std::endl;
		data_in2->write(p_data_in2);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "Write data_in3: " << p_data_in3 << std::endl;
		data_in3->write(p_data_in3);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "Write data_in4: " << p_data_in4 << std::endl;
		data_in4->write(p_data_in4);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "Write data_in5: " << p_data_in5 << std::endl;
		data_in5->write(p_data_in5);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "Write data_in6: " << p_data_in6 << std::endl;
		data_in6->write(p_data_in6);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "Write data_in7: " << p_data_in7 << std::endl;
		data_in7->write(p_data_in7);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "Write data_in8: " << p_data_in8 << std::endl;
		data_in8->write(p_data_in8);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "Write data_in9: " << p_data_in9 << std::endl;
		data_in9->write(p_data_in9);
	}

	void print_output(){

	  //		sc_time t1(100, "SC_MS");
		sc_uint<2> ecc_status_in=1;
		sc_biguint<160> p_data_out0;
		sc_biguint<160> p_data_out1;
		sc_biguint<160> p_data_out2;
		sc_biguint<160> p_data_out3;
		sc_biguint<160> p_data_out4;
		sc_biguint<160> p_data_out5;

		while(ecc_status_in!=_FW_FINISHED_){
			ecc_status_in = ecc_status->read();
			wait(100, "SC_MS");
		}

		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "ecc_status: " << ecc_status_in << std::endl;
		data_out0->nb_read(p_data_out0);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "data_out0: " << p_data_out0 << std::endl;
		data_out1->nb_read(p_data_out1);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "data_out1: " << p_data_out1<< std::endl;
		data_out2->nb_read(p_data_out2);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "data_out2: " << p_data_out2 << std::endl;
		data_out3->nb_read(p_data_out3);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "data_out3: " << p_data_out3 << std::endl;
		data_out4->nb_read(p_data_out4);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "data_out4: " << p_data_out4 << std::endl;
		data_out5->nb_read(p_data_out5);
		std::cout << "[" << sc_time_stamp() << "]: " << "[InputFW]:" << "data_out5: " << p_data_out5 << std::endl;		
	}

	void input_test(sc_uint<4> p_ecc_control, 
					sc_biguint<160> p_data_in0, 
					sc_biguint<160> p_data_in1, 
					sc_biguint<160> p_data_in2, 
					sc_biguint<160> p_data_in3,
					sc_biguint<160> p_data_in4,
					sc_biguint<160> p_data_in5,
					sc_biguint<160> p_data_in6,
					sc_biguint<160> p_data_in7,
					sc_biguint<160> p_data_in8,
					sc_biguint<160> p_data_in9)
	{
		set_input(p_ecc_control, p_data_in0, p_data_in1, p_data_in2, p_data_in3, p_data_in4, p_data_in5, p_data_in6,p_data_in7, p_data_in8, p_data_in9);

		//print_output();
	}

	void key_pair_generate( 
					sc_biguint<160> p_data_in0, 
					sc_biguint<160> p_data_in1, 
					sc_biguint<160> p_data_in2, 
					sc_biguint<160> p_data_in3,
					sc_biguint<160> p_data_in4,
					sc_biguint<160> p_data_in5,
					sc_biguint<160> &d,
					sc_biguint<160> &q_x,
					sc_biguint<160> &q_y)
	{
	  //sc_time t1(100, "SC_MS");
		sc_uint<2> ecc_status_in=1;

		set_input(_FW_ECDSA_KEY_GENERATION_, p_data_in0, p_data_in1, p_data_in2, p_data_in3, p_data_in4, p_data_in5, 0, 0, 0, 0);

		while(ecc_status_in!=_FW_FINISHED_){
			ecc_status_in = ecc_status->read();
			wait(100, "SC_MS");
		}
		d = data_out0->read();
		q_x = data_out1->read();
		q_y = data_out2->read();
	}

	void pt_multiply(sc_biguint<160> p1_x, 
					sc_biguint<160> p1_y, 
					sc_biguint<160> val, 
					//sc_biguint<160> a, 
					//sc_biguint<160> m,  
					sc_biguint<160> &q_k_x, 
					sc_biguint<160> &q_k_y)
	{
	  //	sc_time t1(100, "SC_MS");
		sc_uint<2> ecc_status_in=1;

		set_input(_FW_PT_MULTIPLY_, p1_x, p1_y, val, _ECC_PARAM_A_, _ECC_PARAM_M_, 0, 0, 0, 0, 0);	
		while(ecc_status_in!=_FW_FINISHED_){
			ecc_status_in = ecc_status->read();
			wait(100, "SC_MS");
		}	
		q_k_x = data_out0->read();
		q_k_y = data_out1->read();
	}

	void pt_add(sc_biguint<160> p1_x, 
					sc_biguint<160> p1_y,
					sc_biguint<160> p2_x, 
					sc_biguint<160> p2_y,  
					//sc_biguint<160> a, 
					//sc_biguint<160> m,  
					sc_biguint<160> &q_x, 
					sc_biguint<160> &q_y)
	{
		sc_uint<2> ecc_status_in=1;

		set_input(_FW_PT_ADD_, p1_x, p1_y, p2_x, p2_y, _ECC_PARAM_A_, _ECC_PARAM_M_, 0, 0, 0, 0);	
		while(ecc_status_in!=_FW_FINISHED_){
			ecc_status_in = ecc_status->read();
			wait(100, "SC_MS");
		}	
		q_x = data_out0->read();
		q_y = data_out1->read();
	}

	

	void exe(){

		// Q_u: (301727299847069982628311142185057499015902752747,1443497128460397109256863009454192008427931943644)
		// d_s: 1939324126
		/*sc_biguint<160> q_u_x = "301727299847069982628311142185057499015902752747";
		sc_biguint<160> q_u_y =  "1443497128460397109256863009454192008427931943644";

		sc_biguint<160> d_s = "1939324126";


		// Q_s: (443866508661939321270084425037935970149463671727,362798604120864343029131623204884326151629204038)
		// d_u: 1939324126
		sc_biguint<160> q_s_x = "443866508661939321270084425037935970149463671727";
		sc_biguint<160> q_s_y =  "362798604120864343029131623204884326151629204038";

		sc_biguint<160> d_u = "2087345753";*/


		// Elliptische Kurvenparameter a
		/*sc_biguint<160> ec_param_a=2;
		
		// GF(m)
		sc_biguint<160> m=7;

		// Generatorpunkt
		sc_biguint<160> g_x=2;
		sc_biguint<160> g_y=4;

		// Ordnung des Generatorpunktes n
		sc_biguint<160> n=5;

		// Public Key
		sc_biguint<160> p_x=3;
		sc_biguint<160> p_y=4;

		// Private Key
		sc_biguint<160> d=3;

		// Digitale Signatur
		sc_biguint<160> r=3;
		sc_biguint<160> s=3;
				
		sc_biguint<160> hash = 790;*/

		sc_biguint<160> d_u;
		sc_biguint<160> q_x_u;
		sc_biguint<160> q_y_u;

		sc_biguint<160> d_s;
		sc_biguint<160> q_x_s;
		sc_biguint<160> q_y_s;

		sc_biguint<160> q_x_uk;
		sc_biguint<160> q_y_uk;

		sc_biguint<160> q_x_sk;
		sc_biguint<160> q_y_sk;

		//sc_time t1(5000, "SC_MS");

		sc_biguint<160> ec_param_a = _ECC_PARAM_A_;
		sc_biguint<160> ec_param_b = _ECC_PARAM_B_;
		sc_biguint<160> ec_param_m = _ECC_PARAM_M_;
		sc_biguint<160> ec_param_gx = _ECC_PARAM_G_X_;
		sc_biguint<160> ec_param_gy = _ECC_PARAM_G_Y_;
		sc_biguint<160> ec_param_n = _ECC_PARAM_N_;

		std::cout << "ec_param_a: " << ec_param_a << std::endl;
		std::cout << "ec_param_b: " << ec_param_b << std::endl;
		std::cout << "ec_param_m: " << ec_param_m << std::endl;
		std::cout << "ec_param_gx: " << ec_param_gx << std::endl;
		std::cout << "ec_param_gy: " << ec_param_gy << std::endl;
		std::cout << "ec_param_n: " << ec_param_n << std::endl;

		sc_biguint<160> p_x = 6;
		sc_biguint<160> p_y = 8;
		sc_biguint<160> m = 11;
		sc_biguint<160> a = 4;
		sc_biguint<160> b = 7;

		//std::cout << "Is on curve: " << isOnCurve(p_x, p_y, m, a, b) << std::endl;

		pt_multiply(ec_param_gx, ec_param_gy, 2, q_x_u, q_y_u);
		std::cout << "Q_u: " << "(" << q_x_u << "," << q_y_u << ")" << std::endl;
		wait(5000, "SC_MS");


		/*key_pair_generate(_ECC_PARAM_A_, _ECC_PARAM_B_, _ECC_PARAM_G_X_, _ECC_PARAM_G_Y_, _ECC_PARAM_N_, _ECC_PARAM_M_, d_u, q_x_u, q_y_u);
		std::cout << "d_u: " << d_u << std::endl;
		std::cout << "Q_u: " << "(" << q_x_u << "," << q_y_u << ")" << std::endl;
		wait(t1);

		key_pair_generate(_ECC_PARAM_A_, _ECC_PARAM_B_, _ECC_PARAM_G_X_, _ECC_PARAM_G_Y_, _ECC_PARAM_N_, _ECC_PARAM_M_, d_s, q_x_s, q_y_s);
		std::cout << "d_s: " << d_s << std::endl;
		std::cout << "Q_s: " << "(" << q_x_s << "," << q_y_s << ")" << std::endl;
		wait(t1);

		pt_multiply(q_x_s, q_y_s, d_u, q_x_sk, q_y_sk);
		std::cout << "Q_k (At User): " << "(" << q_x_sk << "," << q_y_sk << ")" << std::endl;
		wait(t1);

		pt_multiply(q_x_u, q_y_u, d_s, q_x_uk, q_y_uk);
		std::cout << "Q_k (At Server): " << "(" << q_x_uk << "," << q_y_uk << ")" << std::endl;
		wait(t1);

		pt_multiply(_ECC_PARAM_G_X_, _ECC_PARAM_G_Y_,(d_s*d_u) , q_x_uk, q_y_uk);
		std::cout << "Q_k (Direkt): " << "(" << q_x_uk << "," << q_y_uk << ")" << std::endl;
		wait(t1);

		/*

		pt_multiply(q_x_u, q_y_u, 6, q_x_sk, q_y_sk);
		std::cout << "Q_s_k: " << "(" << q_x_sk << "," << q_y_sk << ")" << std::endl;
		wait(t1);

		/*pt_multiply(_ECC_PARAM_G_X_, _ECC_PARAM_G_Y_, d_s, q_x_s, q_y_s);
		std::cout << "Q_s: " << "(" << q_x_s << "," << q_y_s << ")" << std::endl;
		wait(t1);



		pt_multiply(q_x_s, q_y_s, d_u, q_x_uk, q_y_uk);
		std::cout << "Q_u_k: " << "(" << q_x_uk << "," << q_y_uk << ")" << std::endl;*/
		//input_test(_FW_ECDSA_KEY_GENERATION_, _ECC_PARAM_A_, _ECC_PARAM_B_, _ECC_PARAM_G_X_, _ECC_PARAM_G_Y_, _ECC_PARAM_N_, _ECC_PARAM_M_, 0, 0, 0, 0);
		//wait(t1);
		
		
		//print_output();

		sc_stop();
	}

	SC_CTOR(InputFWModule){
		SC_THREAD(exe);
	}

};

