/**
 * @brief Populates the table
 * @param table Table to populate with values.
 * @param num_rows Number of tuples to insert.
 */
void ConstraintsTestsUtil::PopulateTable(concurrency::Transaction *transaction,
                                         storage::DataTable *table,
                                         int num_rows) {
  // Ensure that the tile group is as expected.
  UNUSED_ATTRIBUTE const catalog::Schema *schema = table->GetSchema();
  PL_ASSERT(schema->GetColumnCount() == 4);

  for (int rowid = 0; rowid < num_rows; rowid++) {
    int populate_value = rowid;

    // First column is unique in this case
    auto col1 = type::ValueFactory::GetIntegerValue(PopulatedValue(populate_value, 0));

    // In case of random, make sure this column has duplicated values
    auto col2 = type::ValueFactory::GetIntegerValue(PopulatedValue(populate_value, 1));

    auto col3 = type::ValueFactory::GetDecimalValue(PopulatedValue(populate_value, 2));

    // In case of random, make sure this column has duplicated values
    auto col4 = type::ValueFactory::GetVarcharValue(
        std::to_string(PopulatedValue(populate_value, 3)));

    ConstraintsTestsUtil::ExecuteInsert(transaction, table, col1, col2, col3,
                                        col4);
  }
}
/**
 * @brief Populates the table
 * @param table Table to populate with values.
 * @param num_rows Number of tuples to insert.
 */
void ExecutorTestsUtil::PopulateTable(storage::DataTable *table, int num_rows,
                                      bool mutate, bool random, bool group_by) {
  // Random values
  if (random) std::srand(std::time(nullptr));

  const catalog::Schema *schema = table->GetSchema();

  // Ensure that the tile group is as expected.
  assert(schema->GetColumnCount() == 4);

  // Insert tuples into tile_group.
  auto &txn_manager = concurrency::TransactionManager::GetInstance();
  const bool allocate = true;
  auto txn = txn_manager.BeginTransaction();
  auto testing_pool = TestingHarness::GetInstance().GetTestingPool();

  for (int rowid = 0; rowid < num_rows; rowid++) {
    int populate_value = rowid;
    if (mutate) populate_value *= 3;

    storage::Tuple tuple(schema, allocate);

    if (group_by) {
      // First column has only two distinct values
      tuple.SetValue(0, ValueFactory::GetIntegerValue(PopulatedValue(
                            int(populate_value / (num_rows / 2)), 0)),
                     testing_pool);

    } else {
      // First column is unique in this case
      tuple.SetValue(
          0, ValueFactory::GetIntegerValue(PopulatedValue(populate_value, 0)),
          testing_pool);
    }

    // In case of random, make sure this column has duplicated values
    tuple.SetValue(
        1, ValueFactory::GetIntegerValue(PopulatedValue(
               random ? std::rand() % (num_rows / 3) : populate_value, 1)),
        testing_pool);

    tuple.SetValue(2, ValueFactory::GetDoubleValue(PopulatedValue(
                          random ? std::rand() : populate_value, 2)),
                   testing_pool);

    // In case of random, make sure this column has duplicated values
    Value string_value =
        ValueFactory::GetStringValue(std::to_string(PopulatedValue(
            random ? std::rand() % (num_rows / 3) : populate_value, 3)));
    tuple.SetValue(3, string_value, testing_pool);

    ItemPointer tuple_slot_id = table->InsertTuple(txn, &tuple);
    EXPECT_TRUE(tuple_slot_id.block != INVALID_OID);
    EXPECT_TRUE(tuple_slot_id.offset != INVALID_OID);
    txn->RecordInsert(tuple_slot_id);
  }

  txn_manager.CommitTransaction();
}
storage::Tuple *ExecutorTestsUtil::GetTuple(storage::DataTable *table,
                                            oid_t tuple_id, VarlenPool *pool) {
  storage::Tuple *tuple = new storage::Tuple(table->GetSchema(), true);
  tuple->SetValue(0, ValueFactory::GetIntegerValue(PopulatedValue(tuple_id, 0)),
                  pool);
  tuple->SetValue(1, ValueFactory::GetIntegerValue(PopulatedValue(tuple_id, 1)),
                  pool);
  tuple->SetValue(2, ValueFactory::GetDoubleValue(PopulatedValue(tuple_id, 2)),
                  pool);
  tuple->SetValue(3, ValueFactory::GetStringValue("12345"), pool);

  return tuple;
}
/**
 * @brief  Populates the tiles in the given tile-group in a specific manner.
 * @param tile_group Tile-group to populate with values.
 * @param num_rows Number of tuples to insert.
 */
void ExecutorTestsUtil::PopulateTiles(
    std::shared_ptr<storage::TileGroup> tile_group, int num_rows) {
  // Create tuple schema from tile schemas.
  std::vector<catalog::Schema> &tile_schemas = tile_group->GetTileSchemas();
  std::unique_ptr<catalog::Schema> schema(
      catalog::Schema::AppendSchemaList(tile_schemas));

  // Ensure that the tile group is as expected.
  assert(schema->GetColumnCount() == 4);

  // Insert tuples into tile_group.
  auto &txn_manager = concurrency::TransactionManager::GetInstance();
  const bool allocate = true;
  auto txn = txn_manager.BeginTransaction();
  const txn_id_t txn_id = txn->GetTransactionId();
  const cid_t commit_id = txn->GetCommitId();
  auto testing_pool = TestingHarness::GetInstance().GetTestingPool();

  for (int col_itr = 0; col_itr < num_rows; col_itr++) {
    storage::Tuple tuple(schema.get(), allocate);
    tuple.SetValue(0, ValueFactory::GetIntegerValue(PopulatedValue(col_itr, 0)),
                   testing_pool);
    tuple.SetValue(1, ValueFactory::GetIntegerValue(PopulatedValue(col_itr, 1)),
                   testing_pool);
    tuple.SetValue(2, ValueFactory::GetDoubleValue(PopulatedValue(col_itr, 2)),
                   testing_pool);
    Value string_value = ValueFactory::GetStringValue(
        std::to_string(PopulatedValue(col_itr, 3)));
    tuple.SetValue(3, string_value, testing_pool);

    oid_t tuple_slot_id = tile_group->InsertTuple(txn_id, &tuple);
    tile_group->CommitInsertedTuple(tuple_slot_id, txn_id, commit_id);
  }

  txn_manager.CommitTransaction();
}