////////////////////////////////////////////////////////
// File Name: FileExtractor.cpp
//
//  Abstract: Extracts compressed files.
//
////////////////////////////////////////////////////////

#include "stdafx.h"
#include "LoadCodecs.h"
#include "OpenArchive.h"
#include "ArchiveOpenCallback.h"
#include "FileHelperFunctions.h"
#include "FileExtractor.h"
#include "ArchiveExtractCallback.h"

using namespace NWindows::NDLL;

CCodecs *gpCodecs(nullptr);
CMyComPtr<ICompressCodecsInfo> gspCompressCodecsInfo;
extern HMODULE gModule;


bool gbCodecsLoaded(false);

typedef shared_ptr<C7zipCompressedFileCollection::SCompressedFileHandle> CompressedFileHandleSharedPtr;

//Struct declared in the .cpp to hide it from clients of the class.
struct C7zipCompressedFileCollection::SCompressedFileHandle
{
public:
	SCompressedFileHandle();
	~SCompressedFileHandle();

	shared_ptr<CArchiveLink> m_spArchiveLink;
};

/////////////////////////////////////////////////////////////////////////////
// Function       : SCompressedFileHandle::SCompressedFileHandle
// Description    : Constuctor
/////////////////////////////////////////////////////////////////////////////
C7zipCompressedFileCollection::SCompressedFileHandle::SCompressedFileHandle()
{
	m_spArchiveLink.reset(new CArchiveLink());
}

/////////////////////////////////////////////////////////////////////////////
// Function       : SCompressedFileHandle::~SCompressedFileHandle
// Description    : Destructor
/////////////////////////////////////////////////////////////////////////////
C7zipCompressedFileCollection::SCompressedFileHandle::~SCompressedFileHandle()
{
	m_spArchiveLink.reset();
}

/////////////////////////////////////////////////////////////////////////////
// Function       : ReadCompressedFile
// Description    : Reads a compressed file and saves it into compressed file objects.
// Return type    : bool - A boolean value indicating success or failure.
// Argument [In]  : LPCWSTR File - The compressed file.
// Argument [Out] : CompressedFilesSharedPtr& spCompressedFiles - The compressed files.
/////////////////////////////////////////////////////////////////////////////
bool C7zipCompressedFileCollection::ReadCompressedFile(LPCWSTR pFile, CompressedFileCollectionSharedPtr& spCompressedFiles)
{
	bool RetVal = false;
	UString PathToThisDLL;
	CATLString PathTo7zDLL;

	if(!gbCodecsLoaded)
	{
		if(MyGetModuleFileName(gModule, PathToThisDLL))
		{
			PathTo7zDLL = GetDirectoryName((LPCTSTR)PathToThisDLL);
			PathTo7zDLL = CombinePaths(PathTo7zDLL, DLLPath);

			gpCodecs = new CCodecs((LPCTSTR)PathTo7zDLL);
			gspCompressCodecsInfo = gpCodecs;	//Reference to be released when this DLL is released.
			if(SUCCEEDED(gpCodecs->Load()))
			{
				gbCodecsLoaded = true;
			}
		}
	}

	if(gbCodecsLoaded)
	{
		CComPtr<SevenZipExtractor::ArchiveOpenCallback> spOpenCallback(new SevenZipExtractor::ArchiveOpenCallback());
		shared_ptr<C7zipCompressedFileCollection::SCompressedFileHandle> spNewFileHandle(new C7zipCompressedFileCollection::SCompressedFileHandle());
		if(SUCCEEDED(spNewFileHandle->m_spArchiveLink->Open2(gpCodecs, CIntVector(), false, nullptr, pFile, spOpenCallback)))
		{
			if(spNewFileHandle->m_spArchiveLink->Arcs.Size() > 0)
			{
				spCompressedFiles = C7zipCompressedFileCollection::CreateInstance(spNewFileHandle);
				RetVal = true;
			}
		}
	}

	return RetVal;
}

