// ArchiveExtractCallback.h

#ifndef __ARCHIVE_EXTRACT_CALLBACK_H
#define __ARCHIVE_EXTRACT_CALLBACK_H

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

#include "../../IPassword.h"

#include "../../Common/FileStreams.h"
#include "../../Common/ProgressUtils.h"

#include "../../Archive/IArchive.h"

#include "ExtractMode.h"
#include "IFileExtractCallback.h"
#include "OpenArchive.h"

#include "HashCalc.h"

#ifndef _SFX

class COutStreamWithHash:
  public ISequentialOutStream,
  public CMyUnknownImp
{
  CMyComPtr<ISequentialOutStream> _stream;
  UInt64 _size;
  bool _calculate;
public:
  IHashCalc *_hash;

  MY_UNKNOWN_IMP
  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
  void SetStream(ISequentialOutStream *stream) { _stream = stream; }
  void ReleaseStream() { _stream.Release(); }
  void Init(bool calculate = true)
  {
    InitCRC();
    _size = 0;
    _calculate = calculate;
  }
  void EnableCalc(bool calculate) { _calculate = calculate; }
  void InitCRC() { _hash->InitForNewFile(); }
  UInt64 GetSize() const { return _size; }
};

#endif

struct CExtractNtOptions
{
  CBoolPair NtSecurity;
  CBoolPair SymLinks;
  CBoolPair HardLinks;
  CBoolPair AltStreams;
  bool ReplaceColonForAltStream;
  bool WriteToAltStreamIfColon;

  CExtractNtOptions():
      ReplaceColonForAltStream(false),
      WriteToAltStreamIfColon(false)
  {
    SymLinks.Val = true;
    HardLinks.Val = true;
    AltStreams.Val = true;
  }
};

#ifndef _SFX

class CGetProp:
  public IGetProp,
  public CMyUnknownImp
{
public:
  const CArc *Arc;
  UInt32 IndexInArc;
  UString Name; // relative path

  MY_UNKNOWN_IMP1(IGetProp)
  INTERFACE_IGetProp(;)
};

#endif

#ifndef _SFX
#ifndef UNDER_CE

// FIXME #define SUPPORT_LINKS

#endif
#endif


#ifdef SUPPORT_LINKS

struct CHardLinkNode
{
  UInt64 StreamId;
  UInt64 INode;

  int Compare(const CHardLinkNode &a) const;
};

class CHardLinks
{
public:
  CRecordVector<CHardLinkNode> IDs;
  CObjectVector<FString> Links;

  void Clear()
  {
    IDs.Clear();
    Links.Clear();
  }

  void PrepareLinks()
  {
    while (Links.Size() < IDs.Size())
      Links.AddNew();
  }
};

#endif

