// UpdatePair.cpp

#include "StdAfx.h"

#include <time.h>

#include "Common/Defs.h"
#include "Common/Wildcard.h"
#include "Windows/Time.h"

#include "UpdatePair.h"
#include "SortUtils.h"

using namespace NWindows;
using namespace NTime;

static int MyCompareTime(NFileTimeType::EEnum fileTimeType, const FILETIME &time1, const FILETIME &time2)
{
  switch(fileTimeType)
  {
    case NFileTimeType::kWindows:
      return ::CompareFileTime(&time1, &time2);
    case NFileTimeType::kUnix:
      {
        UInt32 unixTime1, unixTime2;
        FileTimeToUnixTime(time1, unixTime1);
        FileTimeToUnixTime(time2, unixTime2);
        return MyCompare(unixTime1, unixTime2);
      }
    case NFileTimeType::kDOS:
      {
        UInt32 dosTime1, dosTime2;
        FileTimeToDosTime(time1, dosTime1);
        FileTimeToDosTime(time2, dosTime2);
        return MyCompare(dosTime1, dosTime2);
      }
  }
  throw 4191618;
}

static const wchar_t *kDuplicateFileNameMessage = L"Duplicate filename:";
static const wchar_t *kNotCensoredCollisionMessaged = L"Internal file name collision (file on disk, file in archive):";

static void ThrowError(const UString &message, const UString &s1, const UString &s2)
{
  UString m = message;
  m += L'\n';
  m += s1;
  m += L'\n';
  m += s2;
  throw m;
}

static void TestDuplicateString(const UStringVector &strings, const CIntVector &indices)
{
  for(int i = 0; i + 1 < indices.Size(); i++)
    if (CompareFileNames(strings[indices[i]], strings[indices[i + 1]]) == 0)
      ThrowError(kDuplicateFileNameMessage, strings[indices[i]], strings[indices[i + 1]]);
}

void GetUpdatePairInfoList(
    const CDirItems &dirItems,
    const CObjectVector<CArcItem> &arcItems,
    NFileTimeType::EEnum fileTimeType,
    CRecordVector<CUpdatePair> &updatePairs)
{
  CIntVector dirIndices, arcIndices;
  
  int numDirItems = dirItems.Items.Size();
  int numArcItems = arcItems.Size();
  
  
  {
    UStringVector arcNames;
    arcNames.Reserve(numArcItems);
    for (int i = 0; i < numArcItems; i++)
      arcNames.Add(arcItems[i].Name);
    SortFileNames(arcNames, arcIndices);
    TestDuplicateString(arcNames, arcIndices);
  }

  UStringVector dirNames;
  {
    dirNames.Reserve(numDirItems);
    for (int i = 0; i < numDirItems; i++)
      dirNames.Add(dirItems.GetLogPath(i));
    SortFileNames(dirNames, dirIndices);
    TestDuplicateString(dirNames, dirIndices);
  }
  
  int dirIndex = 0, arcIndex = 0;
  while (dirIndex < numDirItems && arcIndex < numArcItems)
  {
    CUpdatePair pair;
    int dirIndex2 = dirIndices[dirIndex];
    int arcIndex2 = arcIndices[arcIndex];
    const CDirItem &di = dirItems.Items[dirIndex2];
    const CArcItem &ai = arcItems[arcIndex2];
    int compareResult = CompareFileNames(dirNames[dirIndex2], ai.Name);
    if (compareResult < 0)
    {
      pair.State = NUpdateArchive::NPairState::kOnlyOnDisk;
      pair.DirIndex = dirIndex2;
      dirIndex++;
    }
    else if (compareResult > 0)
    {
      pair.State = ai.Censored ?
          NUpdateArchive::NPairState::kOnlyInArchive:
          NUpdateArchive::NPairState::kNotMasked;
      pair.ArcIndex = arcIndex2;
      arcIndex++;
    }
    else
    {
      if (!ai.Censored)
        ThrowError(kNotCensoredCollisionMessaged, dirNames[dirIndex2], ai.Name);
      pair.DirIndex = dirIndex2;
      pair.ArcIndex = arcIndex2;
      switch (MyCompareTime(
          ai.TimeType != - 1 ? (NFileTimeType::EEnum)ai.TimeType : fileTimeType,
          di.MTime, ai.MTime))
      {
        case -1: pair.State = NUpdateArchive::NPairState::kNewInArchive; break;
        case 1:  pair.State = NUpdateArchive::NPairState::kOldInArchive; break;
        default:
          if (ai.SizeDefined)
            if (di.Size != ai.Size)
              pair.State = NUpdateArchive::NPairState::kUnknowNewerFiles;
            else
              pair.State = NUpdateArchive::NPairState::kSameFiles;
          else
              pair.State = NUpdateArchive::NPairState::kUnknowNewerFiles;
      }
      dirIndex++;
      arcIndex++;
    }
    updatePairs.Add(pair);
  }

  for (; dirIndex < numDirItems; dirIndex++)
  {
    CUpdatePair pair;
    pair.State = NUpdateArchive::NPairState::kOnlyOnDisk;
    pair.DirIndex = dirIndices[dirIndex];
    updatePairs.Add(pair);
  }
  
  for (; arcIndex < numArcItems; arcIndex++)
  {
    CUpdatePair pair;
    int arcIndex2 = arcIndices[arcIndex];
    pair.State = arcItems[arcIndex2].Censored ?
        NUpdateArchive::NPairState::kOnlyInArchive:
        NUpdateArchive::NPairState::kNotMasked;
    pair.ArcIndex = arcIndex2;
    updatePairs.Add(pair);
  }

  updatePairs.ReserveDown();
}
