// Copyright (c) 2009-2017 The OTS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "config.h"

#if defined(HAVE_CORETEXT)
#include <ApplicationServices/ApplicationServices.h>
#elif defined(HAVE_WIN32)
#define NOMINMAX
#include <Windows.h>
#elif defined(HAVE_FREETYPE)
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#endif

#include <fstream>
#include <iostream>
#include <vector>

#include "test-context.h"
#include "ots-memory-stream.h"

namespace {

int Usage(const char *argv0) {
  std::fprintf(stderr, "Usage: %s <ttf file>\n", argv0);
  return 1;
}

bool DumpResults(const uint8_t *result1, const size_t len1,
                 const uint8_t *result2, const size_t len2) {
  std::ofstream out1("out1.ttf", std::ofstream::out | std::ofstream::binary);
  std::ofstream out2("out2.ttf", std::ofstream::out | std::ofstream::binary);
  if (!out1.good() || !out2.good())
    return false;

  out1.write(reinterpret_cast<const char*>(result1), len1);
  out2.write(reinterpret_cast<const char*>(result2), len2);
  if (!out1.good() || !out2.good())
    return false;

  return true;
}

// Platform specific implementations.
bool VerifyTranscodedFont(uint8_t *result, const size_t len);

#if defined(HAVE_CORETEXT)
bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
  CFDataRef data = CFDataCreate(0, result, len);
  if (!data) {
    return false;
  }

  CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(data);
  CGFontRef cgFontRef = CGFontCreateWithDataProvider(dataProvider);
  CGDataProviderRelease(dataProvider);
  CFRelease(data);
  if (!cgFontRef) {
    return false;
  }

  size_t numGlyphs = CGFontGetNumberOfGlyphs(cgFontRef);
  CGFontRelease(cgFontRef);
  if (!numGlyphs) {
    return false;
  }
  return true;
}

#elif defined(HAVE_WIN32)
// Windows
bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
  DWORD num_fonts = 0;
  HANDLE handle = AddFontMemResourceEx(result, len, 0, &num_fonts);
  if (!handle) {
    return false;
  }
  RemoveFontMemResourceEx(handle);
  return true;
}

#elif defined(HAVE_FREETYPE)
bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
  FT_Library library;
  FT_Error error = ::FT_Init_FreeType(&library);
  if (error) {
    return false;
  }
  FT_Face dummy;
  error = ::FT_New_Memory_Face(library, result, len, 0, &dummy);
  if (error) {
    return false;
  }
  ::FT_Done_Face(dummy);
  ::FT_Done_FreeType(library);
  return true;
}

#else
bool VerifyTranscodedFont(uint8_t *result, const size_t len) {
  std::fprintf(stderr, "Can't verify the transcoded font on this platform.\n");
  return false;
}

#endif

}  // namespace

int main(int argc, char **argv) {
  if (argc != 2) return Usage(argv[0]);

  std::ifstream ifs(argv[1], std::ifstream::binary);
  if (!ifs.good()) {
    std::fprintf(stderr, "Failed to read file!\n");
    return 1;
  }
  std::vector<uint8_t> in((std::istreambuf_iterator<char>(ifs)),
                          (std::istreambuf_iterator<char>()));

  // A transcoded font is usually smaller than an original font.
  // However, it can be slightly bigger than the original one due to
  // name table replacement and/or padding for glyf table.
  //
  // However, a WOFF font gets decompressed and so can be *much* larger than
  // the original.
  std::vector<uint8_t> result(in.size() * 8);
  ots::MemoryStream output(result.data(), result.size());

  ots::TestContext context(0);

  if (!context.Process(&output, in.data(), in.size())) {
    std::fprintf(stderr, "Failed to sanitize file!\n");
    return 1;
  }
  const size_t result_len = output.Tell();

  std::vector<uint8_t> result2(result_len);
  ots::MemoryStream output2(result2.data(), result2.size());
  if (!context.Process(&output2, result.data(), result_len)) {
    std::fprintf(stderr, "Failed to sanitize previous output!\n");
    return 1;
  }
  const size_t result2_len = output2.Tell();

  bool dump_results = false;
  if (result2_len != result_len) {
    std::fprintf(stderr, "Outputs differ in length\n");
    dump_results = true;
  } else if (std::memcmp(result.data(), result2.data(), result_len)) {
    std::fprintf(stderr, "Outputs differ in content\n");
    dump_results = true;
  }

  if (dump_results) {
    std::fprintf(stderr, "Dumping results to out1.tff and out2.tff\n");
    if (!DumpResults(result.data(), result_len, result2.data(), result2_len)) {
      std::fprintf(stderr, "Failed to dump output files.\n");
      return 1;
    }
  }

  // Verify that original font can be opened by the font renderer. If this
  // fails then no point in verfying the transcoded font.
  if (!VerifyTranscodedFont(in.data(), in.size())) {
    std::fprintf(stderr, "Failed to verify the original font\n");
    return 0;
  }

  // Verify that the transcoded font can be opened by the font renderer for
  // Linux (FreeType2), Mac OS X, or Windows.
  if (!VerifyTranscodedFont(result.data(), result_len)) {
    std::fprintf(stderr, "Failed to verify the transcoded font\n");
    return 1;
  }

  return 0;
}