class CArchiveExtractCallback:
  public IArchiveExtractCallback,
  // public IArchiveVolumeExtractCallback,
  public ICryptoGetTextPassword,
  public ICompressProgressInfo,
  public CMyUnknownImp
{
  const CArc *_arc;
  CExtractNtOptions _ntOptions;

  const NWildcard::CCensorNode *_wildcardCensor;
  CMyComPtr<IFolderArchiveExtractCallback> _extractCallback2;
  CMyComPtr<ICompressProgressInfo> _compressProgress;
  CMyComPtr<ICryptoGetTextPassword> _cryptoGetTextPassword;
  FString _directoryPath;
  FString _directoryPathFull;
  NExtract::NPathMode::EEnum _pathMode;
  NExtract::NOverwriteMode::EEnum _overwriteMode;

  #ifndef _SFX

  CMyComPtr<IFolderExtractToStreamCallback> ExtractToStreamCallback;
  CGetProp *GetProp_Spec;
  CMyComPtr<IGetProp> GetProp;

  #endif

  FString _diskFilePath;
  UString _filePath;
  UInt64 _position;
  bool _isSplit;
  bool _isAltStream;

  bool _extractMode;

  bool WriteCTime;
  bool WriteATime;
  bool WriteMTime;

  bool _encrypted;

  struct CProcessedFileInfo
  {
    FILETIME CTime;
    FILETIME ATime;
    FILETIME MTime;
    UInt32 Attrib;

    bool CTimeDefined;
    bool ATimeDefined;
    bool MTimeDefined;
    bool AttribDefined;

    bool IsDir;
  } _fi;

  UInt32 _index;
  UInt64 _curSize;
  bool _curSizeDefined;
  COutFileStream *_outFileStreamSpec;
  CMyComPtr<ISequentialOutStream> _outFileStream;

  #ifndef _SFX

  COutStreamWithHash *_hashStreamSpec;
  CMyComPtr<ISequentialOutStream> _hashStream;
  bool _hashStreamWasUsed;

  #endif

  UStringVector _removePathParts;
  bool _use_baseParentFolder_mode;
  UInt32 _baseParentFolder;

  bool _stdOutMode;
  bool _testMode;
  bool _multiArchives;

  CMyComPtr<ICompressProgressInfo> _localProgress;
  UInt64 _packTotal;
  UInt64 _unpTotal;

  FStringVector _extractedFolderPaths;
  CRecordVector<UInt32> _extractedFolderIndices;

  #if defined(_WIN32) && !defined(UNDER_CE) && !defined(_SFX)
  bool _saclEnabled;
  #endif

  void CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath);
  HRESULT GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined);
  HRESULT GetUnpackSize();

  HRESULT SendMessageError(const char *message, const FString &path);
  HRESULT SendMessageError2(const char *message, const FString &path1, const FString &path2);

public:

  CLocalProgress *LocalProgressSpec;

  UInt64 NumFolders;
  UInt64 NumFiles;
  UInt64 NumAltStreams;
  UInt64 UnpackSize;
  UInt64 AltStreams_UnpackSize;

  MY_UNKNOWN_IMP2(ICryptoGetTextPassword, ICompressProgressInfo)
  // COM_INTERFACE_ENTRY(IArchiveVolumeExtractCallback)

  INTERFACE_IArchiveExtractCallback(;)

  STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);

  // IArchiveVolumeExtractCallback
  // STDMETHOD(GetInStream)(const wchar_t *name, ISequentialInStream **inStream);

  STDMETHOD(CryptoGetTextPassword)(BSTR *password);

  CArchiveExtractCallback();

  void InitForMulti(bool multiArchives,
      NExtract::NPathMode::EEnum pathMode,
      NExtract::NOverwriteMode::EEnum overwriteMode)
  {
    _multiArchives = multiArchives;
    _pathMode = pathMode;
    _overwriteMode = overwriteMode;
    NumFolders = NumFiles = NumAltStreams = UnpackSize = AltStreams_UnpackSize = 0;
  }

  #ifndef _SFX

  void SetHashMethods(IHashCalc *hash)
  {
    if (!hash)
      return;
    _hashStreamSpec = new COutStreamWithHash;
    _hashStream = _hashStreamSpec;
    _hashStreamSpec->_hash = hash;
  }

  #endif

  void Init(
      const CExtractNtOptions &ntOptions,
      const NWildcard::CCensorNode *wildcardCensor,
      const CArc *arc,
      IFolderArchiveExtractCallback *extractCallback2,
      bool stdOutMode, bool testMode,
      const FString &directoryPath,
      const UStringVector &removePathParts,
      UInt64 packSize);

  #ifdef SUPPORT_LINKS
private:
  CHardLinks _hardLinks;
public:
  // call PrepareHardLinks() after Init()
  HRESULT PrepareHardLinks(const CRecordVector<UInt32> *realIndices);  // NULL means all items
  #endif

  // call it after Init()

  void SetBaseParentFolderIndex(UInt32 indexInArc)
  {
    _use_baseParentFolder_mode = true;
    _baseParentFolder = indexInArc;
  }

  HRESULT SetDirsTimes();
};

#endif
