#ifndef __BOOK_AMV
	#define __BOOK_AMV

/*
	Esta clase sirve para crear un libro a partir de un archivo de texto y explorarlo.
	Los encabezados de las secciones están en el formato w.x.y.z..., donde w,x,y,z, son cadenas de caracteres
	qeu representan las secciones y subsecciones del libro.

	El programa se encarga de organizar la información de acuerdo a los encabezados de secciones.

	Por medio de la estructura de datos book, podremos explorar el libro en todas sus secciones y subsecciones.
	Este programa puede ampliarse para funcionar en modo gráfico y permitir una exploración más agradable del libro.
*/

#include <iostream>
#include <string>
#include <map>
#include <fstream>
#include <strstream>
#include <cstring>
#include <string>
#include <deque>
#include "REEvaluator.h"
#include "amvutils.h"

using namespace std;

namespace amv	{

// Declaración hacia adelante
struct bookNode;

// Clase linkBookNode
// Es necesaria para la inicialización de los apuntadores a los hijos de los objetos bookNode
struct linkBookNode	{
bookNode *pbn;
linkBookNode(): pbn(0)
	{
	}
};

// Clase bookNode
// Representa una sección del libro y las subsecciones se encuentran en el mapa childs, las cuales a su vez
// pueden tener subsecciones
struct bookNode	{
string title;
deque<string> content;
map<string,linkBookNode> childs;

	void showContent(ostream &os) const
		{
		int i;

		for(i=0; i<content.size(); ++i)
			{
			if(content[i][content[i].size()-1]=='\r')
				os<<content[i]<<endl;
			else
				os<<content[i]<<'\r'<<endl;
			}
		os<<'\r'<<endl;
		map<string,linkBookNode>::const_iterator it1=childs.begin();
		for(; it1!=childs.end(); ++it1)
			{
			os<<it1->first<<'\t';
			os<<it1->second.pbn->title<<'\r'<<endl;
			}
		}

	//It debe ser un iterador a datos de tipo string, la secuencia [first,last) denota las subsecciones a encontrar
	template <class It>	
	bookNode *find(It first, It last)
		{
		linkBookNode *p=0;

		if(childs.find(*first)==childs.end())
			return 0;

		for(p=&(childs[*first]); ++first!=last;)
			{
			if(p->pbn->childs.find(*first)==p->pbn->childs.end())
				{
				return 0;
				}
			p=&(p->pbn->childs[*first]);
			}

		return p->pbn;
		}

	// Agrega una subsección, definida por la secuencia [first,last)
	template <class It>	
	bookNode *add(It first, It last)
		{
		linkBookNode *p=0;

		for(p=&(childs[*first]); ++first!=last;)
			{
			p=&(p->pbn->childs[*first]);
			}

		if(!p->pbn)
			p->pbn=new bookNode;

		return p->pbn;
		}
};


// Clase book
// Guarda jerárquicamente la información de un libro y permite acceder a ella
class book	{

bookNode *root ,*currentNode;
deque<string> position;
amv::REEvaluator REEsection;
char delimiter;

	//Obtiene el contenido de una sección, si devuelve true significa que la última
	//línea es el encabezado de otra sección
	bool getContent(istream &is, deque<string> &content)
		{
		char line[4096];
		string s;

		while(is.getline(line,4096))
			{
			istrstream istr(line,strlen(line));
			content.push_back(line);
			if(REEsection.getLexema(istr,s))
				return true;
			}

		return false;
		}

