/****************************************************************************************
*		Base 64 encoder/decoder written by Thomas Madsen 2012. 
*		The source code and programs are provided 'as-is', without any express or implied warranty. 
*		In no event will the author be held liable for any damages arising from the use of this software. 
*
*****************************************************************************************/



#include <iostream>
#include <fstream>
#include <string>
#include <math.h>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

string chartobinary8(char str)
{
	unsigned int decimal = (int)str;
	unsigned int a[8];
	stringstream ss;
	for (int i = 0; i <= 7; i++)
		{
			a[i] = decimal % 2; decimal = (decimal - a[i])/2;
		}
	for (int i =0; i <= 7; i++)
		{
			ss << a[7-i];
		}
	return ss.str();	
}

char binarytochar8(string str)
{
	string a[8];
	unsigned int b[8];
	int result = 0;
	for (int i = 0; i <= 7; i++)
		{
			a[i] = str.at(i);
			istringstream (a[i]) >> b[i];
			result = result + b[i]*pow(2,7-i); 
		}

	return (char)result;
}

string binarytochar6(string str)
{
	string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	string result;
	string a[6];
	unsigned int b[6];
	int r = 0;
	for (int i = 0; i <= 5; i++)
		{
			a[i] = str.at(i);
			istringstream (a[i]) >> b[i];
			r = r + b[i]*pow(2,5-i); 
		}
	result = alphabet.substr( r, 1);
	return result;
}

string chartobinary6(string str)
{
	string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	size_t found = alphabet.find(str);
	if (found != string::npos){
		int decimal = int(found);
		unsigned int a[6];
		stringstream ss;
		for (int i = 0; i <= 5; i++)
			{
				a[i] = decimal % 2; decimal = (decimal - a[i])/2;
			}
		for (int i =0; i <= 5; i++)
			{
				ss << a[5-i];
			}
	return ss.str();
}
else{ return "000000";}	
}



