#include "User.h"
#include <iostream>
using namespace std;

User::User(string s1, string s2) {
	
	name = s1;
	if (s1 != "")
		if (s2 == "")
			email = s1 + "@ime.usp.br";
		else
			email = s2;
	key = 0;
	pkg = PKG::getInstance();
}

void User::setName(string s) {
	name = s;
}

void User::setEmail(string s) {
	email = s;
}

string User::getName(void) {
	return name;
}

string User::getEmail(void) {
	return email;
}

vector<uint32> User::encrypt(string msg, uint32 key, bool isRootOfA, uint32 secretKey) {
	
	uint32 t, i;
	uint64 a;
	vector<uint32> s;
	uint8 mask;
	int8 x, j;

	cout << "Encrypting...";
	for (i = 0; i < msg.size(); i++) {
		
		mask = 1;		
		for (j = 0; j < 8; j++) {
			x = (((uint8)(msg.at(i)) & mask) >> j) == 1 ? 1 : -1;
			do {
				do {
					t = (uint32)((double)rand() / RAND_MAX * pkg->getM());
				} while (Util::jacobi(t, pkg->getM()) != x);
			
				a = key;
				a = a * Util::inv(t, pkg->getM()) % pkg->getM();
				if (isRootOfA)
					a = a + t % pkg->getM();
				else
					if (t > (uint32)a)
						a = t - a;
					else
						a = pkg->getM() - a + t;
			} while (Util::jacobi((uint32)(a + 2 * secretKey % pkg->getM()), pkg->getM()) == 0);			
			s.push_back((uint32)a);
			mask <<= 1;
		}		
	}	

	cout << " done." << endl << endl;

	return s;
}

string User::decrypt(vector<uint32> s) {
	
	vector<uint32>::iterator it;
	uint64 x;
	uint8 bitPosition, byte;
	string msg;
	int8 j;
	bitPosition = 0;
	byte = 0;
	msg = "";

	cout << "Decrypting...";
	for (it = s.begin(); it != s.end(); it++) {

		x = *it;
		x += 2 * key;
		x %= pkg->getM();

		j = Util::jacobi((uint32)x, pkg->getM());
		byte |= (((j + 1) / 2) << bitPosition);
		if (++bitPosition == 8) {
			msg += byte;
			bitPosition = 0;
			byte = 0;
		}
	
	}

	cout << " done." << endl << endl;
	s.clear();

	return msg;
}

void User::createKey(void) {

	key = pkg->createKey(email);;	

}

bool User::isRootOfA(void) {

	return Util::powMod(key, 2, pkg->getM()) == Util::hash(email, pkg->getM());

}

uint32 User::getKey(void) {

	return key;
}