/*
 * 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 <string.h>

#include <vector>
#include <string>
#include <algorithm>

#include "sfntly/font.h"
#include "sfntly/font_factory.h"
#include "sfntly/table/core/cmap_table.h"
#include "sfntly/tag.h"
#include "sfntly/port/type.h"
#include "sfntly/port/refcount.h"
#include "test/test_data.h"
#include "test/test_font_utils.h"

#include "gtest/gtest.h"

#if GTEST_HAS_PARAM_TEST

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

typedef std::vector<bool> BitSet;

class CMapIteratorTestCase {
 public:
  CMapIteratorTestCase(int32_t platform_id, int32_t encoding_id,
                       const char* file_name)
      : platform_id_(platform_id),
        encoding_id_(encoding_id),
        file_name_(file_name) {
  }
  ~CMapIteratorTestCase() {}
  int32_t platform_id() const { return platform_id_; }
  int32_t encoding_id() const { return encoding_id_; }
  const char* file_name() const { return file_name_; }

 private:
  int32_t platform_id_;
  int32_t encoding_id_;
  const char* file_name_;
};

class CMapIteratorTests
    : public ::testing::TestWithParam<CMapIteratorTestCase> {
 public:
  virtual void SetUp();
  virtual void TearDown() {}

  BitSet* GenerateCMapEntries(int32_t start, int32_t count);
  int32_t CompareCMapIterAndBitSet(CMapTable::CMap::CharacterIterator*
                                   character_iterator,
                                   BitSet* bit_set);

  Ptr<CMapTable::CMap> cmap_;
};

void CMapIteratorTests::SetUp() {
  FontArray fonts;
  Ptr<FontFactory> font_factory;
  const char* file_name = GetParam().file_name();
  LoadFont(file_name, font_factory, &fonts);
  Ptr<Font> font;
  font.Attach(fonts[0].Detach());
  Ptr<CMapTable> cmap_table = down_cast<CMapTable*>(font->GetTable(Tag::cmap));
  ASSERT_FALSE(cmap_table == NULL);
  cmap_.Attach(cmap_table->GetCMap(GetParam().platform_id(),
                                   GetParam().encoding_id()));
  ASSERT_FALSE(cmap_ == NULL);
}

BitSet* CMapIteratorTests::GenerateCMapEntries(int32_t start, int32_t count) {
  BitSet* entries = new BitSet(count);
  for (int32_t c = start; c < start + count; ++c) {
    int32_t g = cmap_->GlyphId(c);
    if (g != CMapTable::NOTDEF)
      (*entries)[c] = true;
  }
  return entries;
}

int32_t
CMapIteratorTests::
CompareCMapIterAndBitSet(CMapTable::CMap::CharacterIterator* character_iterator,
                         BitSet* bit_set) {
  int32_t iterator_not_bitset_count = 0;
  BitSet::iterator end = bit_set->end(),
      beginning = bit_set->begin(),
      init_beginning = beginning,
      current = std::find(beginning, end, true);
  for (int32_t next_bit = current - beginning;
       character_iterator->HasNext() && current != end;
       next_bit = current - init_beginning) {
    int32_t c = character_iterator->Next();
    EXPECT_TRUE(c <= next_bit || current == end);
    if (!(c <= next_bit || current == end))
      return -1;
    if (c == next_bit) {
      beginning = current + 1;
      current = std::find(beginning, end, true);
    } else {
      iterator_not_bitset_count++;
    }
  }
  EXPECT_EQ(end, current);
#if defined (SFNTLY_DEBUG_CMAP)
  fprintf(stderr, "%s %d: Differences between iterator and bitset: %d\n",
          cmap_->format(), GetParam().file_name(), iterator_not_bitset_count);
#endif
  return iterator_not_bitset_count;
}

TEST_P(CMapIteratorTests, IteratorTest) {
  BitSet* bit_set = GenerateCMapEntries(0, 0x10ffff);
  CMapTable::CMap::CharacterIterator* character_iterator = NULL;
  character_iterator = cmap_->Iterator();
  EXPECT_NE(character_iterator,
            static_cast<CMapTable::CMap::CharacterIterator*>(NULL));
  CompareCMapIterAndBitSet(character_iterator, bit_set);
  delete character_iterator;
  delete bit_set;
}

CMapIteratorTestCase kCMapIteratorTestsTestCases[] = {
  CMapIteratorTestCase(CMapTable::WINDOWS_BMP.platform_id,
                       CMapTable::WINDOWS_BMP.encoding_id,
                       SAMPLE_TTF_FILE)
};

INSTANTIATE_TEST_CASE_P(CMapIteratorTests,
                        CMapIteratorTests,
                        ::testing::ValuesIn(kCMapIteratorTestsTestCases));
}

#else

TEST(DummyTest, ValueParameterizedTestsAreNotSupportedOnThisPlatform) {}

#endif  // GTEST_HAS_PARAM
