#include<iostream>
#include<string>
#include <cstdlib>
#include<cctype>
#include<stdexcept>
#include<algorithm>
#include<vector>
#include<map>
#include "code.h"
using namespace std;
typedef string::size_type s_size;	
		
int nrand(int n)
{
	if(n<=0 ||n>RAND_MAX)
		throw domain_error("Argument to nrand is out of range");
	const int bucket_size=RAND_MAX/n;
	int r; 
	do r = rand()/bucket_size;
	while (r>=n);
	return r;
}
string alphabet()
{
	string ret;
	for(int i=0; i<26; i++)
		ret+=('A'+i);
	return ret;
}


string shuffle(const string& s)
{
	string ret=s;
	for(s_size i=1; i<s.size(); i++)
	{
		int replacement=nrand(i+1);
		swap(ret[i],ret[replacement]);
	}
	return ret;
}
bool process_pair(string& s1, string& s2)// Checks that non-alpha's match.
					  // Makes alpha's uppercase.
{
	if(s1.size()!=s2.size())
		return false;
	for(s_size i=0; i!=s1.size();i++)
	{
		if(isalpha(s1[i]) && isalpha(s2[i]))
		{
			s1[i]=toupper(s1[i]);
			s2[i]=toupper(s2[i]);
		}
		else if(s1[i]!=s2[i])
			return false;
	}
}
			
	
		


				
	bool Code::add_pair(char base, char codeIt)
	{
		
	
		if (code_key[base]!='\0' && code_key[base]!=codeIt)
		{
			cerr<<"The code proposed isn't 1-1.";
			return false;
		}
		if (reverse_key[codeIt]!='\0' && reverse_key[codeIt]!=base)
		{
			cerr<<"The code proposed isn't 1-1.";
			return false;
		}
			code_key[base]=codeIt;
			reverse_key[codeIt]=base;
			
	}
		



bool Code::load(string string1, string string2)//Load a code using string2 as the encoding of string1.
	{
		if(!process_pair(string1, string2))
		{
			cout<<"The strings are not related by a code.\n";
		       	return false;
		}
		Code temp;
		temp.code_key=code_key;
		temp.reverse_key=reverse_key;
		for(s_size i =0;i!=string1.size(); i++)
		{
				if(!temp.add_pair(string1[i], string2[i]))
				{
					cerr<<"\nFailure at "<<i<<"th position.\n";
					return false;
				}
		}
		reverse_key=temp.reverse_key;
		code_key=temp.code_key;
		return true;
	}
	
void Code::show() const// display the code key.
	{
		for(map<char,char>::const_iterator i=code_key.begin(); i!=code_key.end(); i++)
			cout<<i->first<<":"<<i->second<<" ";
	}

void Code::unload(string s)
	{
		char base, codeIt;
		for(s_size i=0; i!=s.size(); i++)
			if(isalpha(s[i]))
			{
				base=toupper(s[i]);
				codeIt=toupper(code_key[base]);
				code_key[base]='\0';
				reverse_key[codeIt]='\0';
			}
	}
					
void Code::clear()
	{
		code_key.clear();
		reverse_key.clear();
	}
bool Code::change(char base,char codeIt)
	{
		

		if(isalpha(base) && isalpha(codeIt))
		{
			base=toupper(base);
			codeIt=toupper(codeIt);
			if(reverse_key[codeIt]=='\0')
				{
					char temp=code_key[base];
					reverse_key[temp]='\0';
					code_key[base]=codeIt;
					reverse_key[codeIt]=base;
					return true;
				}
			else
				cout<<endl<<codeIt<<" is already coding "<<reverse_key[codeIt]<<".\n";
		}
		else
			cout<<base<<" and "<<codeIt<<" aren't both letters.\n";
		return false;
	}
void Code::random()
{
	clear();
	string s1=alphabet();
	string s2=shuffle(s1);
	load(s1,s2);
	return;
}
string Code::apply(const string& plain)// Encode input according to code_key, with '-' for uncoded letters.
{
	string ret=plain;
	for(s_size i=0; i!=plain.size(); i++)
		if(isalpha(plain[i]))
			if(code_key[toupper(plain[i])]!='\0')
				ret[i]=code_key[toupper(plain[i])];
			else
				ret[i]='-';
	
	
	return ret;
}
string Code::apply_reverse(const string& coded)
{
	string ret=coded;
	for(s_size i=0; i!=coded.size(); i++)
		if(isalpha(coded[i]))
			if(reverse_key[toupper(coded[i])]!='\0')
				ret[i]=reverse_key[toupper(coded[i])];
			else
				ret[i]='_';
	
	
	return ret;
}

int Code::code_size()
{
	int counter=0;
	for(map<char,char>::const_iterator i=code_key.begin(); i!=code_key.end(); i++)
	{
		if(i->second!='\0')
			counter++;
	}
	return counter;
}

void Code::invert()
{
	swap(code_key, reverse_key);
}

bool Code::encryptable(const string& s)
{
	for(s_size i=0; i!=s.size(); i++)
		if(isalpha(s[i]) && code_key[toupper(s[i])]=='\0')
			return false;
	return true;
}
	
	

			
			
				
				

				
				
		
