
// clucene includes
#include "CLucene.h"
#include "CLucene/util/Reader.h"
#include "CLucene/util/Misc.h"
#include "CLucene/queryParser/MultiFieldQueryParser.h"
#include "CLucene/search/PrefixQuery.h"

#ifdef HIGHLIGHTING
# include "..\contributions\highlighter\QueryTermExtractor.h"
# include "..\contributions\highlighter\QueryScorer.h"
# include "..\contributions\highlighter\Highlighter.h"
# include "..\contributions\highlighter\SimpleHTMLFormatter.h"

#define HIGHLIGHT_HTML_START _T("<b>")
#define HIGHLIGHT_HTML_END _T("</b>")
#define HIGHLIGHT_FRAGMENT_SIZE 100
#define HIGHLIGHT_MAX_NUM_FRAGMENTS 3
#define HIGHLIGHT_SEPARATOR _T("...")

using namespace lucene::search::highlight;
#endif

#include <iostream>

#include "clucene_dll.h"

using namespace std;

using namespace lucene::index;
using namespace lucene::analysis;
using namespace lucene::analysis::standard;
using namespace lucene::util;
using namespace lucene::store;
using namespace lucene::document;
using namespace lucene::search;
using namespace lucene::queryParser;



//private================================
struct Resource{
public:
    //searcher
    Document* doc;
    IndexSearcher* searcher;
    Query* query;
    Hits* hits;
    int32_t docId;
	int32_t hitPos;
    int count;
    char directory[CL_MAX_DIR];
    CL_NS(util)::CLArrayList<SortComparatorSource*,CL_NS(util)::Deletor::Object<SortComparatorSource> > customsorters;

    //general
    TCHAR errstr[errlen];
    //Directory* dir;
	IndexReader* reader;
	TermDocs* termDocs;
	Filter* filter;
	Sort* sort;
    
    Resource(const char* dir){
        doc = NULL;
        searcher = NULL;
        query = NULL;
        hits = NULL;
        docId = 0;
		hitPos = 0;
        errstr[0]=0;
	    reader = NULL;
		termDocs = NULL;
	    count=1;
		strncpy(directory,dir,CL_MAX_DIR);
		filter = NULL;
		sort = NULL;
    }
    ~Resource(){
    	_CLDELETE( doc );
		_CLDELETE( reader );
		_CLDELETE( termDocs );
		_CLDELETE( sort );
		_CLDELETE( filter );
    }

};

extern "C" {
	//
	Resource* resources[MAX_CLUCENE_DLL_RESOURCES];
	int resourcesInitialised = 0;
	bool resource_mutex = false;
	TCHAR globalerrstr[errlen];
	StandardAnalyzer analyzer;
	int isValidResource(const int resource, const int reportFail = 1);
}

void reportGlobalError(const wchar_t* error){
	unsigned int i;
	if ( error == NULL )
		return;

	for ( i=0;i<errlen-1&&i<wcslen(error);i++ )
		globalerrstr[i]=error[i];
	globalerrstr[i]=0;
	//CL_CallBack_Error ( errstr );
}
void reportError(const wchar_t* error, const int resource){
	unsigned int i;
	if ( error == NULL )
		return;

    if ( isValidResource(resource) ){
    	for ( i=0;i<errlen-1&&i<wcslen(error);i++ )
    		resources[resource]->errstr[i]=error[i];
    	resources[resource]->errstr[i]=0;
    	//CL_CallBack_Error ( errstr );
	}
	
}

void reportGlobalError(const char* error){
	unsigned int i;
	if ( error == NULL )
		return;

	for ( i=0;i<errlen-1&&i<strlen(error);i++ )
		globalerrstr[i]=error[i];
	globalerrstr[i]=0;
	//CL_CallBack_Error ( errstr );
}
void reportError(const char* error, const int resource){
	unsigned int i;
	if ( error == NULL )
		return;

    if ( isValidResource(resource) ){
    	for ( i=0;i<errlen-1&&i<strlen(error);i++ )
    		resources[resource]->errstr[i]=error[i];
    	resources[resource]->errstr[i]=0;
    	//CL_CallBack_Error ( errstr );
	}
	
}

int getResource(const char* dir){
	//multithreading mutex lock...
	while ( resource_mutex ){
		_sleep(5);	
	}
	
	resource_mutex = true;
	try{
		if ( !resourcesInitialised ){
			for ( int i=1;i<MAX_CLUCENE_DLL_RESOURCES;i++ )
				resources[i] = NULL;
			resourcesInitialised = 1;
		}
		int pos = 0;
	    for ( int i=1;i<MAX_CLUCENE_DLL_RESOURCES;i++ ){
	        if ( pos == 0 && resources[i] == NULL ){
	            pos = i;
	        }
	        if ( resources[i] != NULL && strcmp(resources[i]->directory,dir)==0 ){
	            //directory already found
	            resources[i]->count++;
	            resource_mutex = false;
	            return i;
	        }
	    }
	    if ( pos > 0 ){
	        //no existing directory found
	        resources[pos] = new Resource(dir);
	        resource_mutex = false;
	        return pos;
	    }
	    reportGlobalError(_T("Max resource limit exceeded"));
	}_CLFINALLY(
		resource_mutex = false;
	);
    return 0;
}


