/*
 * Copyright 2011 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "sfntly/port/type.h"
#include <assert.h>
#include <unicode/ucnv.h>

#include <string>
#include <iostream>

#include "gtest/gtest.h"
#include "sfntly/font.h"
#include "sfntly/font_factory.h"
#include "sfntly/table/core/cmap_table.h"
#include "sfntly/data/memory_byte_array.h"
#include "sfntly/table/core/font_header_table.h"
#include "sfntly/tag.h"

#include "test/test_utils.h"
#include "test/test_font_utils.h"
#include "test/test_data.h"

#if GTEST_HAS_PARAM_TEST

namespace sfntly {
using ::testing::TestWithParam;
using ::testing::Values;

class CMapTestCase {
 public:
  CMapTestCase(const char* font_name,
               int32_t first_platform_id,
               int32_t first_encoding_id,
               const char* first_charset_name,
               int32_t second_platform_id,
               int32_t second_encoding_id,
               const char* second_charset_name,
               int32_t low_char,
               int32_t high_char)
      : font_name_(font_name),
        first_platform_id_(first_platform_id),
        first_encoding_id_(first_encoding_id),
        first_charset_name_(first_charset_name),
        second_platform_id_(second_platform_id),
        second_encoding_id_(second_encoding_id),
        second_charset_name_(second_charset_name),
        low_char_(low_char),
        high_char_(high_char) {
  }

  const char* font_name() const { return font_name_; }
  int32_t first_platform_id() const { return first_platform_id_; }
  int32_t first_encoding_id() const { return first_encoding_id_; }
  const char* first_charset_name() const { return first_charset_name_; }
  int32_t second_platform_id() const { return second_platform_id_; }
  int32_t second_encoding_id() const { return second_encoding_id_; }
  const char* second_charset_name() const { return second_charset_name_; }
  int32_t low_char() const { return low_char_; }
  int32_t high_char() const { return high_char_; }

 private:
  const char* font_name_;
  int32_t first_platform_id_;
  int32_t first_encoding_id_;
  const char* first_charset_name_;
  int32_t second_platform_id_;
  int32_t second_encoding_id_;
  const char* second_charset_name_;
  int32_t low_char_;
  int32_t high_char_;
};

class CMapTests : public :: testing::TestWithParam<CMapTestCase> {
 public:
  CMapTests() : encoder1_(NULL), encoder2_(NULL), successful_setup_(false) {
  }
  virtual void SetUp() {}
  virtual void TearDown();

  void CommonSetUp(FontArray* font_array);

  void CompareCMaps();

  Ptr<CMapTable::CMap> cmap1_;
  Ptr<CMapTable::CMap> cmap2_;
  UConverter* encoder1_;
  UConverter* encoder2_;
  bool successful_setup_;
};

::std::ostream& operator<<(::std::ostream& os, const CMapTestCase *test_case) {
  return os << "("
            << test_case->font_name() << ", "
            << test_case->first_platform_id() << ", "
            << test_case->first_encoding_id() << ", "
            << test_case->first_charset_name() << ", "
            << test_case->second_platform_id() << ", "
            << test_case->second_encoding_id() << ", "
            << test_case->second_charset_name() << ", "
            << test_case->low_char() << ", "
            << test_case->high_char() << ")";
}

void CMapTests::CommonSetUp(FontArray* font_array) {
  ASSERT_NE(font_array, reinterpret_cast<FontArray*>(NULL));
  ASSERT_FALSE(font_array->empty());
  Ptr<Font> font;
  font = font_array->at(0);
  ASSERT_NE(font, reinterpret_cast<Font*>(NULL));
  Ptr<CMapTable> cmap_table =
      down_cast<CMapTable*>(font->GetTable(Tag::cmap));
  cmap1_.Attach(cmap_table->GetCMap(GetParam().first_platform_id(),
                                    GetParam().first_encoding_id()));
  ASSERT_NE((cmap1_), reinterpret_cast<CMapTable::CMap*>(NULL));
  cmap2_.Attach(cmap_table->GetCMap(GetParam().second_platform_id(),
                                    GetParam().second_encoding_id()));
  ASSERT_NE((cmap2_), reinterpret_cast<CMapTable::CMap*>(NULL));
  encoder1_ = TestUtils::GetEncoder(GetParam().first_charset_name());
  encoder2_ = TestUtils::GetEncoder(GetParam().second_charset_name());
  successful_setup_ = true;
}

void CMapTests::TearDown() {
  if (encoder1_)
    ucnv_close(encoder1_);
  if (encoder2_)
    ucnv_close(encoder2_);
}

void CMapTests::CompareCMaps() {
  ASSERT_TRUE(successful_setup_);
  for (int32_t uchar = GetParam().low_char();
       uchar <= GetParam().high_char(); ++uchar) {
    int32_t c1 = uchar;
    if (encoder1_ != NULL)
      c1 = TestUtils::EncodeOneChar(encoder1_, (int16_t)uchar);
    int32_t c2 = uchar;
    if (encoder2_ != NULL)
      c2 = TestUtils::EncodeOneChar(encoder2_, (int16_t)uchar);
    int32_t glyph_id1 = cmap1_->GlyphId(c1);
    int32_t glyph_id2 = cmap2_->GlyphId(c2);
#ifdef SFNTLY_DEBUG_CMAP
    if (glyph_id1 != glyph_id2)
      fprintf(stderr, "%x: g1=%x, %x: g2=%x\n", c1, glyph_id1, c2, glyph_id2);
#endif
    ASSERT_EQ(glyph_id1, glyph_id2);
  }
#ifdef SFNTLY_SFNTLY_DEBUG_CMAPCMAP
  fprintf(stderr, "\n");
#endif
}

TEST_P(CMapTests, GlyphsBetweenCMapsFingerprint) {
  Ptr<FontFactory> font_factory;
  font_factory.Attach(FontFactory::GetInstance());
  font_factory->FingerprintFont(true);
  FontArray font_array;
  LoadFont(GetParam().font_name(), font_factory, &font_array);
  CommonSetUp(&font_array);
  CompareCMaps();
}

TEST_P(CMapTests, GlyphsBetweenCMapsNoFingerprint) {
  Ptr<FontFactory> font_factory;
  font_factory.Attach(FontFactory::GetInstance());
  FontArray font_array;
  LoadFont(GetParam().font_name(), font_factory, &font_array);
  CommonSetUp(&font_array);
  CompareCMaps();
}

TEST_P(CMapTests, GlyphsBetweenCMapsUsingByteVector) {
  FontArray font_array;
  LoadFontUsingByteVector(GetParam().font_name(), true, &font_array);
  CommonSetUp(&font_array);
  CompareCMaps();
}

CMapTestCase kCMapTestsTestCases[] = {
  CMapTestCase(SAMPLE_TTF_FILE,
               PlatformId::kWindows,
               WindowsEncodingId::kUnicodeUCS2,
               NULL,
               PlatformId::kUnicode,
               UnicodeEncodingId::kUnicode2_0_BMP,
               NULL,
               (int32_t)0x20,
               (int32_t)0x7f),
};

INSTANTIATE_TEST_CASE_P(CMapTests,
                        CMapTests,
                        ::testing::ValuesIn(kCMapTestsTestCases));
}

#else

TEST(DummyTest, ValueParameterizedTestsAreNotSupportedOnThisPlatform) {}

#endif  // GTEST_HAS_PARAM