#include "CLucene/StdHeader.h"
#include "lucenebase.h"

#include "CLucene/util/StringBuffer.h"
#include "CLucene/util/Misc.h"
#include "CLucene/util/VoidList.h"
CL_NS_USE(util)

CL_NS_DEF(debug)


#ifndef LUCENE_ENABLE_LUCENEBASE
//todo: hack - on msvc, the native new/delete's crashes
//the app. shouldn't need to do this because
//we might want to use native new/deletes
void* LuceneBase::operator new (size_t size)
{
   return malloc (size);
} 
void LuceneBase::operator delete (void *p)
{
   free(p);
}
#else

///\exclude
class lucenebaseCompare:public std::binary_function<const LuceneBase*,const LuceneBase*,bool>
{
public:
	bool operator()( const LuceneBase* val1, const LuceneBase* val2 ) const{
		bool ret = val1->__cl_initnum<val2->__cl_initnum;
		return ret;
	}
};

int_t LuceneBase_num = 0; //counter for initnumber
struct basefiles{
   int_t refcount; ///times this has been used
   char* value; ///reference to the the basefile
}; //structure for name counting
struct basepointers{
   basefiles* file;
   int_t initline;
   int_t initnumber;
};//structure for pointer-filename references
VoidMap<const char,basefiles*,Compare::Char> LuceneBase_Files; //list of filenames used
VoidMap<LuceneBase,basepointers*,voidCompare> LuceneBase_List; //list of pointers counted

//variables to trim filenames to just the base names
char LuceneBase_FTStart[MAX_PATH];
int_t LuceneBase_FIStart=-1;

int_t LuceneBase_break = 316; //to break at this item, change this 
							 //and put break points at points below

//trim the filename and return the refcounted basefiles* structure
basefiles* getLuceneBase_Files(const char* file){
	if ( LuceneBase_FIStart == -1 ){
		//this trims the start of the name file name so
		//that the whole of the filename is not stored - more asthetic :)
		//need to find the base
		LuceneBase_FIStart = strlen(__FILE__) - 20; //(length of debug/lucenebase.cpp)
		strcpy(LuceneBase_FTStart,__FILE__);
		LuceneBase_FTStart[LuceneBase_FIStart] = 0;
	}
	if ( strncmp(file,LuceneBase_FTStart,LuceneBase_FIStart) == 0 ){
		//this file should be within the same directory area as we found lucenebase.cpp
		//to be, lets trim the start
		file+=LuceneBase_FIStart;
	}

   //now return an existing files structure (with refcount++) or create a new one
   CL_NS(util)::VoidMap<const char,basefiles*,Compare::Char>::iterator itr = LuceneBase_Files.getIterator((const char*)file);
   if ( itr != NULL ){
      basefiles* bf = itr->second;
      bf->refcount++;
      return bf;
   }else{
      basefiles* ref = new basefiles;
      ref->value = lucenestrdup(file);
      ref->refcount = 1;
	   LuceneBase_Files.put(ref->value,ref);
	   return ref;
   }
}
void* LuceneBase::operator new (size_t size, const char * file, int_t line)
{
 void* p = malloc (size);
 //if ( p != 0 ){
	LuceneBase* lb = (LuceneBase*)p;
   basefiles* br = getLuceneBase_Files(file);
   basepointers* bp = new basepointers;
   bp->file = br;
   bp->initnumber = LuceneBase_num++;
   bp->initline = line;

   lb->__cl_initnum = bp->initnumber;

	if ( LuceneBase_break == lb->__cl_initnum )
		lb->__cl_initnum = lb->__cl_initnum; //put break point here

#ifdef LUCENE_ENABLE_REFCOUNT
	lb->__cl_refcount=0;
#endif
	LuceneBase_List.put(lb, bp);
 //}
 return p;
} 
void LuceneBase::operator delete (void *p, char const * file, int_t line)
{
	LuceneBase* lb=(LuceneBase*)p;
   CL_NS(util)::VoidMap<LuceneBase,basepointers*,voidCompare>::iterator itr =
      LuceneBase_List.getIterator(lb);
   if ( itr != NULL ){
      basepointers* bp = itr->second;
      basefiles* bf = bp->file;
      bf->refcount--;
      if ( bf->refcount <= 0 ){
         LuceneBase_Files.setDoDelete(true,DELETE_TYPE_DELETE_ARRAY);
         LuceneBase_Files.setDoDelete(false,DELETE_TYPE_DELETE);
         LuceneBase_Files.remove (bf->value);
      }
      LuceneBase_List.setDoDelete(false,DELETE_TYPE_DELETE);
      LuceneBase_List.remove(lb,true,false);
   }else{
      //break
   }
	free(p); 
}
void* LuceneBase::operator new (size_t size)
{
   void* p = malloc (size);
   //if ( p != 0 ){

	LuceneBase* lb = (LuceneBase*)p;
   basefiles* br = getLuceneBase_Files("undefined");
   basepointers* bp = new basepointers;
   bp->file = br;
   bp->initnumber = LuceneBase_num++;
   bp->initline = -1;

   lb->__cl_initnum = bp->initnumber;

	if ( LuceneBase_break == lb->__cl_initnum )
		lb->__cl_initnum = lb->__cl_initnum; //put break point here

   #ifdef LUCENE_ENABLE_REFCOUNT
	   lb->__cl_refcount=0;
   #endif
	LuceneBase_List.put(lb,bp);
//}
return p;
} 