	template <class It>
	void getTitle(It first, It last, string &title)
		{
		for( ; first!=last; ++first)
			{
			if(!first->empty())
				break;
			}

		if(first!=last)
			title=*first;
		else
			title="Sín título";
		}
public:
	book(const char *filename, const char *REsection="[0-9]+<(.<[0-9]+)*", char delim='.'): root(0), REEsection(REsection), delimiter(delim)
		{
		ifstream is(filename);

		if(is)
			{
			currentNode=root=new bookNode;
			bookNode *pbn;
			bool pending=false;
			char line[4096], line1[4096];
			string s,s1;
			deque<string> v;
			root->title=filename;
			root->content.push_back(root->title);

			while(true)
				{
				if(pending)
					{
					v=amv::split<deque<string>,string>(s1,delimiter);
					pbn=root->add(v.begin(),v.end());
					pending=getContent(is,pbn->content);
					
					if(pending)
						{
						s1=pbn->content.back();
						pbn->content.pop_back();
						getTitle(pbn->content.begin(),pbn->content.end(),pbn->title);
						}
					else
						{
						getTitle(pbn->content.begin(),pbn->content.end(),pbn->title);
						break;
						}
					}
				else
					{
					is.getline(line,4096);
					istrstream istr(line,strlen(line));
					if(REEsection.getLexema(istr,s))
						{
						v=amv::split<deque<string>,string>(s,delimiter);
						pbn=root->add(v.begin(),v.end());
						pending=getContent(is,pbn->content);

						if(pending)
							{
							s1=pbn->content.back();
							pbn->content.pop_back();
							getTitle(pbn->content.begin(),pbn->content.end(),pbn->title);
							}
						else
							{
							getTitle(pbn->content.begin(),pbn->content.end(),pbn->title);
							break;
							}
						}
					}
				}
			}
		else
			cerr<<"No se encontró el archivo con los datos del libro\n";
		}

	//Permite explorar el libro propocionando la ruta de la sección (x.y.z), CTRL+Z sirve para salir
	void show1(ostream &os)
		{
		string s;
		bookNode *pbn;

		cerr<<"Sección: ";
		while(cin>>s)
			{
			if(REEsection.eval(s.begin(),s.end()))
				{
				deque<string> v=amv::split<deque<string>,string>(s,delimiter);

				if(pbn=root->find(v.begin(),v.end()))
					pbn->showContent(os);
				else
					cerr<<"El número de sección que tecleó no existe\n";
				}
			else
				cerr<<"Use el formato x"<<delimiter<<"y"<<delimiter<<"z para las secciones\n";
			cerr<<"\nSección: ";
			}
		}

	//Permite explorar el libro proporcionando el número de subsección relativo a la sección en la que te encuentras
	//0 va a la sección padre y permite salir del programa cuando estás en la sección principal
	//? muestra la sección en la que estás actualmente
	void show(ostream &os)
		{
		string s,prompt=": ";
		bookNode *pbn=root, *pbn1;
		deque<string> position;

		pbn->showContent(os);
		cerr<<endl<<prompt;
		while(cin>>s)
			{
			if(s=="0")
				{
				if(position.empty())
					{
					cerr<<"\nHasta la vista\n";
					return;
					}
				else
					position.pop_back();
				}
			else if(s=="?")
				{
				pbn->showContent(os);
				cerr<<endl<<prompt;
				continue;
				}
			else
				position.push_back(s);

			if(position.size())
				{
				if(pbn1=root->find(position.begin(),position.end()))
					{
					pbn=pbn1;
					pbn->showContent(os);
					}
				else
					{
					cerr<<"El número de sección que tecleó no existe\n";
					position.pop_back();
					}
				}
			else
				{
				pbn=root;
				pbn->showContent(os);
				}

			prompt=position.size()? join(position,'.')+": ": ": ";
			cerr<<endl<<prompt;
			}
		}

	bool go(const string &s, ostream &os)
		{
		string prompt=": ";
		bookNode *pbn1;

			if(s=="0")
				{
				if(position.empty())
					return false;
				else
					position.pop_back();
				}
			else if(s=="?")
				{
				currentNode->showContent(os);
				//cerr<<endl<<prompt;
				return true;
				}
			else
				position.push_back(s);

			if(position.size())
				{
				if(pbn1=root->find(position.begin(),position.end()))
					{
					currentNode=pbn1;
					currentNode->showContent(os);
					}
				else
					{
					//cerr<<"El número de sección que tecleó no existe\n";
					position.pop_back();
					return false;
					}
				}
			else
				{
				currentNode=root;
				currentNode->showContent(os);
				}

			prompt=position.size()? join(position,'.')+": ": ": ";
			//cerr<<endl<<prompt;
		return true;
		}

	operator bool() const
		{
		return root;
		}
};

}

#endif