// Archive/IsoItem.h

#ifndef ZIP7_INC_ARCHIVE_ISO_ITEM_H
#define ZIP7_INC_ARCHIVE_ISO_ITEM_H

#include "../../../../C/CpuArch.h"

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

#if defined(__DOS__)
  #include "../../../DOS/TimeUtils.h"
  using namespace NDOS;
#else
  #include "../../../Windows/TimeUtils.h"
  using namespace NWindows;
#endif

#include "IsoHeader.h"

namespace NArchive {
namespace NIso {

struct CRecordingDateTime
{
  Byte Year;
  Byte Month;
  Byte Day;
  Byte Hour;
  Byte Minute;
  Byte Second;
  signed char GmtOffset; // min intervals from -48 (West) to +52 (East) recorded.
  
  bool GetFileTime(NCOM::CPropVariant &prop) const
  {
    UInt64 v;
    const bool res = NTime::GetSecondsSince1601(Year + 1900, Month, Day, Hour, Minute, Second, v);
    if (res)
    {
      v = (UInt64)((Int64)v - (Int64)((Int32)GmtOffset * 15 * 60));
      v *= 10000000;
      prop.SetAsTimeFrom_Ft64_Prec(v, k_PropVar_TimePrec_Base);
    }
    return res;
  }
};

enum EPx
{
  k_Px_Mode,
  k_Px_Links,
  k_Px_User,
  k_Px_Group,
  k_Px_SerialNumber

  // k_Px_Num
};

/*
enum ETf
{
  k_Tf_CTime,
  k_Tf_MTime,
  k_Tf_ATime,
  k_Tf_Attrib,
  k_Tf_Backup,
  k_Tf_Expiration,
  k_Tf_Effective

  // k_Tf_Num
};
*/

struct CDirRecord
{
  UInt32 ExtentLocation;
  UInt32 Size;
  CRecordingDateTime DateTime;
  Byte FileFlags;
  Byte FileUnitSize;
  Byte InterleaveGapSize;
  Byte ExtendedAttributeRecordLen;
  UInt16 VolSequenceNumber;
  CByteBuffer FileId;
  CByteBuffer SystemUse;

  bool AreMultiPartEqualWith(const CDirRecord &a) const
  {
    return FileId == a.FileId
        && (FileFlags & (~NFileFlags::kNonFinalExtent)) ==
        (a.FileFlags & (~NFileFlags::kNonFinalExtent));
  }

  bool IsDir() const { return (FileFlags & NFileFlags::kDirectory) != 0; }
  bool IsNonFinalExtent() const { return (FileFlags & NFileFlags::kNonFinalExtent) != 0; }

  bool IsSystemItem() const
  {
    if (FileId.Size() != 1)
      return false;
    Byte b = *(const Byte *)FileId;
    return (b == 0 || b == 1);
  }

  
  const Byte* FindSuspRecord(unsigned skipSize, Byte id0, Byte id1, unsigned &lenRes) const
  {
    lenRes = 0;
    if (SystemUse.Size() < skipSize)
      return NULL;
    const Byte *p = (const Byte *)SystemUse + skipSize;
    unsigned rem = (unsigned)(SystemUse.Size() - skipSize);
    while (rem >= 5)
    {
      unsigned len = p[2];
      if (len < 3 || len > rem)
        return NULL;
      if (p[0] == id0 && p[1] == id1 && p[3] == 1)
      {
        if (len < 4)
          return NULL; // Check it
        lenRes = len - 4;
        return p + 4;
      }
      p += len;
      rem -= len;
    }
    return NULL;
  }

  
  const Byte* GetNameCur(bool checkSusp, unsigned skipSize, unsigned &nameLenRes) const
  {
    const Byte *res = NULL;
    unsigned len = 0;
    if (checkSusp)
      res = FindSuspRecord(skipSize, 'N', 'M', len);
    if (!res || len < 1)
    {
      res = (const Byte *)FileId;
      len = (unsigned)FileId.Size();
    }
    else
    {
      res++;
      len--;
    }
    unsigned i;
    for (i = 0; i < len; i++)
      if (res[i] == 0)
        break;
    nameLenRes = i;
    return res;
  }


