// MethodProps.h

#ifndef __7Z_METHOD_PROPS_H
#define __7Z_METHOD_PROPS_H

#include "../../Common/MyString.h"

#include "../../Windows/PropVariant.h"

#include "../ICoder.h"

bool StringToBool(const UString &s, bool &res);
HRESULT PROPVARIANT_to_bool(const PROPVARIANT &prop, bool &dest);
unsigned ParseStringToUInt32(const UString &srcString, UInt32 &number);
HRESULT ParsePropToUInt32(const UString &name, const PROPVARIANT &prop, UInt32 &resValue);

HRESULT ParseMtProp(const UString &name, const PROPVARIANT &prop, UInt32 defaultNumThreads, UInt32 &numThreads);

struct CProp
{
  PROPID Id;
  bool IsOptional;
  NWindows::NCOM::CPropVariant Value;
  CProp(): IsOptional(false) {}
};

struct CProps
{
  CObjectVector<CProp> Props;

  void Clear() { Props.Clear(); }

  bool AreThereNonOptionalProps() const
  {
    FOR_VECTOR (i, Props)
      if (!Props[i].IsOptional)
        return true;
    return false;
  }

  void AddProp32(PROPID propid, UInt32 level);

  void AddPropString(PROPID propid, const wchar_t *s)
  {
    CProp prop;
    prop.IsOptional = true;
    prop.Id = propid;
    prop.Value = s;
    Props.Add(prop);
  }

  HRESULT SetCoderProps(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce) const;
};

class CMethodProps: public CProps
{
  HRESULT SetParam(const UString &name, const UString &value);
public:
  int GetLevel() const;
  int Get_NumThreads() const
  {
    int i = FindProp(NCoderPropID::kNumThreads);
    if (i >= 0)
      if (Props[i].Value.vt == VT_UI4)
        return (int)Props[i].Value.ulVal;
    return -1;
  }

  bool Get_DicSize(UInt32 &res) const
  {
    res = 0;
    int i = FindProp(NCoderPropID::kDictionarySize);
    if (i >= 0)
      if (Props[i].Value.vt == VT_UI4)
      {
        res = Props[i].Value.ulVal;
        return true;
      }
    return false;
  }

  int FindProp(PROPID id) const;

  UInt32 Get_Lzma_Algo() const
  {
    int i = FindProp(NCoderPropID::kAlgorithm);
    if (i >= 0)
      if (Props[i].Value.vt == VT_UI4)
        return Props[i].Value.ulVal;
    return GetLevel() >= 5 ? 1 : 0;
  }

  UInt32 Get_Lzma_DicSize() const
  {
    int i = FindProp(NCoderPropID::kDictionarySize);
    if (i >= 0)
      if (Props[i].Value.vt == VT_UI4)
        return Props[i].Value.ulVal;
    int level = GetLevel();
    return level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26));
  }

  UInt32 Get_Lzma_NumThreads(bool &fixedNumber) const
  {
    fixedNumber = false;
    int numThreads = Get_NumThreads();
    if (numThreads >= 0)
    {
      fixedNumber = true;
      return numThreads < 2 ? 1 : 2;
    }
    return Get_Lzma_Algo() == 0 ? 1 : 2;
  }

  UInt32 Get_BZip2_NumThreads(bool &fixedNumber) const
  {
    fixedNumber = false;
    int numThreads = Get_NumThreads();
    if (numThreads >= 0)
    {
      fixedNumber = true;
      if (numThreads < 1) return 1;
      if (numThreads > 64) return 64;
      return numThreads;
    }
    return 1;
  }

  UInt32 Get_BZip2_BlockSize() const
  {
    int i = FindProp(NCoderPropID::kDictionarySize);
    if (i >= 0)
      if (Props[i].Value.vt == VT_UI4)
      {
        UInt32 blockSize = Props[i].Value.ulVal;
        const UInt32 kDicSizeMin = 100000;
        const UInt32 kDicSizeMax = 900000;
        if (blockSize < kDicSizeMin) blockSize = kDicSizeMin;
        if (blockSize > kDicSizeMax) blockSize = kDicSizeMax;
        return blockSize;
      }
    int level = GetLevel();
    return 100000 * (level >= 5 ? 9 : (level >= 1 ? level * 2 - 1: 1));
  }

  UInt32 Get_Ppmd_MemSize() const
  {
    int i = FindProp(NCoderPropID::kUsedMemorySize);
    if (i >= 0)
      if (Props[i].Value.vt == VT_UI4)
        return Props[i].Value.ulVal;
    int level = GetLevel();
    return level >= 9 ? (192 << 20) : ((UInt32)1 << (level + 19));
  }

  void AddLevelProp(UInt32 level)
  {
    AddProp32(NCoderPropID::kLevel, level);
  }

  void AddNumThreadsProp(UInt32 numThreads)
  {
    AddProp32(NCoderPropID::kNumThreads, numThreads);
  }

  HRESULT ParseParamsFromString(const UString &srcString);
  HRESULT ParseParamsFromPROPVARIANT(const UString &realName, const PROPVARIANT &value);
};

class COneMethodInfo: public CMethodProps
{
public:
  UString MethodName;
  
  void Clear()
  {
    CMethodProps::Clear();
    MethodName.Empty();
  }
  bool IsEmpty() const { return MethodName.IsEmpty() && Props.IsEmpty(); }
  HRESULT ParseMethodFromPROPVARIANT(const UString &realName, const PROPVARIANT &value);
  HRESULT ParseMethodFromString(const UString &s);
};

#endif