// FilterCoder.h

#ifndef __FILTER_CODER_H
#define __FILTER_CODER_H

#include "../../../C/Alloc.h"

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

#include "../IPassword.h"

#define MY_QUERYINTERFACE_ENTRY_AG(i, sub0, sub) else if (iid == IID_ ## i) \
  { if (!sub) RINOK(sub0->QueryInterface(IID_ ## i, (void **)&sub)) \
    *outObject = (void *)(i *)this; }


struct CAlignedMidBuffer
{
  Byte *_bufBase;
  Byte *_buf;

  CAlignedMidBuffer(): _bufBase(NULL), _buf(NULL) {}
  ~CAlignedMidBuffer() { ::MidFree(_bufBase); }

  void AllocAlignedMask(size_t size, size_t alignMask)
  {
    ::MidFree(_bufBase);
    _buf = NULL;
    _bufBase = (Byte *)::MidAlloc(size + alignMask);

    if (_bufBase)
    {
      _buf = (Byte *)(((ptrdiff_t)_bufBase + alignMask) & ~(ptrdiff_t)alignMask);
    }
  }
};

class CFilterCoder:
  public ICompressCoder,

  public ICompressSetOutStreamSize,
  public ICompressInitEncoder,

  public ICompressSetInStream,
  public ISequentialInStream,

  public ICompressSetOutStream,
  public ISequentialOutStream,
  public IOutStreamFinish,

  public ICompressSetBufSize,

  public ICryptoSetPassword,
  public ICryptoProperties,

  public ICompressSetCoderProperties,
  public ICompressWriteCoderProperties,
  public ICryptoResetInitVector,

  public ICompressSetDecoderProperties2,
  public CMyUnknownImp,
  public CAlignedMidBuffer
{
  UInt32 _bufSize;
  UInt32 _inBufSize;
  UInt32 _outBufSize;

  bool _encodeMode;
  bool _outSizeIsDefined;
  UInt64 _outSize;
  UInt64 _nowPos64;

  CMyComPtr<ISequentialInStream> _inStream;
  CMyComPtr<ISequentialOutStream> _outStream;
  UInt32 _bufPos;
  UInt32 _convPos;    // current pos in buffer for converted data
  UInt32 _convSize;   // size of converted data starting from _convPos

  void InitSpecVars()
  {
    _bufPos = 0;
    _convPos = 0;
    _convSize = 0;

    _outSizeIsDefined = false;
    _outSize = 0;
    _nowPos64 = 0;
  }

  HRESULT Alloc();
  HRESULT Init_and_Alloc();
  HRESULT Flush2();

  CMyComPtr<ICryptoSetPassword> _SetPassword;
  CMyComPtr<ICryptoProperties> _CryptoProperties;

  CMyComPtr<ICompressSetCoderProperties> _SetCoderProperties;
  CMyComPtr<ICompressWriteCoderProperties> _WriteCoderProperties;
  CMyComPtr<ICryptoResetInitVector> _CryptoResetInitVector;

  CMyComPtr<ICompressSetDecoderProperties2> _SetDecoderProperties2;

public:
  CMyComPtr<ICompressFilter> Filter;

  CFilterCoder(bool encodeMode);
  ~CFilterCoder();

  class C_InStream_Releaser
  {
  public:
    CFilterCoder *FilterCoder;
    C_InStream_Releaser(): FilterCoder(NULL) {}
    ~C_InStream_Releaser() { if (FilterCoder) FilterCoder->ReleaseInStream(); }
  };

  class C_OutStream_Releaser
  {
  public:
    CFilterCoder *FilterCoder;
    C_OutStream_Releaser(): FilterCoder(NULL) {}
    ~C_OutStream_Releaser() { if (FilterCoder) FilterCoder->ReleaseOutStream(); }
  };

  MY_QUERYINTERFACE_BEGIN2(ICompressCoder)

  MY_QUERYINTERFACE_ENTRY(ICompressSetOutStreamSize)
  MY_QUERYINTERFACE_ENTRY(ICompressInitEncoder)

  MY_QUERYINTERFACE_ENTRY(ICompressSetInStream)
  MY_QUERYINTERFACE_ENTRY(ISequentialInStream)

  MY_QUERYINTERFACE_ENTRY(ICompressSetOutStream)
  MY_QUERYINTERFACE_ENTRY(ISequentialOutStream)
  MY_QUERYINTERFACE_ENTRY(IOutStreamFinish)

  MY_QUERYINTERFACE_ENTRY(ICompressSetBufSize)

  MY_QUERYINTERFACE_ENTRY_AG(ICryptoSetPassword, Filter, _SetPassword)
  MY_QUERYINTERFACE_ENTRY_AG(ICryptoProperties, Filter, _CryptoProperties)

  MY_QUERYINTERFACE_ENTRY_AG(ICompressSetCoderProperties, Filter, _SetCoderProperties)
  MY_QUERYINTERFACE_ENTRY_AG(ICompressWriteCoderProperties, Filter, _WriteCoderProperties)
  MY_QUERYINTERFACE_ENTRY_AG(ICryptoResetInitVector, Filter, _CryptoResetInitVector)

  MY_QUERYINTERFACE_ENTRY_AG(ICompressSetDecoderProperties2, Filter, _SetDecoderProperties2)
  MY_QUERYINTERFACE_END
  MY_ADDREF_RELEASE


  STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream,
      const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress);

  STDMETHOD(SetOutStreamSize)(const UInt64 *outSize);
  STDMETHOD(InitEncoder)();

  STDMETHOD(SetInStream)(ISequentialInStream *inStream);
  STDMETHOD(ReleaseInStream)();
  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);

  STDMETHOD(SetOutStream)(ISequentialOutStream *outStream);
  STDMETHOD(ReleaseOutStream)();
  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
  STDMETHOD(OutStreamFinish)();

  STDMETHOD(SetInBufSize)(UInt32 streamIndex, UInt32 size);
  STDMETHOD(SetOutBufSize)(UInt32 streamIndex, UInt32 size);

  STDMETHOD(CryptoSetPassword)(const Byte *data, UInt32 size);

  STDMETHOD(SetKey)(const Byte *data, UInt32 size);
  STDMETHOD(SetInitVector)(const Byte *data, UInt32 size);

  STDMETHOD(SetCoderProperties)(const PROPID *propIDs,
      const PROPVARIANT *properties, UInt32 numProperties);
  STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream);
  STDMETHOD(ResetInitVector)();

  STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size);


  HRESULT Init_NoSubFilterInit();
};

#endif