  bool GetSymLink(unsigned skipSize, AString &link) const
  {
    link.Empty();
    const Byte *p = NULL;
    unsigned len = 0;
    p = FindSuspRecord(skipSize, 'S', 'L', len);
    if (!p || len < 1)
      return false;

    if (*p != 0)
      return false;

    p++;
    len--;

    while (len != 0)
    {
      if (len < 2)
        return false;
      unsigned flags = p[0];
      unsigned cl = p[1];
      p += 2;
      len -= 2;

      if (cl > len)
        return false;

      bool needSlash = false;
      
           if (flags & (1 << 1)) link += "./";
      else if (flags & (1 << 2)) link += "../";
      else if (flags & (1 << 3)) link.Add_Slash();
      else
        needSlash = true;

      for (unsigned i = 0; i < cl; i++)
      {
        const Byte c = p[i];
        if (c == 0)
        {
          break;
          // return false;
        }
        link += (char)c;
      }

      p += cl;
      len -= cl;

      if (len == 0)
        break;

      if (needSlash)
        link.Add_Slash();
    }

    return true;
  }

  static bool GetLe32Be32(const Byte *p, UInt32 &dest)
  {
    UInt32 v1 = GetUi32(p);
    UInt32 v2 = GetBe32(p + 4);
    if (v1 == v2)
    {
      dest = v1;
      return true;
    }
    return false;
  }


  bool GetPx(unsigned skipSize, unsigned pxType, UInt32 &val) const
  {
    val = 0;
    const Byte *p = NULL;
    unsigned len = 0;
    p = FindSuspRecord(skipSize, 'P', 'X', len);
    if (!p)
      return false;
    // px.Clear();
    if (len < (pxType + 1) * 8)
      return false;

    return GetLe32Be32(p + pxType * 8, val);
  }

  /*
  bool GetTf(int skipSize, unsigned pxType, CRecordingDateTime &t) const
  {
    const Byte *p = NULL;
    unsigned len = 0;
    p = FindSuspRecord(skipSize, 'T', 'F', len);
    if (!p)
      return false;
    if (len < 1)
      return false;
    Byte flags = *p++;
    len--;

    unsigned step = 7;
    if (flags & 0x80)
    {
      step = 17;
      return false;
    }

    if ((flags & (1 << pxType)) == 0)
      return false;

    for (unsigned i = 0; i < pxType; i++)
    {
      if (len < step)
        return false;
      if (flags & (1 << i))
      {
        p += step;
        len -= step;
      }
    }

    if (len < step)
      return false;
    
    t.Year = p[0];
    t.Month = p[1];
    t.Day = p[2];
    t.Hour = p[3];
    t.Minute = p[4];
    t.Second = p[5];
    t.GmtOffset = (signed char)p[6];

    return true;
  }
  */

  bool CheckSusp(const Byte *p, unsigned &startPos) const
  {
    if (p[0] == 'S' &&
        p[1] == 'P' &&
        p[2] == 0x7 &&
        p[3] == 0x1 &&
        p[4] == 0xBE &&
        p[5] == 0xEF)
    {
      startPos = p[6];
      return true;
    }
    return false;
  }

  bool CheckSusp(unsigned &startPos) const
  {
    const Byte *p = (const Byte *)SystemUse;
    const size_t len = SystemUse.Size();
    const unsigned kMinLen = 7;
    if (len < kMinLen)
      return false;
    if (CheckSusp(p, startPos))
      return true;
    const unsigned kOffset2 = 14;
    if (len < kOffset2 + kMinLen)
      return false;
    return CheckSusp(p + kOffset2, startPos);
  }
};

}}

#endif
