/* last modified 02/23/99 */
/*
****
*                                                                 
*  Book Builder, copyrighted 1999 by Marc-Philippe HUGET          
                                                                 
   Don't forget this program is a cardware, so please, send me a postcard,
   I am very glad receiving postcard coming from all over the world

   Postal Address :  M. Marc-Philippe Huget
                     8, Avenue Léon Blum
					 38100 GRENOBLE
					 FRANCE

   Thanks in advance for your postcard !

   You can modify this code or implement it in your program. If you modify 
   the code, just send me new version or link to your page. If you implement *
   this code in your program, just send me an e-mail. I am very pleased to   *
   know my program is useful                                              ****
*/

#include <stdio.h>
//include MFC header
#include "stdafx.h"

//this class offers information for one line. In fact, this program implement
//a flat version of a tree
//if you have
//          c
//      b
//          d
//  a
//
//      e
//b and e are sons of a, etc.
//here, you have one line for a (CString move) and index for line where you can
//find b and e
//let us suppose, you have this store
// 0. a 
// 1. b
// 2. c
// 3. d
// 4. e
// next_moves for a are 1 and 4
class Move : public CObject {
public:
	CString move;
	CWordArray next_moves;
	
	//constructor, simply sets the move string
	Move(CString one_move) {
		move = one_move;

	} //constructor

	//constructor sets the move string and positions
	//this case is when you load a tree
	Move(CString one_move, CString position) {
		move = one_move;

		//delete blanks before and after chars
		position.TrimLeft();
		position.TrimRight();


		while (position.GetLength() != 0) {
			//we search blank char in order to cut position
			int pos = position.Find(' ');

			//we find one blank
			if (pos != -1) {
				CString one_position = position.Left(pos);
				one_position.TrimLeft();
				one_position.TrimRight();

				//we add this position for this node
				next_moves.Add((WORD)atoi(one_position));
				
				//we delete this part and begin again with the position
				position = position.Right(position.GetLength()-pos-1);
				position.TrimLeft();
				position.TrimRight();
			
			} //if

			else {
				//we don't find another find, so you are on the last position
				next_moves.Add((WORD)atoi(position));
				position = "";

			} //else

		} //while

	} //constructor

}; //class Move


//define some functions
void recursion(int indice);
void build_book();
void build_tree();
void load_tree();

//this variable contains the flat-tree
CObArray Tree;

//this variable contains opening book in Spracklen's format
CString List;

//streams from where and to where we work
char FILE_IN[256];
char FILE_OUT[256];
char FILE_TREE_IN[256];
char FILE_TREE_OUT[256];

//depth for the PGN file
int DEPTH;

//flag in order to see if you load a tree
int LOAD_TREE;

