#include "ObjProGen/cpyrght_exe.h"
#include "ObjProGen/dirfil.h"
#include <fstream.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "texutil.h"
#include "mkstr.h"
#include "slist.h"
#include "copyright.h"

// first argument is destination file , second argumetn is directory of nodes


void usage()
{
	cerr << "Usage:\nnod_sort -m menu destination_direcotry\n" ; 
	cerr << "nod_sort -s suffix dest_file [-d dir] [-f file] [-e file]\n" ;
	cerr << "(-e excludes files (name can be a full path or name in\n" ;
	cerr << "in a directory) Must have at least one -f or -d any beyond.\n" ;
	cerr << "that any number of -d -e or -f options is allowd up to 1000.\n";
	exit(1);
}


const buf_size = 8192 ;
struct FileDesc {
	const char * file ;
	const char * obj_class ;
	const char * var_dir ;
	static const char * dir ;
	static char buf[buf_size];
	const char * file_name() ;
	const char * var_file_name() ;
	FileDesc(const char * f);
	FileDesc(const char *f, const char * dir);
};

const char * FileDesc::dir = 0 ;
char FileDesc::buf[buf_size] ;




const char * FileDesc::var_file_name()
{
	strcpy(buf,var_dir);
	strcat(buf,"/");
	strcat(buf,file);
	return buf ;
}

const char * FileDesc::file_name()
{
	strcpy(buf,dir);
	strcat(buf,"/");
	strcat(buf,file);
	return buf ;
}


FileDesc::FileDesc(const char *f, const char *d):file(f),var_dir(d)
{
	// cerr << "FileDesc::FileDesc(\"" << f << "\",\"" << d << "\")\n" ;
	const buf_size = 8192;
	char buf[buf_size];
	if (var_dir) if (*var_dir) file = Concatenate(var_file_name());
	ifstream in(file);
	if (!in.good()) {
		cerr << "Cannot read file `" << file << "'.\n" ;
		usage();
	}
	AddCopyright& ck_comment = AddCopyright::add_copyright();
	char * start = 0 ;
	char * b = 0 ;
	while(in.good())
	{
		buf[0]='\n' ;
		in.getline(buf,buf_size);
		// cerr << "Checking: " << file << " `" << buf << "'\n" ;
		if (ck_comment.may_be_comment(buf,file)) continue ;
		// cerr << "not a comment\n" ;
		for (b = buf; *b != '\n' ; b++) {
			if (!start) {
				if (*b != ' ') continue ;
				else {
					start = b + 1 ;
					continue ;
				}
			}

			if (*b == '}') break ;
			else if (!isalnum(*b)) {
				start = 0 ;
				break ;
			}
		}
		if (start) break ;
	} // end loop on lines
	if (!start) {
		cerr << "Cannot find class name in `" << file << "'.\n" ;
		usage();
	}
	*b = '\0' ;
	obj_class = Concatenate(start);
	// cerr << "obj_class 1 for `" << file  << "' is `" << obj_class << "'.\n" ;
}

FileDesc::FileDesc(const char *f):file(f)
{
	const buf_size = 8192;
	char buf[buf_size];
	file = Concatenate(file_name());
	ifstream in(file);
	if (!in.good()) {
		cerr << "Cannot read file `" << file << "'.\n" ;
		exit(1);
	}
	AddCopyright& ck_comment = AddCopyright::add_copyright();
	char * start = 0 ;
	char * b = 0 ;
	while(in.good()) {
		buf[0]='\n' ;
		in.getline(buf,buf_size);
		if (ck_comment.may_be_comment(buf,file)) continue ;
		if (!*buf) continue ;
		start = 0 ;
		for (b = buf; *b != '\n' ; b++) if (isalnum(*b)) {
				if (!start) start = b ;
		} else if (start) break ;
		if (!start) {
			cerr << "Cannot find class name in `" << file << "'.\n" ;
			exit(1);
		}
		break ;
	}
	*b = '\0' ;
	obj_class = Concatenate(start);
	// cerr << "obj_class 2 for `" << file  << "' is `" << obj_class << "'.\n" ;
}

static int compare(const void * a, const void * b)
{
    const FileDesc ** A = (const FileDesc **) a ;
    const FileDesc ** B = (const FileDesc **) b ;
    if (!A) if (!B) return 0 ; else return -1 ;
    if (!B) return 1 ;

    const char * Aa = (*A)->obj_class ;
    const char * Bb = (*B)->obj_class ;
    if (!Aa) if (!Bb) return 0; else return -1 ;
    if (!Bb) return 1 ;
    return TeXstrcmp(Aa,Bb);
}


int exclude(const char * name, ConstStringList& exc,const char *suf)

{
	char * no_suf = RemoveSuffix(name,suf);
	ConstStringListIterator next(exc);
	const char * f ;
	while (f=next()) {
		if (!strcmp(no_suf,f)) return 1 ;
		if (*no_suf != '/') {
			char * no_dir = OpdRemoveDirectory(f);
			int ret = 0 ;
			if (!strcmp(no_dir,no_suf)) ret = 1 ;
			delete no_dir ;
			if (ret) {
				// cerr << "Exclude `" << no_dir << "' `" << no_suf << "'.\n" ;
				return 1 ;
			}
		}
	}
	delete no_suf ;
	return 0 ;
}

