typedef const char *     LPCSTR;

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include "7zip.h"

C7zip::C7zip (LPCSTR FileName) :
	m_FileSize(0),
	m_CurrentFile(-1),
	m_blockIndex(0xFFFFFFFF),
	m_outBuffer(0),
	m_outBufferSize(0),
	m_NotfyCallback(NotfyCallbackDefault),
	m_NotfyCallbackInfo(NULL),
	m_db(NULL),
	m_Opened(false)
{
	memset(&m_FileName,0,sizeof(m_FileName));
	memset(&m_archiveLookStream,0,sizeof(m_archiveLookStream));

	m_db = new CSzArEx;
	memset(m_db,0,sizeof(CSzArEx));

	m_archiveStream.s.Read = SzFileReadImp;
	m_archiveStream.s.Seek = SzFileSeekImp;

	m_allocImp.Alloc = AllocAllocImp;
	m_allocImp.Free = AllocFreeImp;

	m_allocTempImp.Alloc = AllocAllocImp;
	m_allocTempImp.Free = AllocFreeImp;

	InFile_Open(&m_archiveStream.file,FileName);
	if (m_archiveStream.file.handle == INVALID_HANDLE_VALUE)
	{
	//PrintError("can not open input file");
		return;
	}
	m_FileSize = GetFileSize(m_archiveStream.file.handle,NULL); 

	char drive[_MAX_DRIVE] ,dir[_MAX_DIR], ext[_MAX_EXT];
	_splitpath( FileName, drive, dir, m_FileName, ext );

	CrcGenerateTable();
	SzArEx_Init(m_db);

	LookToRead_Init(&m_archiveLookStream);
	LookToRead_CreateVTable(&m_archiveLookStream, False);
	m_archiveLookStream.realStream = &m_archiveStream.s;

	SRes res = SzArEx_Open(m_db, &m_archiveLookStream.s,  &m_allocImp, &m_allocTempImp);
	if (res == SZ_OK)
	{
		m_Opened = true;
	}
	else
	{
		//SzArEx_Open will delete the passed db if it fails
		m_db = NULL;
	}
}

C7zip::~C7zip (void)
{
	if (m_db)
	{
		delete m_db;
		m_db = NULL;
	}
#ifdef tofix
	SetNotificationCallback(NULL,NULL);
	SzArDbExFree(&m_db, m_allocImp.Free);

	if (m_archiveStream.File)
	{
		fclose(m_archiveStream.File);
	}
	if (m_outBuffer)
	{
		m_allocImp.Free(m_outBuffer);
	}
#endif
}

void C7zip::SetNotificationCallback (LP7ZNOTIFICATION NotfyFnc, void * CBInfo)
{
	m_NotfyCallback     = NotfyFnc ? NotfyFnc : NotfyCallbackDefault;
	m_NotfyCallbackInfo = CBInfo;
}

#ifdef tofix
void C7zip::StatusUpdate(_7Z_STATUS status, int Value1, int Value2, C7zip * _this )
{
	CFileItem * File = _this->m_CurrentFile >= 0 ? _this->FileItem(_this->m_CurrentFile) : NULL;
	
	switch (status)
	{
	case LZMADECODE_START: 	_this->m_NotfyCallback("Start decoding",_this->m_NotfyCallbackInfo); break;
	case LZMADECODE_UPDATE: 
		{
			char Msg[200];
			sprintf(Msg,"decoding %s: %0.2f%%",File->Name, (Value1/(float)Value2) * 100);
			_this->m_NotfyCallback(Msg,_this->m_NotfyCallbackInfo); 
		}
		break;
	case LZMADECODE_DONE:  _this->m_NotfyCallback("Finished decoding",_this->m_NotfyCallbackInfo); break;
	}
}
#endif

bool C7zip::GetFile(int index, Byte * Data, size_t DataLen )
{
	m_CurrentFile = -1;
	if (Data == NULL || DataLen == 0)
	{
		return false;
	}
	if (m_archiveStream.file.handle == INVALID_HANDLE_VALUE)
	{
		return false;
	}
	m_CurrentFile = index;

    size_t offset;
    size_t outSizeProcessed;
	
	wchar_t Msg[200];
	std::wstring FileName = FileNameIndex(index);
	_snwprintf(Msg, sizeof(Msg) / sizeof(Msg[0]), L"extracting %s", FileName.c_str());
	m_NotfyCallback(Msg,m_NotfyCallbackInfo);

	SRes res = SzArEx_Extract(m_db, &m_archiveLookStream.s, index, 
            &m_blockIndex, &m_outBuffer, &m_outBufferSize, 
            &offset, &outSizeProcessed, 
            &m_allocImp, &m_allocTempImp);
    if (res != SZ_OK)
	{
		m_CurrentFile = -1;
		return false;
	}

	if (DataLen < outSizeProcessed)
	{
		outSizeProcessed = DataLen;
	}
	memcpy(Data,m_outBuffer + offset,outSizeProcessed);
	m_NotfyCallback(L"",m_NotfyCallbackInfo);
	m_CurrentFile = -1;
	return true;
}

void * C7zip::AllocAllocImp (void * /*p*/, size_t size)
{
	return malloc(size);
	//return new BYTE[size];
}

void C7zip::AllocFreeImp (void * /*p*/, void *address)
{
	if (address != NULL)
	{
		free(address);
	}
}

SRes C7zip::SzFileReadImp(void *object, void *buffer, size_t *processedSize)
{
	CFileInStream *p = (CFileInStream *)object;
	DWORD dwRead;
	if (!ReadFile(p->file.handle,buffer,*processedSize,&dwRead,NULL))
	{
		return SZ_ERROR_FAIL;
	}
	//p->s.curpos += read_sz;
	*processedSize = dwRead;
	return SZ_OK;
}

SRes C7zip::SzFileSeekImp(void *p, Int64 *pos, ESzSeek origin)
{
	CFileInStream *s = (CFileInStream *)p;
	DWORD dwMoveMethod;

	switch (origin)
	{
	case SZ_SEEK_SET: 
		dwMoveMethod = FILE_BEGIN;
		break;
	case SZ_SEEK_CUR:
		dwMoveMethod = FILE_CURRENT;
		break;
	case SZ_SEEK_END:
		dwMoveMethod = FILE_END;
		break;
	default:
		return SZ_ERROR_FAIL;

	}
	*pos =  SetFilePointer(s->file.handle,(LONG)*pos, NULL, dwMoveMethod);
	if (*pos == INVALID_SET_FILE_POINTER)
	{
		return SZ_ERROR_FAIL;
	}
	return SZ_OK;
}

const char * C7zip::FileName ( char * FileName, int SizeOfFileName ) const
{
	if (FileName == NULL) 
	{
		return NULL;
	}
	int Len = strlen(m_FileName);
	if (Len > (SizeOfFileName - 1))
	{
		Len = (SizeOfFileName - 1);
	}
	strncpy(FileName,m_FileName,Len);
	FileName[Len] = 0;
	return FileName;
}

std::wstring C7zip::FileNameIndex (int index)
{
	std::wstring filename;
    if (m_db == NULL || m_db->FileNameOffsets == 0)
	{
		/* no filename */
		return filename;
	}
	int namelen = SzArEx_GetFileNameUtf16(m_db, index, NULL);
	if (namelen <= 0)
	{
		/* no filename */
		return filename;
	}
	filename.resize(namelen);
	SzArEx_GetFileNameUtf16(m_db, index, (UInt16 *)filename.c_str());
	return filename;
}