// ExtractCallbackSfx.h

#include "StdAfx.h"

#include "../../../Common/Wildcard.h"

#include "../../../Windows/FileDir.h"
#include "../../../Windows/FileFind.h"
#include "../../../Windows/FileName.h"
#include "../../../Windows/PropVariant.h"

#include "ExtractCallbackSfx.h"

using namespace NWindows;
using namespace NFile;
using namespace NDir;

static LPCWSTR kCantDeleteFile = L"Can not delete output file";
static LPCWSTR kCantOpenFile = L"Can not open output file";
static LPCWSTR kUnsupportedMethod = L"Unsupported Method";

void CExtractCallbackImp::Init(IInArchive *archiveHandler,
    const FString &directoryPath,
    const UString &itemDefaultName,
    const FILETIME &defaultMTime,
    UInt32 defaultAttributes)
{
  _message.Empty();
  _isCorrupt = false;
  _itemDefaultName = itemDefaultName;
  _defaultMTime = defaultMTime;
  _defaultAttributes = defaultAttributes;
  _archiveHandler = archiveHandler;
  _directoryPath = directoryPath;
  NName::NormalizeDirPathPrefix(_directoryPath);
}

HRESULT CExtractCallbackImp::Open_CheckBreak()
{
  #ifndef _NO_PROGRESS
  return ProgressDialog.Sync.ProcessStopAndPause();
  #else
  return S_OK;
  #endif
}

HRESULT CExtractCallbackImp::Open_SetTotal(const UInt64 * /* numFiles */, const UInt64 * /* numBytes */)
{
  return S_OK;
}

HRESULT CExtractCallbackImp::Open_SetCompleted(const UInt64 * /* numFiles */, const UInt64 * /* numBytes */)
{
  #ifndef _NO_PROGRESS
  return ProgressDialog.Sync.ProcessStopAndPause();
  #else
  return S_OK;
  #endif
}

HRESULT CExtractCallbackImp::Open_Finished()
{
  return S_OK;
}

STDMETHODIMP CExtractCallbackImp::SetTotal(UInt64 size)
{
  #ifndef _NO_PROGRESS
  ProgressDialog.Sync.SetProgress(size, 0);
  #endif
  return S_OK;
}

STDMETHODIMP CExtractCallbackImp::SetCompleted(const UInt64 *completeValue)
{
  #ifndef _NO_PROGRESS
  RINOK(ProgressDialog.Sync.ProcessStopAndPause());
  if (completeValue != NULL)
    ProgressDialog.Sync.SetPos(*completeValue);
  #endif
  return S_OK;
}

void CExtractCallbackImp::CreateComplexDirectory(const UStringVector &dirPathParts)
{
  FString fullPath = _directoryPath;
  FOR_VECTOR (i, dirPathParts)
  {
    fullPath += us2fs(dirPathParts[i]);
    CreateDir(fullPath);
    fullPath.Add_PathSepar();
  }
}

STDMETHODIMP CExtractCallbackImp::GetStream(UInt32 index,
    ISequentialOutStream **outStream, Int32 askExtractMode)
{
  #ifndef _NO_PROGRESS
  if (ProgressDialog.Sync.GetStopped())
    return E_ABORT;
  #endif
  _outFileStream.Release();

  UString fullPath;
  {
    NCOM::CPropVariant prop;
    RINOK(_archiveHandler->GetProperty(index, kpidPath, &prop));
    if (prop.vt == VT_EMPTY)
      fullPath = _itemDefaultName;
    else
    {
      if (prop.vt != VT_BSTR)
        return E_FAIL;
      fullPath.SetFromBstr(prop.bstrVal);
    }
    _filePath = fullPath;
  }

  if (askExtractMode == NArchive::NExtract::NAskMode::kExtract)
  {
    NCOM::CPropVariant prop;
    RINOK(_archiveHandler->GetProperty(index, kpidAttrib, &prop));
    if (prop.vt == VT_EMPTY)
      _processedFileInfo.Attributes = _defaultAttributes;
    else
    {
      if (prop.vt != VT_UI4)
        return E_FAIL;
      _processedFileInfo.Attributes = prop.ulVal;
    }

    RINOK(_archiveHandler->GetProperty(index, kpidIsDir, &prop));
    _processedFileInfo.IsDir = VARIANT_BOOLToBool(prop.boolVal);

    bool isAnti = false;
    {
      NCOM::CPropVariant propTemp;
      RINOK(_archiveHandler->GetProperty(index, kpidIsAnti, &propTemp));
      if (propTemp.vt == VT_BOOL)
        isAnti = VARIANT_BOOLToBool(propTemp.boolVal);
    }

    RINOK(_archiveHandler->GetProperty(index, kpidMTime, &prop));
    switch (prop.vt)
    {
      case VT_EMPTY: _processedFileInfo.MTime = _defaultMTime; break;
      case VT_FILETIME: _processedFileInfo.MTime = prop.filetime; break;
      default: return E_FAIL;
    }

    UStringVector pathParts;
    SplitPathToParts(fullPath, pathParts);
    if (pathParts.IsEmpty())
      return E_FAIL;

    UString processedPath = fullPath;

    if (!_processedFileInfo.IsDir)
      pathParts.DeleteBack();
    if (!pathParts.IsEmpty())
    {
      if (!isAnti)
        CreateComplexDirectory(pathParts);
    }

    FString fullProcessedPath = _directoryPath + us2fs(processedPath);

    if (_processedFileInfo.IsDir)
    {
      _diskFilePath = fullProcessedPath;

      if (isAnti)
        RemoveDir(_diskFilePath);
      else
        SetDirTime(_diskFilePath, NULL, NULL, &_processedFileInfo.MTime);
      return S_OK;
    }

    NFind::CFileInfo fileInfo;
    if (fileInfo.Find(fullProcessedPath))
    {
      if (!DeleteFileAlways(fullProcessedPath))
      {
        _message = kCantDeleteFile;
        return E_FAIL;
      }
    }

    if (!isAnti)
    {
      _outFileStreamSpec = new COutFileStream;
      CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
      if (!_outFileStreamSpec->Create(fullProcessedPath, true))
      {
        _message = kCantOpenFile;
        return E_FAIL;
      }
      _outFileStream = outStreamLoc;
      *outStream = outStreamLoc.Detach();
    }
    _diskFilePath = fullProcessedPath;
  }
  else
  {
    *outStream = NULL;
  }
  return S_OK;
}

STDMETHODIMP CExtractCallbackImp::PrepareOperation(Int32 askExtractMode)
{
  _extractMode = (askExtractMode == NArchive::NExtract::NAskMode::kExtract);
  return S_OK;
}

STDMETHODIMP CExtractCallbackImp::SetOperationResult(Int32 resultEOperationResult)
{
  switch (resultEOperationResult)
  {
    case NArchive::NExtract::NOperationResult::kOK:
      break;

    default:
    {
      _outFileStream.Release();
      switch (resultEOperationResult)
      {
        case NArchive::NExtract::NOperationResult::kUnsupportedMethod:
          _message = kUnsupportedMethod;
          break;
        default:
          _isCorrupt = true;
      }
      return E_FAIL;
    }
  }
  if (_outFileStream != NULL)
  {
    _outFileStreamSpec->SetMTime(&_processedFileInfo.MTime);
    RINOK(_outFileStreamSpec->Close());
  }
  _outFileStream.Release();
  if (_extractMode)
    SetFileAttrib(_diskFilePath, _processedFileInfo.Attributes);
  return S_OK;
}