void process_tex_nodes(int argc, char **argv)
{
	ConstStringList inc ;
	ConstStringList exc ;
	const max_dirs = 1000 ;
	ExamineDirectory * the_dirs[max_dirs] ;
	int next_dir = 0 ;
	int next_arg = 1 ;
	if (strcmp(argv[next_arg++],"-s")) usage();
	if (argc < 6) usage();
	const char * subdir = argv[next_arg++] ;
	// const char * suffix = Concatenate(".",subdir) ;
	const char * suffix = subdir ;
	const char * dest = argv[next_arg++] ;
/*
 *	cerr << "Seaching for suffix `" << suffix << "', writing to `" << dest
 *		<< "'.\n" ;
 */
	int file_count = 0 ;
	for (;next_arg< argc;next_arg++) {
		if (!strcmp(argv[next_arg],"-d")) {
			if (++next_arg >= argc) usage() ;
			if (next_dir >= max_dirs) usage();
			char * dir = Concatenate(argv[next_arg],"/",subdir);
/*
 *			cerr << "Checking directory `" << argv[next_arg] <<
 *				"', next_dir = " << next_dir << "\n`" << dir << "'\n" ;
 */
			ExamineDirectory & ex_dir = *(the_dirs[next_dir++] =
				new ExamineDirectory( dir,".tex"));
			int number = ex_dir.GetNumberFiles();
			// cerr << "Found " << number << " files.\n" ;
			file_count += number ;
			if (number < 1) cerr << "WARNING no `" << suffix <<
				"' files found in `" << argv[next_arg] << "'.\n" ;
			continue ;
		}
		if (!strcmp(argv[next_arg],"-f")) {
			if (++next_arg >= argc) usage() ;

			file_count++ ;
			inc.Append(argv[next_arg]);
			continue ;
		}
		if (!strcmp(argv[next_arg],"-e")) {
			if (++next_arg >= argc) usage() ;
            exc.Append(RemoveSuffix(argv[next_arg],suffix));
			continue ;
		}
		usage();
	}
	FileDesc ** file_desc = new FileDesc * [file_count+1] ;
	FileDesc ** f = file_desc ;
	const char * file ;
	for (int d = 0 ; d < next_dir; d++) {
		ExamineDirectory& ex_dir = *(the_dirs[d]);
		const char * directory = ex_dir.directory();
		while (file = ex_dir.NextFile()) {
			// cerr << "Checking file `" << file << "', f =" << (void *) f << "\n" ;
			*f = new FileDesc(file,directory);
			if (!exclude((*f)->file,exc,suffix)) f++ ;
			// else  cerr << "Excluded `" << (*f)->file << "'\n" ;
		}
	}
	ConstStringListIterator next(inc);
	while (file = next()) *f++ = new FileDesc(file,"");
	*f++ = 0 ;	
	// cerr << "f = " << (void *) f << "\n" ;
	// cerr << "file_desc = " << (void *) file_desc << "\n" ;
	int num = f-file_desc - 1 ;
	// cerr << "Sorting " << num << ", " << sizeof(*f) << "\n" ;
	qsort((char *) file_desc, num, sizeof(file_desc[0]),compare);

	FILE * out = fopen(dest,"w") ;
	if (!out) {
		cerr << "Cannot create destination file `" << dest << "'.\n" ;
		usage();
	}
	for (f = file_desc; *f; f++) fprintf(out,"\\input{%s}\n",
		(*f)->file);
	fclose(out);	
	
}


main(int argc, char **argv)
{
	const char * suf = ".nod" ;
	if (argc <= 4) usage();
	if (strcmp(argv[1],"-m")) {
		process_tex_nodes(argc,argv);
		exit(0);
	}
	if (argc != 5) usage() ;
	ofstream out(argv[3]) ;
	if (!out.good()) {
		cerr << "Cannot create `" << argv[3] << "'.\n" ;
		exit(1);
	}
	const char * dir_name = argv[4] ;
	const char * menu = argv[2] ;
	FileDesc::dir = dir_name ;
	ExamineDirectory dir(dir_name,suf);
	int number = dir.GetNumberFiles();
	if (number < 1) {
		cerr << "No `" << suf << "' files found in `" << dir_name << "'.\n" ;
		exit(1);
	}
	FileDesc ** file_desc = new FileDesc * [number+1] ;
	for (int i = 0 ; i < number; i++)
		file_desc[i] = new FileDesc(dir.NextFile()) ;
	file_desc[i] = 0 ;
	qsort((char *) file_desc, number, sizeof(file_desc[0]),compare);


	const buf_size = 8192 ;
	char buf[buf_size] ;
	FILE * in = fopen(menu,"r");
	if (!in) cerr << "Cannot copy menu file `" << menu << "'.\n" ;
    int read ;
    while(read = fread(buf,sizeof(buf[0]),buf_size,in)) out.write(buf,read);
	fclose(in);
                                                              
	for (FileDesc ** f = file_desc; *f; f++) {
		// cerr << (*f)->obj_class << " " << (*f)->file << "\n" ;
		FILE * in = fopen((*f)->file,"r");
		if (!in) cerr << "Cannot copy file `" << (*file_desc)->file
			<< "'.\n" ;
		int read ;
		while(read = fread(buf,sizeof(buf[0]),buf_size,in))
			out.write(buf,read);
		fclose(in);
	}
	out.flush();
	exit(0);
}
