/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 "MockStripeStreams.hh"
#include "SchemaEvolution.hh"
#include "TypeImpl.hh"

#include "wrap/gtest-wrapper.h"

namespace orc {

  bool testConvertReader(const std::string file, const std::string& read, bool can, bool need) {
    auto fileType = std::shared_ptr<Type>(Type::buildTypeFromString(file).release());
    auto readType = std::shared_ptr<Type>(Type::buildTypeFromString(read).release());

    if (!can) {
      EXPECT_THROW(SchemaEvolution(readType, fileType.get()), SchemaEvolutionError)
          << "fileType: " << fileType->toString() << "\nreadType: " << readType->toString();
    } else {
      // if can convert, check that there are no excepted be thrown and
      // we can create reader successfully
      SchemaEvolution se(readType, fileType.get());
      EXPECT_FALSE(se.needConvert(*fileType));
      EXPECT_EQ(need, se.needConvert(*fileType->getSubtype(0)))
          << "fileType: " << fileType->toString() << "\nreadType: " << readType->toString();
      MockStripeStreams streams;
      std::vector<bool> selectedColumns(2);
      EXPECT_CALL(streams, getSelectedColumns()).WillRepeatedly(testing::Return(selectedColumns));
      proto::ColumnEncoding directEncoding;
      directEncoding.set_kind(proto::ColumnEncoding_Kind_DIRECT);
      EXPECT_CALL(streams, getEncoding(testing::_)).WillRepeatedly(testing::Return(directEncoding));

      EXPECT_CALL(streams, getStreamProxy(testing::_, testing::_, testing::_))
          .WillRepeatedly(testing::Return(nullptr));

      std::string dummyStream("dummy");
      ON_CALL(streams, getStreamProxy(1, proto::Stream_Kind_SECONDARY, testing::_))
          .WillByDefault(testing::Return(
              new SeekableArrayInputStream(dummyStream.c_str(), dummyStream.length())));

      EXPECT_CALL(streams, getSchemaEvolution()).WillRepeatedly(testing::Return(&se));

      EXPECT_TRUE(buildReader(*fileType, streams) != nullptr);
    }
    return true;
  }

  TEST(SchemaEvolution, createConvertReader) {
    std::map<size_t, std::string> types = {
        {0, "struct<t1:boolean>"},        {1, "struct<t1:tinyint>"},
        {2, "struct<t1:smallint>"},       {3, "struct<t1:int>"},
        {4, "struct<t1:bigint>"},         {5, "struct<t1:float>"},
        {6, "struct<t1:double>"},         {7, "struct<t1:string>"},
        {8, "struct<t1:char(5)>"},        {9, "struct<t1:varchar(5)>"},
        {10, "struct<t1:char(3)>"},       {11, "struct<t1:varchar(3)>"},
        {12, "struct<t1:decimal(25,2)>"}, {13, "struct<t1:decimal(15,2)>"},
        {14, "struct<t1:timestamp>"},     {15, "struct<t1:timestamp with local time zone>"},
        {16, "struct<t1:date>"}};

    size_t typesSize = types.size();
    std::vector<std::vector<bool>> needConvert(typesSize, std::vector<bool>(typesSize, 0));
    std::vector<std::vector<bool>> canConvert(typesSize, std::vector<bool>(typesSize, 0));

    // all types can convert to itselfs
    for (size_t i = 0; i < types.size(); i++) {
      canConvert[i][i] = true;
    }

    // conversion from numeric to numeric
    for (size_t i = 0; i <= 6; i++) {
      for (size_t j = 0; j <= 6; j++) {
        canConvert[i][j] = true;
        needConvert[i][j] = (i != j);
      }
    }

    // conversion from numeric to string/char/varchar
    for (size_t i = 0; i <= 6; i++) {
      for (size_t j = 7; j <= 11; j++) {
        canConvert[i][j] = true;
        needConvert[i][j] = true;
      }
    }

    // conversion from numeric to decimal
    for (size_t i = 0; i <= 6; i++) {
      for (size_t j = 12; j <= 13; j++) {
        canConvert[i][j] = true;
        needConvert[i][j] = true;
      }
    }

    // conversion from numeric to timestamp
    for (size_t i = 0; i <= 6; i++) {
      for (size_t j = 14; j <= 15; j++) {
        canConvert[i][j] = true;
        needConvert[i][j] = true;
      }
    }

    for (size_t i = 0; i < typesSize; i++) {
      for (size_t j = 0; j < typesSize; j++) {
        testConvertReader(types[i], types[j], canConvert[i][j], needConvert[i][j]);
      }
    }
  }

}  // namespace orc