void LuceneBase::operator delete (void *p)
{
	LuceneBase* lb=(LuceneBase*)p;
   CL_NS(util)::VoidMap<LuceneBase,basepointers*,voidCompare>::iterator itr =
      LuceneBase_List.getIterator(lb);
   if ( itr != NULL ){
      basepointers* bp = itr->second;
      basefiles* bf = bp->file;
      bf->refcount--;
      if ( bf->refcount <= 0 ){
         LuceneBase_Files.setDoDelete(true,DELETE_TYPE_DELETE_ARRAY);
         LuceneBase_Files.setDoDelete(false,DELETE_TYPE_DELETE);
         LuceneBase_Files.remove (bf->value);
      }
      LuceneBase_List.setDoDelete(false,DELETE_TYPE_DELETE);
      LuceneBase_List.remove(lb,true,false);
   }else{
      //break
   }
	free(p); 
}

//static
TCHAR* __internalcl_GetUnclosedObjects(bool print){
	StringBuffer sb;
   bool unknowns = false;
	if ( LuceneBase_List.size() > 0 ){
		sb.appendInt((int_t)LuceneBase_List.size());
		sb.append(_T(" clucene objects are still open\n"));

      VoidMap<LuceneBase,basepointers*,voidCompare>::iterator itr = LuceneBase_List.begin();
      while ( itr != LuceneBase_List.end() ){
		//for ( uint_t i=0;i<LuceneBase_List.size();i++ ){
         LuceneBase* lb = itr->first;
         basepointers* bp = itr->second;
         basefiles* bf = bp->file;

			if ( bp->initline == -1 )
				unknowns = true;
			sb.append(_T("   ") );

			sb.appendInt(lb->__cl_initnum);
			sb.append(_T(". "));

			TCHAR tmp[MAX_PATH];
			/*if ( lb->__cl_initfile < 0 || lb->__cl_initfile >= LuceneBase_Files.size() )
				_tcscpy(tmp,_T("unknown (filename lost), use number to identify object instead"));
			else
			   STRCPY_AtoT(tmp,LuceneBase_Files.at(lb->__cl_initfile),MAX_PATH);*/
         STRCPY_AtoT(tmp,bf->value,MAX_PATH);
			sb.append(tmp);
			
			sb.append(_T(", line ") );
			sb.appendInt(bp->initline);
			sb.append(_T("\n"));

			if ( print && sb.length() > 0 ){
				_tprintf(sb.getBuffer());
				sb.clear();
			}

         itr++;
   	}
	}

	if ( unknowns == true ){
		sb.append(_T("*** Some memory was not created with _CLNEW and was not tracked... ***\n") );
		sb.append(_T("*** Use _CLNEW instead of new when creating CLucene objects ***\n") );
		sb.append(_T("*** Memory may also have not been freed in the current context ***\n") );
	}
	
	if ( print ){
		if ( sb.length() > 0 ){
			_tprintf(sb.getBuffer());
			sb.clear();
		}
		return NULL;
	}else{
		if ( sb.length() > 0 )
			return stringDuplicate(sb.getBuffer());
		else
			return NULL;
	}
}
///This will clear memory relating to refcounting
///other tools can be used to more accurately identify
///memory leaks. This should only be called just
///before closing, and after retrieving the
///unclosed object list
void LuceneBase::__cl_ClearMemory(){
   LuceneBase_Files.clear();
   LuceneBase_List.setDoDelete(true,DELETE_TYPE_NONE);
   LuceneBase_List.setDoDelete(false,DELETE_TYPE_DELETE);
   LuceneBase_List.clear();
}
TCHAR* LuceneBase::__cl_GetUnclosedObjects(){
	return __internalcl_GetUnclosedObjects(false);
}
//static
int_t LuceneBase::__cl_GetUnclosedObjectsCount(){
    return LuceneBase_List.size();
}

const char* LuceneBase::__cl_GetUnclosedObject(int_t item){
   VoidMap<LuceneBase,basepointers*,voidCompare>::iterator itr=LuceneBase_List.begin();
   int_t i=0;
   for ( ;itr!=LuceneBase_List.end() && i<item ;itr++ ){
      i++;
   }
   if ( itr != LuceneBase_List.end() )
      return itr->second->file->value;
   else
      return NULL;
}
void LuceneBase::__cl_PrintUnclosedObjects(){
	__internalcl_GetUnclosedObjects(true);
}

#endif //LUCENE_ENABLE_LUCENEBASE

#ifdef LUCENE_ENABLE_REFCOUNT
#ifdef _DEBUG
	bool lucene_refCount_CanDelete(const LuceneBase* d, const char* file, const int_t line){
#else
	bool lucene_refCount_CanDelete(const LuceneBase* d){
#endif
	if ( d != NULL ){
	      CND_PRECONDITION(d->__cl_refcount>=0,"__cl_refcount < 0");
			if (d!= NULL && d->__cl_refcount == 0 )
				return true;
		}

		return false;
	}
#ifdef _DEBUG
	bool lucene_refCount_CanDelete(const void* d, const char* file, const int_t line){
#else
	bool lucene_refCount_CanDelete(const void* d){
#endif
		return true;
	}
#endif

CL_NS_END
