/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "PDFViaEMFPrintHelper.h"
#include "WindowsEMF.h"
#include "gtest/gtest.h"
#include "nsLocalFile.h"
#include "nsFileStreams.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceDefs.h"
#include "nsString.h"
#include "mozilla/Unused.h"

using namespace mozilla;
namespace mozilla {
namespace widget {

static already_AddRefed<nsIFile>
GetFileViaSpecialDirectory(const char* aSpecialDirName,
                           const char* aFileName)
{
  nsCOMPtr<nsIFile> file;
  nsresult rv = NS_GetSpecialDirectory(aSpecialDirName, getter_AddRefs(file));
  NS_ENSURE_SUCCESS(rv, nullptr);

  rv = file->AppendNative(nsDependentCString(aFileName));
  NS_ENSURE_SUCCESS(rv, nullptr);

  return file.forget();
}

static NS_IMETHODIMP
GetFilePathViaSpecialDirectory(const char* aSpecialDirName,
                               const char* aFileName,
                               nsAutoString& aPath)
{
  nsCOMPtr<nsIFile> file = GetFileViaSpecialDirectory(aSpecialDirName,
                                                      aFileName);
  NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);

  nsAutoCString path;
  nsresult rv  = file->GetNativePath(path);
  if (NS_SUCCEEDED(rv)) {
    aPath = NS_ConvertUTF8toUTF16(path);
  }
  return rv;
}

static bool
SetupPrintHelper(const char* aFileName, PDFViaEMFPrintHelper* aHelper)
{
  nsCOMPtr<nsIFile> file = GetFileViaSpecialDirectory(NS_OS_CURRENT_WORKING_DIR,
                                                      aFileName);
  NS_ENSURE_TRUE(file, false);

  UniquePtr<PDFViaEMFPrintHelper> PDFPrintHelper =
    MakeUnique<PDFViaEMFPrintHelper>();
  nsresult rv = aHelper->OpenDocument(file);
  NS_ENSURE_SUCCESS(rv, false);

  NS_ENSURE_TRUE(aHelper->GetPageCount() > 0, false);

  return true;
}

static uint32_t
GetFileContents(const nsAutoString& aFile, Vector<char>& aBuf)
{
  RefPtr<nsLocalFile> file = new nsLocalFile();
  file->InitWithPath(aFile);
  RefPtr<nsFileInputStream> inputStream = new nsFileInputStream();
  nsresult rv = inputStream->Init(file,
                                  /* ioFlags */ PR_RDONLY,
                                  /* perm */ -1,
                                  /* behaviorFlags */ 0);
  if (NS_FAILED(rv)) {
    return 0;
  }

  int64_t size = 0;
  inputStream->GetSize(&size);
  if (size <= 0) {
    return 0;
  }

  Unused << aBuf.initCapacity(size);
  uint32_t len = static_cast<uint32_t>(size);
  uint32_t read = 0;
  uint32_t haveRead = 0;
  while (len > haveRead) {
    rv = inputStream->Read(aBuf.begin() + haveRead, len - haveRead, &read);
    if (NS_FAILED(rv)) {
      return 0;
    }
    haveRead += read;
  }
  return haveRead;
}

static void
CompareTwoFiles(const nsAutoString& aTestFile, const nsAutoString& aRefFile)
{
  Vector<char> testContents;
  uint32_t testSize = GetFileContents(aTestFile, testContents);
  ASSERT_GT(testSize, static_cast<uint32_t>(0));

  Vector<char> refContents;
  uint32_t refSize = GetFileContents(aRefFile, refContents);
  ASSERT_GT(refSize, static_cast<uint32_t>(0));

  ASSERT_EQ(testSize, refSize);
  ASSERT_TRUE(memcmp(testContents.begin(), refContents.begin(), testSize) == 0);
}

class EMFViaExtDLLHelper: public PDFViaEMFPrintHelper
{
public:
  EMFViaExtDLLHelper() {}
  ~EMFViaExtDLLHelper() {}

protected:
  virtual bool CreatePDFiumEngineIfNeed() override {
    if (!mPDFiumEngine) {
    #ifdef _WIN64
      nsAutoCString externalDll("pdfium_ref_x64.dll");
    #else
      nsAutoCString externalDll("pdfium_ref_x86.dll");
    #endif
      mPDFiumEngine = PDFiumEngineShim::GetInstanceOrNull(externalDll);
    }

    return !!mPDFiumEngine;
  }
};

// Compare an EMF file with the reference which is generated by the external
// library.
TEST(TestEMFConversion, CompareEMFWithReference)
{
  UniquePtr<PDFViaEMFPrintHelper> PDFHelper =
    MakeUnique<PDFViaEMFPrintHelper>();
  ASSERT_TRUE(SetupPrintHelper("PrinterTestPage.pdf", PDFHelper.get()));

  const int pageWidth = 4961;
  const int pageHeight = 7016;

  // Convert a PDF file to an EMF file(PrinterTestPage.pdf -> gtest.emf)
  nsAutoString emfPath;
  ASSERT_TRUE(NS_SUCCEEDED(GetFilePathViaSpecialDirectory(NS_OS_TEMP_DIR,
                                                        "gtest.emf",
                                                        emfPath)));
  ASSERT_TRUE(PDFHelper->SavePageToFile(emfPath.get(), 0,
                                        pageWidth, pageHeight));
  PDFHelper->CloseDocument();
#ifdef _WIN64
  // Convert a PDF file to an EMF file by external library.
  // (PrinterTestPage.pdf -> gtestRef.emf)
  UniquePtr<EMFViaExtDLLHelper> ExtHelper = MakeUnique<EMFViaExtDLLHelper>();
  ASSERT_TRUE(SetupPrintHelper("PrinterTestPage.pdf", ExtHelper.get()));

  nsAutoString emfPathRef;
  ASSERT_TRUE(NS_SUCCEEDED(GetFilePathViaSpecialDirectory(NS_OS_TEMP_DIR,
                                                          "gtestRef.emf",
                                                          emfPathRef)));

  ASSERT_TRUE(ExtHelper->SavePageToFile(emfPathRef.get(), 0,
                                        pageWidth, pageHeight));
  ExtHelper->CloseDocument();

  CompareTwoFiles(emfPath, emfPathRef);
#endif
}

// Input a PDF file which does not exist
TEST(TestEMFConversion, TestInputNonExistingPDF)
{
  UniquePtr<PDFViaEMFPrintHelper> PDFHelper =
    MakeUnique<PDFViaEMFPrintHelper>();
  ASSERT_FALSE(SetupPrintHelper("null.pdf", PDFHelper.get()));
}

// Test insufficient width and height
TEST(TestEMFConversion, TestInsufficientWidthAndHeight)
{
  UniquePtr<PDFViaEMFPrintHelper> PDFHelper =
    MakeUnique<PDFViaEMFPrintHelper>();
  ASSERT_TRUE(SetupPrintHelper("PrinterTestPage.pdf", PDFHelper.get()));

  nsAutoString emfPath;
  ASSERT_TRUE(NS_SUCCEEDED(GetFilePathViaSpecialDirectory(NS_OS_TEMP_DIR,
                                                         "gtest.emf",
                                                          emfPath)));

  ASSERT_FALSE(PDFHelper->SavePageToFile(emfPath.get(), 0, 0, 0));
  ASSERT_FALSE(PDFHelper->SavePageToFile(emfPath.get(), 0, 100, -1));

  PDFHelper->CloseDocument();
}

} // namespace widget
} // namespace mozilla