extern "C" {

int isValidResource(const int resource, const int reportFail){
	if ( resource > -1 && resource < MAX_CLUCENE_DLL_RESOURCES && resources[resource] != NULL )
		return true;
	else if ( reportFail ){
		reportGlobalError(_T("Not a valid resource"));
		return false;
	}else
		return false;
}
	
void resetError(const int resource){
	globalerrstr[0]=0;
	if ( resource > 0 && resource < MAX_CLUCENE_DLL_RESOURCES && resources[resource] != NULL )
	    resources[resource]->errstr[0]=0;
}

#ifndef CLUCENE_LITE
int _addField(const int resource, const TCHAR* wfield, Reader* reader, const int store, const int index, const int token){
	resetError(resource);
	int ret = 0;
	if ( isValidResource(resource) ){
		if ( resources[resource]->doc == NULL ){
			CL_T_New_Document(resource);
		}

		try{
			lucene::document::Field* fld = _CLNEW lucene::document::Field(wfield,reader,store!=0,index!=0,token!=0);
			resources[resource]->doc->add(*fld);
			ret = 1;
		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( _T("Unknown error"), resource);
		}
	}
	return ret;
}
#endif

//search an 
int CL_SEARCHCURRENTQUERY(const int resource){
	int ret = 0;
	if ( isValidResource(resource) ){
		try{
			resources[resource]->searcher = _CLNEW IndexSearcher(resources[resource]->reader);
			if ( resources[resource]->sort == NULL )
				resources[resource]->hits = resources[resource]->searcher->search(resources[resource]->query, resources[resource]->filter);
			else
				resources[resource]->hits = resources[resource]->searcher->search(resources[resource]->query, resources[resource]->filter, resources[resource]->sort);


			resources[resource]->hitPos = 0;
			if ( resources[resource]->hitPos < resources[resource]->hits->length() )
				resources[resource]->docId = resources[resource]->hits->id(resources[resource]->hitPos);
			else
				resources[resource]->docId = 0;

			ret = 1;
		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( _T("Unknown error"), resource);
		}
	}
	return ret;
}

//
//end of private ================================

CLUCENEDLL_API void CL_DeleteMemory(void* data){
	_CLDELETE(data);
}
CLUCENEDLL_API void CL_DeleteArrayMemory(void* data){
	_CLDELETE_ARRAY(data);
}

//start of ascii equivalents ================================
#ifdef _UCS2 //no point if not ucs2
CL_T_Unlock(const TCHAR* dir){
	char* tmp = STRDUP_TtoA(dir);
	int ret = CL_A_Unlock(tmp);
	_CLDELETE_CARRAY(tmp);
	return ret;
}
int CL_T_Open(const TCHAR* dir, int create){
	char* tmp = STRDUP_TtoA(dir);
	int ret = CL_A_Open(tmp,create);
	_CLDELETE_CARRAY(tmp);
	return ret;
}
int CL_T_Add_File(const int resource, const TCHAR* field, const TCHAR* filename, const TCHAR* encoding_scheme, const int store, const int index, const int token){
	char* tmp1 = STRDUP_TtoA(field);
	char* tmp2 = STRDUP_TtoA(filename);
	char* tmp3 = STRDUP_TtoA(encoding_scheme);
	int ret = CL_A_Add_File(resource,tmp1,tmp2,tmp3,store,index,token);
	_CLDELETE_CARRAY(tmp1);
	_CLDELETE_CARRAY(tmp2);
	_CLDELETE_CARRAY(tmp3);
	return ret;
}

#endif
//end of ascii equivalents ================================

//returns resource number
CLUCENEDLL_API int CL_A_Open(const char* dir, int create){
	resetError(-1);
	const int resource = getResource(dir);
	if ( isValidResource(resource,0) ){
    	try{
    		if ( IndexReader::indexExists(dir) ){
				resources[resource]->reader = IndexReader::open(dir);
    			return resource;
    		}else if ( create ){
#ifndef CLUCENE_LITE
				Directory* d = FSDirectory::getDirectory( dir,true );
				IndexWriter w(d,&analyzer,true);
    			w.close();

				resources[resource]->reader = IndexReader::open(d);
    			return resource;
#else
				reportGlobalError("Can't create directory in Lite Mode");
#endif
    		}
    	}catch(CLuceneError& e){
    		reportGlobalError( e.what());
    	}catch(...){
    		reportGlobalError( _T("Unknown error") );
    	}
    }
	return 0;
}


//info must be an inistialised char array, a maximum len will be copied
CLUCENEDLL_API void CL_DOCUMENT_INFO(const int resource, TCHAR* info, const int len){
	if ( isValidResource(resource) ){
		if ( resources[resource]->doc != NULL ){
			const TCHAR* inf = resources[resource]->doc->toString();
			_tcsncpy(info,inf,len);
			_CLDELETE_ARRAY(inf);
		}else{
			info[0]=0;
			reportError(_T("No document available"), resource);
		}
	}
}

class GroupingComparator: public ScoreDocComparator {
	uint8_t* order;
	int32_t length;
public:
	GroupingComparator(uint8_t* order, int32_t length)
	{
		this->order = order;
		this->length = length;
	}
	~GroupingComparator(){		
		_CLDELETE_ARRAY(order);
	}

	int32_t compare (ScoreDoc* i, ScoreDoc* j) {
		CND_PRECONDITION(i->doc<length, "i->doc>=length")
		CND_PRECONDITION(j->doc<length, "j->doc>=length")
		int32_t fi = order[i->doc];
		int32_t fj = order[j->doc];
		if (fi < fj) 
			return -1;
		if (fi > fj) 
			return 1;
		return 0;
	}

	CL_NS(util)::Comparable* sortValue (ScoreDoc* i) {
		return _CLNEW CL_NS(util)::Compare::Int32(i->doc);
	}

	int32_t sortType() {
		return SortField::CUSTOM;
	}
};
class GroupingComparatorSource: public SortComparatorSource {
	CLSet<const TCHAR*, int32_t, CL_NS(util)::Compare::TChar, CL_NS(util)::Deletor::tcArray, CL_NS(util)::Deletor::DummyInt32> groups;
	size_t hc;
public:
	GroupingComparatorSource(const TCHAR** groups){
		int i;
		hc = 0;
		for(i=0;groups[i]!=NULL;i++){
			hc ^= Misc::thashCode(groups[i]);
			this->groups.put(STRDUP_TtoT(groups[i]), i+1);
		}

		if ( i > LUCENE_UINT8_MAX_SHOULDBE )
			_CLTHROWA(CL_ERR_Runtime,"Groups length must be less than 255");

	}
	~GroupingComparatorSource(){
	}

   //return a reference to a string describing the name of the comparator
   //this is used in the explanation
	TCHAR* getName(){
		return _T("Grouping Comparator Source");
	}

	size_t hashCode(){
		return hc;
	}

  /**
   * Creates a comparator for the field in the given index.
   * @param reader Index to create comparator for.
   * @param fieldname  Field to create comparator for.
   * @return Comparator of ScoreDoc objects.
   * @throws IOException If an error occurs reading the index.
   */
	ScoreDocComparator* newComparator (CL_NS(index)::IndexReader* reader, const TCHAR* fieldname){
		int32_t length = reader->maxDoc();
		uint8_t* order;
		if ( length > 0 ){
			//create the order compare
			order = _CL_NEWARRAY(uint8_t,length);
			memset(order,0,sizeof(uint8_t)*length);

			//get the term docs
			TermDocs* termDocs = reader->termDocs();

			//and skip to the field we are talking about
			Term* term = _CLNEW Term (fieldname, LUCENE_BLANK_STRING);
			TermEnum* termEnum = reader->terms (term);
			_CLDECDELETE(term);

			try {
				if (termEnum->term(false) == NULL) {
					_CLTHROWA(CL_ERR_Runtime,"no terms in field"); //todo: make rich message " + field);
				}

				do {
					Term* term = termEnum->term(false);

					//if the term is different, then we are done
					if ( _tcscmp(term->field(),fieldname) != 0 ) 
						break;

					if ( this->groups.exists(term->text()) ){
						//if we are grouping this term, then we use the order here.
						int32_t ordr = this->groups.get(term->text());

						//find which documents contain this term
						termDocs->seek (termEnum);

						//go through each document and give it its order
						while (termDocs->next()) {
							order[termDocs->doc()] = ordr;
						}
					}
					
				} while (termEnum->next());

			} _CLFINALLY(
				termDocs->close();
				_CLDELETE(termDocs);
				termEnum->close();
				_CLDELETE(termEnum);
			);

		}else{
			order = NULL;
		}

		return _CLNEW GroupingComparator(order, length);
	}
};


CLUCENEDLL_API void CL_ADDSORTGROUPING ( const int resource, const wchar_t* field, const wchar_t** groups ){
	if ( isValidResource(resource) ){
		try{
			CLLinkedList<SortField*> fields(false);
			if ( resources[resource]->sort == NULL ){
				resources[resource]->sort = _CLNEW lucene::search::Sort();
			}else{
				SortField** sf = resources[resource]->sort->getFields();
				for(int i=0;sf[i]!=NULL;i++){
					if ( sf[i] != SortField::FIELD_SCORE &&
						 sf[i] != SortField::FIELD_DOC ){
						fields.put(sf[i]->clone());
					}
				}
			}

			GroupingComparatorSource* grping = _CLNEW GroupingComparatorSource(groups);
			resources[resource]->customsorters.put(grping);
			fields.put( _CLNEW SortField(field, grping,false) );
				

			SortField** flds = _CL_NEWARRAY(SortField*,fields.size()+1);
			fields.toArray(flds);

			resources[resource]->sort->setSort(flds);
			_CLDELETE_ARRAY(flds)


		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( _T("Unknown error"), resource);
		}
	}
}

CLUCENEDLL_API void CL_ADDSORTFIELD (const int resource, const wchar_t* field, const int type, const bool reverse){
	if ( isValidResource(resource) ){
		try{
			CLLinkedList<SortField*> fields(false);
			if ( resources[resource]->sort == NULL ){
				resources[resource]->sort = _CLNEW lucene::search::Sort();
			}else{
				SortField** sf = resources[resource]->sort->getFields();
				for(int i=0;sf[i]!=NULL;i++){
					if ( sf[i] != SortField::FIELD_SCORE &&
						 sf[i] != SortField::FIELD_DOC ){
						fields.put(sf[i]->clone());
					}
				}
			}
			
			if ( type == SortField::SCORE )
				fields.put( SortField::FIELD_SCORE );
			else if ( type == SortField::DOC )
				fields.put( SortField::FIELD_DOC );
			else if ( type == SortField::AUTO || 
					  type == SortField::STRING || 
					  type == SortField::INT || 
					  type == SortField::FLOAT ){
				fields.put( _CLNEW SortField(field, type, reverse) );
			}

			SortField** flds = _CL_NEWARRAY(SortField*,fields.size()+1);
			fields.toArray(flds);

			resources[resource]->sort->setSort(flds);
			_CLDELETE_ARRAY(flds)


		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( _T("Unknown error"), resource);
		}
	}
}

CLUCENEDLL_API void CL_FILTERDATE (const int resource, const TCHAR* field, const type_long date, const bool after){
	if ( isValidResource(resource) ){
		try{
			if ( resources[resource]->filter != NULL )
				_CLDELETE(resources[resource]->filter);
			if ( after )
				resources[resource]->filter = lucene::search::DateFilter::After(field,date);
			else
				resources[resource]->filter = lucene::search::DateFilter::Before(field,date);

		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( _T("Unknown error"), resource);
		}
	}
}
CLUCENEDLL_API void CL_FILTERDATE_EX (const int resource, const TCHAR* field, const type_long from, const type_long to){
	if ( isValidResource(resource) ){
		try{
			if ( resources[resource]->filter != NULL )
				_CLDELETE(resources[resource]->filter);
			resources[resource]->filter = _CLNEW lucene::search::DateFilter(field,from,to);

		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( _T("Unknown error"), resource);
		}
	}
}


CLUCENEDLL_API int CL_SEARCHMULTIFIELDSFLAGGED(const int resource, const TCHAR* query, const TCHAR** fields, const int fieldsLen, const uint8_t* flags, bool execute){
	int ret = 0;
	if ( isValidResource(resource) ){
		
		const TCHAR** tmp = _CL_NEWARRAY(const TCHAR*,fieldsLen+1);
		for (int i=0;i<fieldsLen;i++)
			tmp[i]=fields[i];
		tmp[fieldsLen]=NULL;

		Query* q = MultiFieldQueryParser::parse(query,tmp,flags,&analyzer);
		_CLDELETE_ARRAY(tmp);

		CL_CLEARSEARCH(resource);
		resources[resource]->query = q;

		if ( execute )
			ret = CL_SEARCHCURRENTQUERY(resource);
		else
			ret = 1;
	}
	return ret;
}

CLUCENEDLL_API int CL_SEARCHMULTIFIELDS(const int resource, const TCHAR* qry, const TCHAR** fields, const int fieldsLen, bool execute){
	int ret = 0;
	if ( isValidResource(resource) ){
		uint8_t* flags = _CL_NEWARRAY(uint8_t,fieldsLen);
		for ( int i=0;i<fieldsLen;i++ )
			flags[i] = queryParser_NORMAL_FIELD;
		ret = CL_SEARCHMULTIFIELDSFLAGGED(resource,qry,fields,fieldsLen,flags, execute);
		_CLDELETE_ARRAY (flags);
	}
	return ret;
}

CLUCENEDLL_API int CL_SEARCH(const int resource, const TCHAR* query, const TCHAR* field, bool execute){
	int ret = 0;
	if ( isValidResource(resource) ){
		Query* q = QueryParser::parse(query,field,&analyzer);

		CL_CLEARSEARCH(resource);
		resources[resource]->query = q;
		
		if ( execute )
			ret = CL_SEARCHCURRENTQUERY(resource);
		else
			ret = 1;
	}
	return ret;
}
CLUCENEDLL_API void CL_GET_TERMDOCS(const int resource, const TCHAR* field, const TCHAR* value){
	if ( isValidResource(resource) ){
		Term* term = _CLNEW Term(field,value);
		_CLDELETE(resources[resource]->termDocs);
		resources[resource]->termDocs=resources[resource]->reader->termDocs(term);
		_CLDECDELETE(term);
	}
}

CLUCENEDLL_API void CL_ADDQUERY(const int resource, const TCHAR* field, const TCHAR* text, const int query_type, const bool required, const bool prohibited){
	if ( isValidResource(resource) ){
		if ( resources[resource]->query == NULL )
			resources[resource]->query = _CLNEW BooleanQuery();

		BooleanQuery* bq = NULL;
		if ( !resources[resource]->query->instanceOf(_T("BooleanQuery") ) ){
			bq = _CLNEW BooleanQuery();
			BooleanClause* bc = _CLNEW BooleanClause(resources[resource]->query,true,required,prohibited);
			bq->add(bc);
			resources[resource]->query = bq;
		}else
			bq = (BooleanQuery*)resources[resource]->query;

		if ( query_type == LUCENE_QUERY_TERM ){
			Term* term = _CLNEW Term(field,text);
			Query* query = _CLNEW TermQuery(term);
			bq->add(query,true,required,prohibited);
			_CLDECDELETE(term);
		}else if ( query_type == LUCENE_QUERY_PREFIX ){
			Term* term = _CLNEW Term(field,text);
			Query* query = (Query*)_CLNEW PrefixQuery(term);
			bq->add(query,true,required,prohibited);
			_CLDECDELETE(term);

		//for a range query, make the date(in base 10 format)
		//negative to specify the start date, and positive for the end date
		}else if ( query_type == LUCENE_QUERY_RANGEDATE ){
			TCHAR* end = NULL;
			type_long dt = _tcstoi64(text,&end,10);
			int sign =1;
			if ( dt < 0 ){
				sign =-1;
				dt *= -1; //make positive
			}
			TCHAR* date = lucene::document::DateField::timeToString(dt);
			Term* term = _CLNEW Term(field,date);
			_CLDELETE_CARRAY(date);

			if ( sign == -1 ){
				Query* query = (Query*)_CLNEW RangeQuery(term,NULL,true);
				bq->add(query,true,required,prohibited);
			}else{
				Query* query = (Query*)_CLNEW RangeQuery(NULL,term,true);
				bq->add(query,true,required,prohibited);
			}
			_CLDECDELETE(term);
		}else{
			reportError(_T("Undefined or unimplemented query type"),resource);
		}
	}
}

CLUCENEDLL_API void CL_SEARCHINFO(const int resource, TCHAR* pl, size_t len){
	if ( isValidResource(resource) ){
		Query* q = resources[resource]->query;
		if ( q != NULL ){
			const TCHAR* buf = q->toString(_T(""));
			_tcsncpy(pl,buf,len);
			_CLDELETE_ARRAY (buf);
		}
	}
}

#ifdef HIGHLIGHTING
CLUCENEDLL_API void CL_HIGHLIGHT_X(const int resource, const TCHAR* text, const int text_is_filename, TCHAR* ret, const size_t ret_len, const TCHAR* separator, const int max_fragments, const int fragment_size, const int type, const TCHAR* d1, const TCHAR* d2){
	if ( isValidResource(resource) ){
		Query* q = resources[resource]->query;
		if ( q != NULL ){
			SimpleAnalyzer simpleAnalyzer;
			if ( type == 1 ){
				SimpleHTMLFormatter formatter( d1, d2);
				QueryScorer scorer(q);
				SimpleFragmenter fragmenter(fragment_size);
				Highlighter highlighter(&formatter, &scorer);
				highlighter.setTextFragmenter(&fragmenter);


				if ( text_is_filename != 0 ){
					char atext[CL_MAX_PATH];
					STRCPY_TtoA(atext,text,CL_MAX_PATH);
					Reader* r = _CLNEW FileReader(atext,PLATFORM_DEFAULT_READER_ENCODING); //todo: make getBestFragments use an filereader object
					TCHAR* txt = _CL_NEWARRAY(TCHAR,r->available()+1);
					int32_t rd = r->read(txt,0,r->available());
					txt[rd] = 0;
					StringReader reader(txt,rd);
					TokenStream* ts = simpleAnalyzer.tokenStream(_T("dummy"), &reader);
					
					// Compare two best fragments
					TCHAR* highlighted_text = highlighter.getBestFragments(ts, txt, max_fragments, separator);
					_tcsncpy(ret,highlighted_text,ret_len);

					_CLDELETE_ARRAY(txt);
					_CLDELETE_ARRAY (highlighted_text);
					ts->close();
					_CLDELETE( ts );
					_CLDELETE( r );
				}else{
					StringReader reader(text);
					TokenStream* ts = simpleAnalyzer.tokenStream(_T("dummy"), &reader);
					
					// Compare two best fragments
					TCHAR* highlighted_text = highlighter.getBestFragments(ts, text, max_fragments, separator);
					_tcsncpy(ret,highlighted_text,ret_len);
					
					_CLDELETE_ARRAY (highlighted_text);
					ts->close();
					_CLDELETE( ts );
				}
				
			}
		}
	}
}
CLUCENEDLL_API void CL_HIGHLIGHT(const int resource, const TCHAR* text, const int text_is_filename, TCHAR* ret, const size_t ret_len){
	CL_HIGHLIGHT_X(resource,text,text_is_filename,ret,ret_len,HIGHLIGHT_SEPARATOR,HIGHLIGHT_MAX_NUM_FRAGMENTS,HIGHLIGHT_FRAGMENT_SIZE, 1,HIGHLIGHT_HTML_START,HIGHLIGHT_HTML_END);
}
#endif

CLUCENEDLL_API type_long CL_GETDATEFIELD(const int resource, const TCHAR* field){
	type_long ret = 0;
	if ( isValidResource(resource) ){
		
		if ( resources[resource]->termDocs==NULL && ( resources[resource]->hits == NULL || resources[resource]->searcher == NULL ) ){
			reportError ( _T("No termDocs, hits or searcher available"), resource);
			return 0;
		}

		try{
			Document* doc = resources[resource]->reader->document(resources[resource]->docId);
			if ( doc != NULL ){
				const TCHAR* val = doc->get(field);
				
				if ( val != NULL ){
					try{
						ret = DateField::stringToTime(val);
					}catch(...){
						reportError(_T("Field is not a date"), resource);
					}
				}else{
					reportError(_T("Field does not exist"), resource);
				}
				_CLDELETE( doc );
			}
		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( _T("Unknown error"), resource);
		}
	}
	return ret;
}

//value will be initialised as a char array with a length of value_len
CLUCENEDLL_API int CL_GETFIELD(const int resource, const TCHAR* field, TCHAR** value, size_t* value_len){
	int ret = 0;
	if ( isValidResource(resource) ){
		*value_len = 0;


		if ( resources[resource]->termDocs==NULL && ( resources[resource]->hits == NULL || resources[resource]->searcher == NULL ) ){
			reportError ( _T("No termDocs, hits or searcher available"), resource);
			return 0;
		}

		try{
			Document* doc = resources[resource]->reader->document(resources[resource]->docId);
			if ( doc != NULL ){
				//todo: should be a way of getting value length.. not binary safe
				const TCHAR* val = doc->get(field); //a reference to that field
				
				if ( val != NULL ){
					*value_len = _tcslen (val);
					*value = _CL_NEWARRAY(TCHAR,*value_len+1);
					_tcsncpy(*value,val,*value_len+1);
					ret = 1;
				}else{
					reportError(_T("Field does not exist"), resource);
				}
				_CLDELETE( doc );
			}
		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( _T("Unknown error"), resource);
		}
	}
	return ret;
}


#ifdef _UNICODE
//copy error string. 'pl' will be a maximum length of 'len'
CLUCENEDLL_API void CL_T_ErrStr(const int resource, TCHAR* pl, size_t len){
    //resetError(resource);
    if ( isValidResource(resource,false) ){
    	size_t i=0;
    	for ( i=0; i<len && i<errlen && i<_tcslen(pl); i++ )
    		pl[i]=resources[resource]->errstr[i];
    	pl[i]=0;
	}else
		CL_ERRSTRGLOBAL(pl,len);
}
#endif

CLUCENEDLL_API void CL_A_ErrStr(const int resource, char* pl, size_t len){
    //resetError(resource);
    if ( isValidResource(resource,false) ){
    	size_t l=strlen(pl);
		l = l<errlen?l:errlen;
		l = l<len?l:len;
		STRCPY_AtoT(resources[resource]->errstr,pl,l);
    	pl[l]=0;
	}else
		CL_A_ErrStrGlobal(pl,len);
}

#ifdef _UNICODE
//copy global error string. 'pl' will be a maximum length of 'len'
CLUCENEDLL_API void CL_T_ErrStrGlobal(TCHAR* pl, size_t len){
    size_t i=0;
	for ( i=0; i<len && i<errlen && i<_tcslen(pl); i++ )
		pl[i]=globalerrstr[i];
	pl[i]=0;
}
#endif
CLUCENEDLL_API void CL_A_ErrStrGlobal(char* pl, size_t len){
    size_t l=strlen(pl);
	l = l<errlen?l:errlen;
	l = l<len?l:len;
	STRCPY_AtoT(globalerrstr,pl,l);
    pl[l]=0;
}




#ifndef CLUCENE_LITE
CLUCENEDLL_API int CL_A_Unlock(const char* directory){
	resetError(-1);
	int ret = 0;
	try{
		FSDirectory* dir = FSDirectory::getDirectory(directory,false);
		IndexReader::unlock(dir);
		_CLDECDELETE(dir);
		ret = 1;
	}catch(CLuceneError& e){
    	reportGlobalError( e.what());
    }catch(...){
    	reportGlobalError( _T("Unknown error") );
    }
	return ret;
}

//adds a field to the current document
CLUCENEDLL_API int CL_A_Add_File(const int resource, const char* field, const char* filename, const char* encoding_scheme, const int store, const int index, const int token){
	resetError(resource);
	int ret = 0;
	if ( isValidResource(resource) ){
		try{
			FileReader* reader = _CLNEW FileReader(filename,encoding_scheme);
			
			TCHAR* tmp = STRDUP_AtoT(field);
			ret = _addField(resource,tmp,reader,store,index,token);
			_CLDELETE_CARRAY(tmp);
		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( _T("Unknown error"), resource);
		}
	}
	return ret;
}


//adds a field to the current document
CLUCENEDLL_API int CL_ADD_FIELD(const int resource, const TCHAR* field, const TCHAR* value, const int value_length, const int store, const int index, const int token){
	//wvalue is taken by the reader, no need to delete it... gets deleted by field
	StringReader* reader = _CLNEW StringReader(value,value_length);
	return _addField(resource,field,reader,store,index,token);
}

//adds a date field to current document
CLUCENEDLL_API int CL_ADD_DATE(const int resource, const TCHAR* field, const type_long time, const int store, const int index, const int token){
	resetError(resource);
	int ret = 0;
	if ( isValidResource(resource) ){
		if ( resources[resource]->doc == NULL ){
			CL_NEW_DOCUMENT(resource);
		}

		try{
			TCHAR* value = lucene::document::DateField::timeToString(time);

			lucene::document::Field* fld = _CLNEW lucene::document::Field(field,value,store!=0,index!=0,token!=0);
			resources[resource]->doc->add(*fld);

			_CLDELETE_ARRAY (value);
			ret = 1;
		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( _T("Unknown error"), resource);
		}
	}
	return ret;
}


//deletes documents returned from specified query.
//returns number of documents deleted.
//returns -1 if an error occurs
CLUCENEDLL_API int CL_DELETE(const int resource, const TCHAR* query,const TCHAR* field){
	int ret = -1;

	if ( isValidResource(resource) ){
		Query* q = NULL;
		IndexSearcher *s = NULL;
		Hits* h = NULL;

		try{
			q = QueryParser::parse(query,field,&analyzer);
			s = _CLNEW IndexSearcher(resources[resource]->reader);

			h = s->search(q);
			ret = 0;
			for ( int i=0;i<h->length();i++ ){
				resources[resource]->reader->deleteDoc(h->id(i));
				ret++;
			}
		}catch(CLuceneError& e){
			reportError( e.what(), resource);
			ret = -1;
		}catch(...){
			reportError( _T("Unknown error"), resource);
			ret = -1;
		}
		if ( s != NULL )
			s->close();

		_CLDELETE( s );
		_CLDELETE( h );
		_CLDELETE( q );
	}
	return ret;
}
#endif



//NON-CHAR SPECIFIC CODE//////////////////////////////
//
//

CLUCENEDLL_API bool CL_TERMDOCSNEXT(int resource){
	resetError(resource);
	if ( isValidResource(resource) ){
		if ( resources[resource]->termDocs == NULL ){
			reportError("No termdocs available", resource);
			return 0;
		}
		bool ret = resources[resource]->termDocs->next();
		if ( ret )
			resources[resource]->docId = resources[resource]->termDocs->doc();
		return ret;
	}
	return 0;
}
CLUCENEDLL_API void CL_CLEANUP(){
    for ( int i=1;i<MAX_CLUCENE_DLL_RESOURCES;i++ ){
        while(CL_CLOSE(i)>1){
        }
    }
}

//reopen the reader to take note of segment changes
CLUCENEDLL_API int CL_RELOAD(const int resource){
	resetError(resource);
	if ( isValidResource(resource) ){
        try{
			resources[resource]->reader->close();
			_CLDELETE( resources[resource]->reader );
			resources[resource]->reader = IndexReader::open(resources[resource]->directory);
    
        	return 1;
        }catch(CLuceneError& e){
        	reportError( e.what(), resource);
        }catch(...){
        	reportError( "Unknown error", resource);
        }
	}
	return 0;
}

CLUCENEDLL_API int CL_CLOSE(const int resource){
	resetError(resource);
	if ( isValidResource(resource) ){
    	resources[resource]->count--;
    	if ( resources[resource]->count < 1 ){
        	try{
        		resources[resource]->reader->close();
    
    		    CL_CLEARSEARCH(resource);
    		    delete resources[resource];
    		    resources[resource] = NULL;
     
        		return 1;
        	}catch(CLuceneError& e){
        		reportError( e.what(), resource);
        	}catch(...){
        		reportError( "Unknown error", resource);
        	}
        }else
            return resources[resource]->count +1;
	}
	return 0;
}


CLUCENEDLL_API void CL_CLEARSEARCH(const int resource){
	if ( isValidResource(resource) ){
		try{
			if ( resources[resource]->searcher != NULL ){
				resources[resource]->searcher->close();
				_CLDELETE(resources[resource]->searcher);
			}
		}catch(...){
		}

		try{
			if ( resources[resource]->query != NULL ){
				_CLDELETE(resources[resource]->query);
			}
		}catch(...){
		}

		try{
			if ( resources[resource]->termDocs != NULL )
				_CLDELETE(resources[resource]->termDocs);
		}catch(...){
		}

		try{
			if ( resources[resource]->sort != NULL )
				_CLDELETE(resources[resource]->sort);
		}catch(...){
		}

		resources[resource]->customsorters.clear();

		_CLDELETE (resources[resource]->hits);
		resources[resource]->docId = 0;
		resources[resource]->hitPos = 0;
	}
}
CLUCENEDLL_API int CL_HITCOUNT(const int resource){
	if ( isValidResource(resource) ){
		if ( resources[resource]->hits == NULL ){
			return 0;
		}else
			return resources[resource]->hits->length();
	}else
		return 0;
}

CLUCENEDLL_API int CL_NEXTHIT(const int resource){
	int ret = 0;
	if ( isValidResource(resource) ){
		if ( resources[resource]->hits == NULL || resources[resource]->searcher == NULL ){
			reportError ("No hits or searcher available", resource);
		}else{
			resources[resource]->hitPos++;
			if ( resources[resource]->hitPos < resources[resource]->hits->length() ){
				resources[resource]->docId = resources[resource]->hits->id(resources[resource]->hitPos);
				ret = 1;
			}
		}
	}
	return ret;
}

CLUCENEDLL_API int CL_GOTOHIT(const int resource, const int number){
	int ret = 0;
	if ( isValidResource(resource) ){
		if ( resources[resource]->hits == NULL || resources[resource]->searcher == NULL ){
			reportError ("No hits or searcher available", resource);
		}else{
		    if ( (number < 0) || (number >= resources[resource]->hits->length()) )
		        ret = 0;
			else{
				resources[resource]->hitPos = number;
		        resources[resource]->docId = resources[resource]->hits->id(number);
				ret = 1;
			}
		}
	}
	return ret;
}

CLUCENEDLL_API float CL_HITSCORE(const int resource){
    float ret = 0;
    if ( isValidResource(resource) ){
		if ( resources[resource]->hits == NULL || resources[resource]->searcher == NULL ){
			reportError ( _T("No hits or searcher available"), resource);
			return 0;
		}

		try{
			ret = resources[resource]->hits->score(resources[resource]->hitPos);
		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( _T("Unknown error"), resource);
		}
	}
	return ret;
}

#ifndef CLUCENE_LITE
//resets the current document
CLUCENEDLL_API int CL_NEW_DOCUMENT(const int resource){
	resetError(resource);
	if ( isValidResource(resource) ){
		try{
			_CLDELETE( resources[resource]->doc );
			resources[resource]->doc = _CLNEW Document();
			return 1;
		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( "Unknown error", resource);
		}
	}
	return 0;
}

CLUCENEDLL_API int CL_OPTIMIZE(const int resource){
	resetError(resource);
	if ( isValidResource(resource) ){
		try{
			//temporarily close the reader
			resources[resource]->reader->close();
			_CLDELETE( resources[resource]->reader );
			resources[resource]->reader = NULL;

			IndexWriter w(resources[resource]->directory,&analyzer,false);
			w.optimize();
			w.close();

			//reopen the reader.
			resources[resource]->reader = IndexReader::open(resources[resource]->directory);
			return 1;
		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( "Unknown error", resource);
		}
	}
	return 0;
}

CLUCENEDLL_API void CL_DELETECURRENTDOCUMENT(const int resource){
	resetError(resource);
	int ret = 0;
	if ( isValidResource(resource) ){
		try{
			resources[resource]->reader->deleteDoc( resources[resource]->docId );
		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( "Unknown error", resource);
		}
	}
}


//adds the current document
CLUCENEDLL_API int CL_INSERT_DOCUMENT(const int resource){
	resetError(resource);
	int ret = 0;
	if ( isValidResource(resource) ){
		if ( resources[resource]->doc == NULL ){
			reportError("No document available", resource);
			return 0;
		}

		IndexWriter* w = NULL;
		try{
			w = _CLNEW IndexWriter(resources[resource]->reader->getDirectory(),&analyzer,false);
			w->setMaxFieldLength(IndexWriter::DEFAULT_MAX_FIELD_LENGTH);
			w->addDocument(resources[resource]->doc);

			ret = 1;
		}catch(CLuceneError& e){
			reportError( e.what(), resource);
		}catch(...){
			reportError( "Unknown error", resource);
		}
		if ( w != NULL )
			w->close();
		_CLDELETE(w);

		//clear current document
		_CLDELETE( resources[resource]->doc );
		resources[resource]->doc = _CLNEW Document();
	}
	return ret;
}
#endif

//
//
//////////////////////////////////////////////////////

}//extern "C" 