void main(int argc, char **argv)
{
	char cmd[256];
	DEPTH = 60;
	LOAD_TREE = 0;

	//some information
	fprintf(stdout, "BookBuilder 1.0\n");
	fprintf(stdout, "This program is written by Marc-Philippe Huget\n");
	fprintf(stdout, "Email : Marc-Philippe.Huget@imag.fr\n\n");
	fprintf(stdout, "if you have comments or if you find bugs, send me an email\n\n");
	fprintf(stdout, "If you want help, just type help on command-line\n");

	//we parse command-line
	switch(argc) {
	case 2 : 
		strcpy(FILE_IN, argv[1]);
		break;
	case 3:
		strcpy(FILE_IN, argv[1]);
		strcpy(FILE_OUT, argv[2]);
		break;
	case 4:
		strcpy(FILE_IN, argv[1]);
		strcpy(FILE_OUT, argv[2]);
		strcpy(FILE_TREE_IN, argv[3]);
		break;
	case 5:
		strcpy(FILE_IN, argv[1]);
		strcpy(FILE_OUT, argv[2]);
		strcpy(FILE_TREE_IN, argv[3]);
		strcpy(FILE_TREE_OUT, argv[4]);
		break;

	} //switch
	
	//we display informations about files we are using
	if (argc > 0) {
		fprintf(stdout, "file_in : %s\n", FILE_IN);
		fprintf(stdout, "file_out : %s\n", FILE_OUT);
		fprintf(stdout, "file_tree_in : %s\n", FILE_TREE_IN);
		fprintf(stdout, "file_tree_out : %s\n", FILE_TREE_OUT);

	} //if


	while(strcmpi(cmd, "quit") != 0) {
		//I wait a command
		fprintf(stdout, "Command ? ");
		scanf("%s", cmd);

		///user wants to enter the file where we can find PGN games
		if (strcmpi(cmd, "file_in") == 0) {
			fprintf(stdout, "Name of the PGN file : ");
			scanf("%s", FILE_IN);
			fprintf(stdout, "\n");

		} //if

		//user specifies the file where we store the opening book
		else if (strcmpi(cmd, "file_out") == 0) {
			fprintf(stdout, "Name of the book file : ");
			scanf("%s", FILE_OUT);
			fprintf(stdout, "\n");

		} //else

		//user specifies the file where we can find a tree
		else if (strcmpi(cmd, "file_tree_in") == 0) {
			fprintf(stdout, "Name of the saved tree file in order to load it : ");
			scanf("%s", FILE_TREE_IN);
			fprintf(stdout, "\n");

		} //else

		//user specifies the file where we store the tree
		else if (strcmpi(cmd, "file_tree_out") == 0) {
			fprintf(stdout, "Name of the book file in order to save it : ");
			scanf("%s", FILE_TREE_OUT);
			fprintf(stdout, "\n");

		} //else

		//user wants to load a tree, we test if he/she enters a filename before
		else if (strcmpi(cmd, "load_tree") == 0) {
			if (strlen(FILE_TREE_IN) == 0)
				fprintf(stdout, "you have to name this file : file_tree_in\n");

			else {
				load_tree();
				LOAD_TREE = 1;

			} //else

		} //else 

		//user wants to build a book and only a book, we test if we have a file in order to store the book
		else if (strcmpi(cmd, "build_book") == 0) {
			if (strlen(FILE_OUT) == 0) {
				fprintf(stdout, "you have to name this file : file_out \n");
				fprintf(stdout, "file_out : %s\n", FILE_OUT);

			} //if

			else {
				build_book();

			} //else

		} //else

		//some information
		else if (strcmpi(cmd, "help") == 0) {
			fprintf(stdout, "\n");
			fprintf(stdout, "BookBuilder 1.0\n");
			fprintf(stdout, "This program is written by Marc-Philippe Huget\n");
			fprintf(stdout, "Email : Marc-Philippe.Huget@imag.fr\n\n");
			fprintf(stdout, "if you have comments or if you find bugs, send me an email\n\n");
			fprintf(stdout, "Available Commands :\n");
			fprintf(stdout, "file_in           : name of the PGN file, you want to use in order to create a \n");
			fprintf(stdout, "                    tree and a book\n");
			fprintf(stdout, "file_out          : name of the file where I store the book\n");
			fprintf(stdout, "file_tree_in      : name of the tree file if you want to load a tree\n");
			fprintf(stdout, "file_tree_out     : name of the tree file where I store the tree\n");
			fprintf(stdout, "load_tree         : loads a tree\n");
			fprintf(stdout, "build_book        : builds a book from a tree\n");
			fprintf(stdout, "build_tree        : builds a tree and only the tree \n");
			fprintf(stdout, "help              : displays this screen\n");
			fprintf(stdout, "quit              : quits this program\n\n");
			fprintf(stdout, "You can also use command-line arguments \n");
			fprintf(stdout, "1st argument : file_in\n");
			fprintf(stdout, "2nd argument : file_out\n");
			fprintf(stdout, "3rd argument : file_tree_in\n");
			fprintf(stdout, "4th argument : file_tree_out\n");

		} //else

		//user wants to quit this program
		else if (strcmpi(cmd, "quit") == 0) {
			//no operation for the moment

		} //else

		//user wants to define depth 
		else if (strcmpi(cmd, "depth") == 0) {
			fprintf(stdout, "Depth of moves (in half moves) : ");
			scanf("%d", &DEPTH);
			fprintf(stdout, "\n");

		} //else

		//user wants to build a tree, we test if we have a file where we load PGN and a file where we store
		//the tree
		else if (strcmpi(cmd, "build_tree") == 0) {
			if (strlen(FILE_IN) == 0 ||
				strlen(FILE_TREE_OUT) == 0) {
				fprintf(stdout, "you have to name this file : file_in file_tree_out \n");

			} //if
			else { 
				build_tree();

			} //else

		} //else

		//this command is not a command, just tell it to the user
		else {
			fprintf(stdout, "error <%s>\n", cmd);

		} //else

	} //while

} //main