int main(int argc, char* argv[])
{
string FileNameIn = "";			// Name of the inputfile
string FileNameOut = "";		// Name of the output file
int filesize = 0;				// For the filesiez
int corfilesize = 0;			// For corrected filesize
unsigned char r[4];				// For reading. Uses r[3] for encoding. Uses r[4] for decoding. 
unsigned char w;				// For writing
int leftover;					// For remainder of filesize divided by 3
string temp8;					// For the routine 
string temp6;					// For the routine
string option;					// For the encryp/decrypt option

if (argc==4)					// Check that we have three arguments
{
	string option = argv[1];	// Get option '-e' for encryption and '-d' for decryption
	FileNameIn = argv[2];		// Get inputfile name
	FileNameOut = argv[3];		// Get outputfile name
	ifstream myFileIn (FileNameIn, ios::out | ios::binary);
	ofstream myFileOut (FileNameOut, ios::in | ios::binary | ios::trunc);
if (option == "-e")
	{	
		if(myFileIn.is_open())	// Check that file exists / is open
		{
			myFileIn.seekg(0, ios::end ); 			// Move to end of file
			filesize = myFileIn.tellg(); 			// Compute file size
			/**** Compute end *******/	
			leftover = filesize % 3;
			corfilesize = filesize - leftover;		// This is divisible by 3
			
			/************************/
			cout << "Working ... this might take some time depending on the size of the file." << endl;
			for (int i =0; i <= corfilesize/3 - 1; i++) 	// Converts file
				{
				myFileIn.seekg(3*i, ios::beg);
				myFileIn >> noskipws >> r[0];			// Reads from file
				myFileIn >> noskipws >> r[1];
				myFileIn >> noskipws >> r[2];
				
				/********************************************/
					temp8 = chartobinary8(r[0]) + chartobinary8(r[1]) + chartobinary8(r[2]);
					for (int j = 0; j <= 3; j++)
					{
						temp6 = temp8.substr(6*j,6);
						w = binarytochar6(temp6)[0];	
					
						myFileOut << w;					// Writes to file
					}
				/********************************************/
				}
				/********************************************/
			if (leftover == 2)
				{
				myFileIn.seekg(corfilesize, ios::beg);	// Go to the laft part of the end
				myFileIn >> noskipws >> r[0];			// Reads from file
				myFileIn >> noskipws >> r[1];
				r[2] = 0;
				temp8 = chartobinary8(r[0]) + chartobinary8(r[1]) + chartobinary8(r[2]);
				for (int j = 0; j <= 2; j++)
					{
						temp6 = temp8.substr(6*j,6);
						w = binarytochar6(temp6)[0];	
					
						myFileOut << w;					// Writes to file
					}	
				myFileOut << '=';
				
				}
			if (leftover == 1)
				{
				myFileIn.seekg(corfilesize, ios::beg);	// Go to the last part of the file
				myFileIn >> noskipws >> r[0];			// Reads from file
				r[1] = 0; 
				r[2] = 0;
				temp8 = chartobinary8(r[0]) + chartobinary8(r[1]) + chartobinary8(r[2]);
				for (int j = 0; j <= 1; j++)
					{
						temp6 = temp8.substr(6*j,6);
						w = binarytochar6(temp6)[0];	
					
						myFileOut << w;					// Writes to file
					}	
				myFileOut << '=';
				myFileOut << '=';
				
				}
				/********************************************/
				
			myFileIn.close();						// Closes input file
			myFileOut.close();						// Closes output file
		cout << "Succces!" << endl;
		return 0;

		} 						// End if open
		else {cout << "Error 1: Input file does not exist?"; return 1;}	
	} 							// End if option == "-e";
	else if (option == "-d"){
		if(myFileIn.is_open())	// Check that file exists / is open
		{
			myFileIn.seekg(0, ios::end ); 			// Move to end of file
			filesize = myFileIn.tellg(); 			// Compute file size
			corfilesize = filesize - 4;
			myFileIn.seekg(0, ios::beg);	// Go to the beginning of the file
			cout << "Working ... this might take some time depending on the size of the file." << endl;
			/*********************************************/
			for (int i = 0; i <= corfilesize/4 -1; i++)
			{
				myFileIn >> noskipws >> r[0];			// Reads from file
				myFileIn >> noskipws >> r[1];
				myFileIn >> noskipws >> r[2];
				myFileIn >> noskipws >> r[3];
				string rs[4];
				rs[0] = r[0];							// Convert to string
				rs[1] = r[1];
				rs[2] = r[2];
				rs[3] = r[3];
				temp6 = chartobinary6(rs[0]) + chartobinary6(rs[1]) + chartobinary6(rs[2])+ chartobinary6(rs[3]);
				for (int j = 0; j <= 2; j ++)
				{
					temp8 = temp6.substr(8*j, 8);
					w = binarytochar8(temp8);
					myFileOut << w;
								
				}
			}
			/**********The End Part **********************/
			myFileIn.seekg(corfilesize, ios::beg);
			myFileIn >> noskipws >> r[0];				// Reads from file
				myFileIn >> noskipws >> r[1];
				myFileIn >> noskipws >> r[2];
				myFileIn >> noskipws >> r[3];
				string rs[4];
				rs[0] = r[0];							// Convert to string
				rs[1] = r[1];
				rs[2] = r[2];
				rs[3] = r[3];
				temp6 = chartobinary6(rs[0]) + chartobinary6(rs[1]) + chartobinary6(rs[2])+ chartobinary6(rs[3]);
				int max = 0;
				if (rs[2] == "="){max = 1;}
				if (rs[2] != "="){max = 2;}
				
				for (int j = 0; j <= max-1; j ++)
				{
					temp8 = temp6.substr(8*j, 8);
					w = binarytochar8(temp8);
					myFileOut << w;
								
				}
		
		
			/*********************************************/
		myFileIn.close();						// Closes input file
		myFileOut.close();						// Closes output file
		cout << "Succces!" << endl;
		}							// End if open
		else {cout << "Error 2: Input file does not exist?"; return 1;}	
	return 0;
	}								// End if option == "-d"
}								// End if argv == 4
else{							// Else for if argv == 4
		cout << "---------------------------------------------" << endl;
		cout << "Syntax: base64 [option] filein finleout" << endl;
		cout << "[option] is -e for encoding, -d for decoding." << endl;;
		cout << "---------------------------------------------";
		return 0;
	}

	
return 0;
}								// End int main