/////////////////////////////////////////////////////////////////////////////
// Function       : C7zipCompressedFileCollection::CreateInstance
// Description    : Creates a C7zipCompressedFileCollection instance and returns it as a CompressedFilesSharedPtr.
// Return type    : CompressedFilesSharedPtr - The created instance.
// Argument [In]  : CompressedFileHandleSharedPtr spFileHandle - A filehandle to an opened file.
/////////////////////////////////////////////////////////////////////////////
CompressedFileCollectionSharedPtr C7zipCompressedFileCollection::CreateInstance(CompressedFileHandleSharedPtr spFileHandle)
{
	CompressedFileCollectionSharedPtr spRetVal(new C7zipCompressedFileCollection());

	CArc &arc = spFileHandle->m_spArchiveLink->Arcs.Back();

	CComPtr<IInArchive> spArchive = arc.Archive;
	UInt32 NumFiles = 0;
	bool IsFolder = false;

	if(SUCCEEDED(spArchive->GetNumberOfItems(&NumFiles)))
	{
		for(UInt32 Index = 0; Index < NumFiles; Index++)
		{
			if(SUCCEEDED(IsArchiveItemFolder(spArchive, Index, IsFolder)) && !IsFolder)
			{
				spRetVal->m_CompressedFiles.push_back(C7zipCompressedFile::CreateInstance(spFileHandle, Index));
			}
		}
	}

	return spRetVal;
}

/////////////////////////////////////////////////////////////////////////////
// Function       : C7zipCompressedFile::CreateInstance
// Description    : Creates a C7zipCompressedFile instance and returns it as a CompressedFileSharedPtr.
// Return type    : CompressedFileSharedPtr - The created instance.
// Argument [In]  : CompressedFileHandleSharedPtr spFileHandle - A filehandle to an opened file.
// Argument [In]  : int FileIndex - The index in the compressed file.
/////////////////////////////////////////////////////////////////////////////
CompressedFileSharedPtr C7zipCompressedFile::CreateInstance(CompressedFileHandleSharedPtr spFileHandle, int FileIndex)
{
	CompressedFileSharedPtr spRetVal(new C7zipCompressedFile());

	spRetVal->m_FileIndex = FileIndex;
	spRetVal->m_spFileHandle = spFileHandle;

	return spRetVal;
}

/////////////////////////////////////////////////////////////////////////////
// Function       : C7zipCompressedFile::GetFileName
// Description    : Gets the filename.
// Return type    : CString - The returned file.
// Argument [In]  : void
/////////////////////////////////////////////////////////////////////////////
CString C7zipCompressedFile::GetFileName()
{
	CATLString RetVal;
	UString TempPath;

	CArc &arc = m_spFileHandle->m_spArchiveLink->Arcs.Back();

	if(SUCCEEDED(arc.GetItemPath(m_FileIndex, TempPath)))
	{
		RetVal = (LPCTSTR)TempPath;
	}

	return RetVal;
}

/////////////////////////////////////////////////////////////////////////////
// Function       : C7zipCompressedFile::GetFileSize
// Description    : Gets the file size.
// Return type    : unsigned __int64 - The filesize.
// Argument [In]  : void
/////////////////////////////////////////////////////////////////////////////
unsigned __int64 C7zipCompressedFile::GetFileSize()
{
	unsigned __int64 RetVal = 0;
	UInt64 unpackSize = 0;

	CArc &arc = m_spFileHandle->m_spArchiveLink->Arcs.Back();

	if(SUCCEEDED(arc.GetUnpackedSize(m_FileIndex, unpackSize)))
	{
		RetVal = unpackSize;
	}

	return RetVal;
}

/////////////////////////////////////////////////////////////////////////////
// Function       : C7zipCompressedFile::Extract
// Description    : Extracts the compress data to the specified file. File can contain a directory, it will be created.
//					File will be overwritten if it already exists.
// Return type    : bool - A boolean value indicating success or failure.
// Argument [In]  : LPCTSTR OutputFileName - The filename to output the compressed data as.
/////////////////////////////////////////////////////////////////////////////
bool C7zipCompressedFile::Extract(LPCTSTR OutputFileName)
{
	bool RetVal = false;
	CArc &arc = m_spFileHandle->m_spArchiveLink->Arcs.Back();
	CComPtr<IInArchive> spArchive = arc.Archive;
	UInt32 Index = m_FileIndex;

	CreateDirectoryFromFilePath(OutputFileName);

    CComPtr<SevenZipExtractor::CArchiveExtractCallback> spExtractCallback(
        new SevenZipExtractor::CArchiveExtractCallback(&arc, OutputFileName));

    if (SUCCEEDED(spArchive->Extract(&Index, 1, false, spExtractCallback)))
    {
        RetVal = true;
    }

	return RetVal;
}