//this function provides a depth-first search in a tree, we build opening book
void recursion(int index)
{
	Move *one_move = (Move*)Tree[index];

	//have we sons ? yes
	if (one_move->next_moves.GetSize() != 0) {
		List = List + '(';

		List = List + one_move->move;

		//we go in all nodes
		for (int i = 0; i < one_move->next_moves.GetSize(); i++) {
			recursion(one_move->next_moves[i]);
			List = List + ')';
		
		} ///for

	} //if

	//no, it is a leaf
	else {
		List = List + '(';
		List = List + one_move->move;

	} //else

} //recursion


//this function simply constructs opening book. This construction is a depth-first search algorithm
void build_book()
{
	CStdioFile file_out;
	
	try
	{
		fprintf(stdout, "Now, I build the book...\n");

		List = "";
		recursion(0);
		List = List.Right(List.GetLength()-3);

		fprintf(stdout, "Finally, I store the book\n");

		file_out.Open(FILE_OUT, CFile::modeCreate | CFile::modeWrite);
		file_out.WriteString(List);

		file_out.Close();

	} //try

	catch(CFileException *e)
	{
		if (e->m_cause == CFileException::fileNotFound) {
			fprintf(stdout, "ERROR : file not found !\n");
			exit(-1);

		} //if
		if (e->m_cause == CFileException::accessDenied) {
			fprintf(stdout, "ERROR : access denied !\n");
			exit(-1);

		} //if

	} //catch

} //build_book


//this function loads a tree stored on disk
//each line has this format : one move in SAN a list of index in the tree separated by blanks
void load_tree()
{
	CStdioFile file_tree_in;
	CString LigneLue;

	try {
		file_tree_in.Open(FILE_TREE_IN, CFile::modeRead | CFile::typeText);

		BOOL retour = file_tree_in.ReadString(LigneLue);

		//we read all the line in the file and we perform some decompositions on this line
		while (retour != NULL) {

			//we delete blanks before and after the line in order to avoid bad parsing
			LigneLue.TrimLeft();
			LigneLue.TrimRight();

			//we find a blank, so we can cut the string in two parts, the first part is the move
			//and the second part is index
			int pos = LigneLue.Find(' ');

			if (pos != -1) {
				CString coup = LigneLue.Left(pos);
				CString position = LigneLue.Right(LigneLue.GetLength()-pos-1);

				//we add this new node
				Tree.Add(new Move(coup, position));

			} //if

			//we don't find blank, we just add the node
			else
				Tree.Add(new Move(LigneLue));

			retour = file_tree_in.ReadString(LigneLue);

		} //while

	} //try

	catch(CFileException *e) {
		if (e->m_cause == CFileException::fileNotFound)
			fprintf(stdout, "ERROR : file not found !");
		
		if (e->m_cause == CFileException::accessDenied)
			fprintf(stdout, "ERROR : access denied !");

	} //catch

	fprintf(stdout, "Number of moves : %d\n", Tree.GetUpperBound());

} //load_tree


