/*********************************************************************
*	Encryption
*	HEADER FILE
*	Autor:	Michal Jirouš
*	Datum: 29.9.2008
*	Soubor: encryption.cpp
*	Popis: Modul umoznuje ukladani / nacitani zakodovanych zprav podle
* 			jednoduche transpozicni sifry
**********************************************************************/

#include "encryption.h"
#include <sstream>
#include <iostream>
#include <fstream>
#include <time.h>

#include "sys_console.h"

using namespace systemConsole;
using namespace encryption;
using namespace std;

/* Zasifruje text do souboru */
bool encryption::encrypt( const char *filename, std::string data )
{
	if( data.empty() )
	{
		console << "NOTE: No data for encryption. Skipping. " << endline;
		return false;
	}

	console << "Encryption to file " << filename << "..." << endline;
	ofstream fout(filename, ios::binary | ios::trunc | ios::out );
	if(!fout.is_open())
	{
		console << "Error: Could not open file '" << filename << "' for encryption!" << endline;
		return false;
	}
	
	size_t numBytes = data.length();	//pocet bytu v souboru

	int64_t key;	//klic kodovani
	time_t value;
	time( &value );
	key = value;
	
	unsigned int iShift = static_cast<int>(key % iShMax ); //hodnota 1-7 pro logicky posun
	
	unsigned short int ifMap[MAP_SIZE] = {0};	//mapa pro zprehazeni :)
	
	createMap(ifMap, key);		//vytvorime mapu
		
	unsigned int iPosition = 0;
	
	fout.write((char*)&key,sizeof key);	//klic
	fout.write((char*)&numBytes,sizeof numBytes);	//delka souboru
	
	char writeByte = 0x00;
	char buffer[MAP_SIZE];
	
	int j = 0;
	while(numBytes > 0)
	{
		if( numBytes >= MAP_SIZE )
		{
			memcpy( buffer, data.substr( j, MAP_SIZE ).c_str(), sizeof( buffer ) );
			j += MAP_SIZE;
			//fin.read((char*)&buffer, sizeof buffer);
		}
		else
		{
			memcpy(buffer, data.substr(j).c_str(), static_cast<int>(numBytes));
		}
			//fin.read((char*)&buffer, sizeof(char)*static_cast<int>(numBytes));
		
		for(unsigned int i = 0; i < MAP_SIZE;++i)
		{
			iPosition = ifMap[i];	//zjistime index znaku, ktery ma byt nyni ulozen
			if(iPosition >= numBytes)
			{
				writeByte = static_cast<char>(i);//zbytek doplnime nesmysly
			}
			else
			{
				writeByte = buffer[iPosition];
			}
			writeByte = llsh(writeByte, (iShift*iPosition) % iShMax);	//provedeme levy logicky posun
			writeByte = swap(writeByte);	//swapujeme byty
			fout.write((char*)&writeByte,sizeof writeByte);	//ulozime
		}
		
		if( numBytes >= MAP_SIZE )
			numBytes -= MAP_SIZE;
		else
			break;
	}
	
	fout.close();
	console << "Data was successfuly encrypted to file '" << filename << "'" << endline;
	return true;
}

/* Nacte sifrovany soubor do stringu */
string encryption::decryptFile( const char *filename )
{
	console << "Decrypting file '" << filename << "'..." << endline;
	int64_t key = 0;	//klic
	size_t numBytes = 0;
	
	ifstream fin(filename, ios::in | ios::binary);
	if(!fin.is_open())
	{
		console << "Error: Cannot open file '" << filename << "' for decryption!" << endline;
		return string();
	}

	fin.read((char*)&key, sizeof key);	//nacte se klic
	fin.read((char*)&numBytes, sizeof numBytes);	//pocet znaku
	
	unsigned short int ifMap[MAP_SIZE] = {0};
	createMap(ifMap, key);	//vytvori se mapa

	unsigned int iShift = static_cast<int>(key % iShMax ); //hodnota 1-7 pro logicky posun

	unsigned short int ifInverse_Map[MAP_SIZE] = {0};

	for(unsigned int i = 0; i < MAP_SIZE; ++i)
		ifInverse_Map[ ifMap[i] ] = i;	//vygeneruje se inverzni mapa

	int iPosition = 0;

	ostringstream out;
	
	char byteBuffer[MAP_SIZE];

	while( numBytes > 0 )
	{
		fin.read((char*)&byteBuffer, MAP_SIZE );
		if( fin.eof() )
			break;
		for(unsigned int i = 0; i < MAP_SIZE; ++i)
		{
			iPosition = ifInverse_Map[i];	//zjistujeme, index znaku, ktery ma byt nacten
			if(i < (numBytes))
			{
				byteBuffer[iPosition] = swap(byteBuffer[iPosition]);	//swap bytu
				byteBuffer[iPosition] = lrsh(byteBuffer[iPosition], (iShift*i)%iShMax);	//pravy logicky posun
				
				out << byteBuffer[iPosition];
				
			}
			else
				break;	//konec
		
		}
		numBytes -= min( MAP_SIZE, numBytes);
	}

	fin.close();
	console << "File '" << filename << "' was successfuly decrypted." << endline;
	return out.str();
}

/* Vytvoreni mapy podle klice */
void encryption::createMap(unsigned short int ifMap[], int64_t key)
{
	bool used[MAP_SIZE] = {0};
	
	unsigned short int moveIndex;
	for(unsigned int i = 0; i < MAP_SIZE;i++)
	{
		moveIndex = static_cast<unsigned int>(key % MAP_SIZE);
		
		while(used[moveIndex % MAP_SIZE] == true)
			moveIndex++;

		moveIndex %= MAP_SIZE;
		ifMap[i] = moveIndex;
		key -= moveIndex * ENCRYPTION_MAP_MULTIPLICATOR;
		used[moveIndex] = true;
	}

}

/* Levy logicky posun */
unsigned char encryption::llsh(unsigned char byte, int shift)
{
	unsigned char tmp = byte;
	tmp <<= shift;
	byte >>= sizeof(unsigned char)*8 - shift;
	return byte | tmp;
}

/* Pravy logicky posun */
unsigned char encryption::lrsh(unsigned char byte, int shift)
{
	unsigned char tmp = byte;
	tmp >>= shift;
	byte <<= sizeof(unsigned char)*8 - shift;
	return byte | tmp;
}

/* Swap bytu */
unsigned char encryption::swap(unsigned char byte)
{
	return llsh(byte,sizeof(unsigned char)*4);
}

