#include "CLucene/StdHeader.h"
#include "FSDirectory.h"
#include "CLucene/util/Misc.h"
#include "CLucene/util/MD5Digester.h"
#include "CLucene/debug/condition.h"

#include "CLucene/util/dirent.h" //if we have dirent, then the native one will be used

#if defined(LUCENE_FS_MMAP) //or some unix implementation...

CL_NS_DEF(store)
CL_NS_USE(util)

	FSDirectory::MMapInputStream::MMapInputStream(const char* path) {
	//Func - Constructor.
	//       Opens the file named path
	//Pre  - path != NULL
	//Post - if the file could not be opened  an exception is thrown.

	  CND_PRECONDITION(path != NULL, "path is NULL");

	  //Keep in mind that this instance is not a clone
	  isClone = false;

	  fhandle = CreateFileA(path,GENERIC_READ,FILE_SHARE_READ, 0,OPEN_EXISTING,0,0);
	  
	  //Check if a valid handle was retrieved
	  if (fhandle < 0){
		DWORD err = GetLastError();
        if ( err == ERROR_FILE_NOT_FOUND )
		    _CLTHROWA(CL_ERR_IO, "File does not exist");
        else if ( err == EACCES )
            _CLTHROWA(ERROR_ACCESS_DENIED, "File Access denied");
        else if ( err == ERROR_TOO_MANY_OPEN_FILES )
            _CLTHROWA(CL_ERR_IO, "Too many open files");
		else
			_CLTHROWA(CL_ERR_IO, "File IO Error");
	  }

	  DWORD dummy=0;
	  _length = GetFileSize(fhandle,&dummy);
	  data = NULL;
	  pos = 0;

	  if ( _length > 0 ){
		handle = CreateFileMappingA(fhandle,NULL,PAGE_READONLY,0,0,NULL);
		if ( handle != NULL ){
			data = (uint8_t*)MapViewOfFile(handle,FILE_MAP_READ,0,0,0);
			if ( data != NULL ){
				return; //SUCCESS!
			}else{
				CloseHandle(handle);
			}
		}
	  }
	  

	  LPVOID lpMsgBuf;
	  DWORD dw = GetLastError(); 

	  FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER | 
		FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		dw,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR) &lpMsgBuf,
		0, NULL );

      char* errstr = _CL_NEWARRAY(char, strlen(lpMsgBuf)+strlen(lpszFunction)+40); 
      sprintf(errstr, "%s failed with error %d: %s",  lpszFunction, dw, lpMsgBuf); 
      LocalFree(lpMsgBuf);
    
	  _CLTHROWA_DEL(CL_ERR_IO,errstr);
  }

  FSDirectory::MMapInputStream::MMapInputStream(const MMapInputStream& clone): IndexInput(clone){
  //Func - Constructor
  //       Uses clone for its initialization
  //Pre  - clone is a valide instance of FSInputStream
  //Post - The instance has been created and initialized by clone

	  data = clone.data;
	  pos = clone.pos;
	  handle = NULL;
	  fhandle = NULL;

	  //clone the file length
	  _length  = clone._length;
	  //Keep in mind that this instance is a clone
	  isClone = true;
  }

  uint8_t FSDirectory::MMapInputStream::readByte(){
	  return *(data+(pos++));
  }

  void FSDirectory::MMapInputStream::readBytes(uint8_t* b, const int32_t offset, const int32_t len){
	memcpy(b+offset, data+pos, len);
	pos+=len;
  }
  int64_t FSDirectory::MMapInputStream::getFilePointer(){
	return pos;
  }
  void FSDirectory::MMapInputStream::seek(const int64_t pos){
	  this->pos=pos;
  }

  FSDirectory::MMapInputStream::~MMapInputStream(){
  //Func - Destructor
  //Pre  - True
  //Post - The file for which this instance is responsible has been closed.
  //       The instance has been destroyed

	  close();
  }

  InputStream* FSDirectory::MMapInputStream::clone() const
  {
    return _CLNEW FSDirectory::MMapInputStream(*this);
  }
  void FSDirectory::MMapInputStream::close()  {
    InputStream::close();

	if ( !isClone ){
		if ( data != NULL ){
			if ( ! UnmapViewOfFile(data) ){
				CND_PRECONDITION( false, "UnmapViewOfFile(data) failed"); //todo: change to rich error
			}
		}

		if ( handle != NULL ){
			if ( ! CloseHandle(handle) ){
				CND_PRECONDITION( false, "CloseHandle(handle) failed");
			}
		}
		if ( fhandle != NULL ){
			if ( !CloseHandle(fhandle) ){
				CND_PRECONDITION( false, "CloseHandle(fhandle) failed");
			}
		}
	}
	handle = NULL;
	fhandle = NULL;
	data = NULL;
	pos = 0;
  }


CL_NS_END
#endif