//this function deals with the construction of the tree
void build_tree() 
{
	WORD index = 0;
	int num_moves = 0;

	CStdioFile file_in;
	CStdioFile file_tree_out;
	CStdioFile file_out;


	CString LigneLue;
	CString coup;

	//we add the root
	Tree.Add(new Move("00"));

	try 
	{
		file_in.Open(FILE_IN, CFile::modeRead | CFile::typeText);

		fprintf(stdout, "\nPlease wait, I build the tree...\n");

		BOOL retour = file_in.ReadString(LigneLue);

		//we read each line in the PGN file
		while (retour != NULL) {

			//we delete the blanks
			LigneLue.TrimLeft();
			LigneLue.TrimRight();

			//we try to split parts in the line 
			while (LigneLue.GetLength() != 0) {
				CString lettre = LigneLue[0];

				//we have an information line so we drop it and reset variables
				if (lettre == '[') {
					index = 0;
					num_moves = 0;
					break;

				} //if

				//we have not a useless character
				else if (lettre != '1' &&
					     lettre != '2' &&
						 lettre != '3' &&
						 lettre != '4' &&
						 lettre != '5' &&
						 lettre != '6' &&
						 lettre != '7' &&
						 lettre != '8' &&
						 lettre != '9' &&
						 lettre != '0' &&
						 lettre != '.' &&
						 lettre != ' ' &&
						 lettre != '+' &&
						 lettre != '#' &&
						 lettre != '-' && 
						 lettre != '/') {
					
					//we search the next blank
					LigneLue.TrimLeft();
					LigneLue.TrimRight();
					int pos = LigneLue.Find(' ');

					//we have a blank so we cut the string
					if (pos != -1) {
						coup = LigneLue.Left(pos);
						LigneLue = LigneLue.Right(LigneLue.GetLength() - pos - 1);

					} //if

					//no so we take the entire string
					else {
						coup = LigneLue;
						LigneLue = "";

					} //else

					//we increment the number of moves
					num_moves++;

					//if we have check information, we delete it
					if (coup[coup.GetLength()-1] == '+' ||
						coup[coup.GetLength()-1] == '#')
						coup = coup.Left(coup.GetLength()-1);

					//have we sufficient moves ? Yes, we don't store the next moves, no, we store the move
					if (num_moves <= DEPTH) {

						//we search if we have yet this move
						Move *one_move;
						one_move = (Move*)Tree[index];

						int find = 0;

						//we are in a line in a tree, we have some nodes, we search if the move is not
						//a node
						for (int i = 0; i < one_move->next_moves.GetSize(); i++) {
							WORD position = one_move->next_moves[i];

							Move *temp = (Move*)Tree[position];

							coup.TrimLeft();
							coup.TrimRight();

							if (temp->move == coup) {
								index= position;
								find = 1;
								break;

							} //if

						} //for

						//we don't find the move, so we add the move at the end of the tree
						if (!find) {
							Tree.Add(new Move(coup));

							one_move->next_moves.Add((WORD)Tree.GetUpperBound());

							//we display number of lines in the tree
							if ((Tree.GetUpperBound() % 1000) == 0)
								fprintf(stdout, "Number of stored moves : %d\n", Tree.GetUpperBound());

							index = Tree.GetUpperBound();

						} //if

					} //if

				} //if
				
				else
					LigneLue = LigneLue.Right(LigneLue.GetLength()-1);

			} //while	

			retour = file_in.ReadString(LigneLue);

		} //while

		fprintf(stdout, "Tree is built. I store it on disk...\n");
		fprintf(stdout, "I have %d moves\n", Tree.GetUpperBound());

		//we store the book
		file_tree_out.Open(FILE_TREE_OUT, CFile::modeCreate | CFile::modeWrite);

		for (int i = 0; i <= Tree.GetUpperBound(); i++) {
			Move *one_move = (Move*)Tree[i];

			CString MoveList = "";
			char buffer[10];

			//we build the list of moves
			for (int j = 0; j < one_move->next_moves.GetSize(); j++) {
				sprintf(buffer, "%d", one_move->next_moves[j]);
	
				MoveList = MoveList + " " + buffer;

			} //for

			CString ToWrite = one_move->move + MoveList;

			if (ToWrite.GetLength() != 0)
				file_tree_out.WriteString(ToWrite+ "\n");

		} //for

		file_in.Close();
		file_tree_out.Close();

	} //tru
	catch(CFileException *e) {
		if (e->m_cause == CFileException::fileNotFound)
			fprintf(stdout, "ERROR : file not found !");

		if (e->m_cause == CFileException::accessDenied)
			fprintf(stdout, "ERROR : access denied !");

	} //catch

} //build_tree