#include "sudoku.h"
using namespace std;

Sudoku::Sudoku(int sizeOfSide, const char *symbols) throw (int) {

	this->symbols = test_input(sizeOfSide, symbols);
	this->sizeOfSide = sizeOfSide;
	allocate_playboard_memory(sizeOfSide);
	clear_playboard();
}


Sudoku::Sudoku(const Sudoku& source) {

	sizeOfSide = source.getSizeOfSide();
	symbols = source.getSymbols();
	allocate_playboard_memory(sizeOfSide);
	clear_playboard();
	
	for (int i=0; i<(sizeOfSide); i++)
		for (int j=0; j<(sizeOfSide); j++)
			p_data[i][j] = source.getAt(i,j);
}


Sudoku::~Sudoku() {

	release_playboard_memory(sizeOfSide);
}


const set<char> Sudoku::test_input(int playboard_side, const char* symbols) const {

	set<char> temp_symbols;
	double sqrt_playboard_side, fractpart;

	if (playboard_side<2) throw 1;

	sqrt_playboard_side = sqrt(playboard_side);
	fractpart = modf(sqrt_playboard_side,&sqrt_playboard_side);
	if (fractpart!=0) throw 2;

	for (unsigned int i=0;i<strlen(symbols);i++) {

		temp_symbols.insert(symbols[i]);
		if (symbols[i]==' ') throw 2;
	}
	if (temp_symbols.size()!=((unsigned int) playboard_side)) throw 3;

	return temp_symbols;
}


void Sudoku::clear_playboard() {

	for (int i=0; i<sizeOfSide; i++)
		for (int j=0; j<sizeOfSide; j++)
			p_data[i][j] = ' ';
}


void Sudoku::allocate_playboard_memory(int playboard_side) {

	p_data = new char*[playboard_side];
	for (int i=0; i<playboard_side; i++)
		p_data[i] = new char[playboard_side];
}


void Sudoku::release_playboard_memory(int playboard_side) {

	for (int i=0; i<playboard_side; i++) delete[] p_data[i];
	delete[] p_data;
}


void Sudoku::setType(int sizeOfSide, const char *symbols) throw (int) {

	release_playboard_memory(this->sizeOfSide);
	this->symbols = test_input(sizeOfSide, symbols);
	this->sizeOfSide = sizeOfSide;
	allocate_playboard_memory(sizeOfSide);
	clear_playboard();
}


int Sudoku::getSizeOfSide() const {

	return sizeOfSide;
}


const set<char>& Sudoku::getSymbols() const {

	return symbols;
}


char Sudoku::getAt(int x, int y) const throw (int) {

	if ((x>=sizeOfSide)||(x<0)||(y<0)||(y>=sizeOfSide)) throw 1;
	return p_data[x][y];
}


void Sudoku::setAt(int x, int y, char symbol) throw (int) {

	if ((x>=sizeOfSide)||(x<0)||(y<0)||(y>=sizeOfSide)) throw 1;
	
	set<char>::iterator it;
	it = symbols.find(symbol);
	if ((it==symbols.end())&&(symbol!=' ')) throw 2;
	p_data[x][y] = symbol;
}


bool Sudoku::isFilled() const {

	bool isFilled = true;
	for (int i=0; i<sizeOfSide; i++)
		for (int j=0; j<sizeOfSide; j++) 
			if (p_data[i][j]==' ') isFilled = false;
	return isFilled;
}


bool Sudoku::isSolved() const {

	set<char> temp_set;
	set<char>::iterator it;
	for (int i=0; i<sizeOfSide; i++) {

		temp_set.erase(temp_set.begin(),temp_set.end());
		temp_set.insert(p_data[i][0]);
		for (int j=1; j<sizeOfSide; j++) {
		
			it = temp_set.find(p_data[i][j]);
			if (it!=temp_set.end()) return false;
			temp_set.insert(p_data[i][j]);
		}
	}

	return true;
}


ostream& operator<<(ostream& os, const Sudoku& SudokuToWrite) {

	os << SudokuToWrite.sizeOfSide << ' ';
	set<char>::iterator it;
	for (it=SudokuToWrite.symbols.begin();it!=SudokuToWrite.symbols.end();it++) 
		os << *it;
	os << endl << endl;
	
	for (int i=0; i<SudokuToWrite.sizeOfSide; i++) {

		for (int j=0; j<SudokuToWrite.sizeOfSide; j++)
			os << SudokuToWrite.getAt(i,j);
		os << endl;
	}

	return os;
}


istream& operator>>(istream& is, Sudoku& SudokuToLoadIn) {

	is >> SudokuToLoadIn.sizeOfSide;
	
	// read a space between sizeOfSide and symbols
	is.get();

	char symbol;;

	SudokuToLoadIn.symbols.erase(SudokuToLoadIn.symbols.begin(),SudokuToLoadIn.symbols.end());
	for (int i=0; i<SudokuToLoadIn.sizeOfSide; i++) {

		cout << alpha << endl;
		symbol = is.get();
		SudokuToLoadIn.symbols.insert(symbol);
	}

	// read a new line between symbols and playboard
	is.get();
	is.get();

	int i = 0;

	while((!is.eof())&&(i<SudokuToLoadIn.sizeOfSide)) {
	
		int j = -1;
		symbol = is.get();
		while (symbol!='\n') {

			j++;
			SudokuToLoadIn.p_data[i][j] = symbol;
			symbol = is.get();
		} 
		i++;
	}	
	return is;
}