/**
 * @brief Create a physical tile
 * @param
 * @return Physical tile
 */
std::unique_ptr<storage::Tile> LogicalTile::Materialize() {
  // Create new schema according underlying physical tile
  std::unique_ptr<catalog::Schema> source_tile_schema(GetPhysicalSchema());

  // Get the number of tuples within this logical tiles
  const int num_tuples = GetTupleCount();

  //const catalog::Schema *output_schema;
  std::unordered_map<oid_t, oid_t> old_to_new_cols;
  oid_t column_count = source_tile_schema->GetColumnCount();
  for (oid_t col = 0; col < column_count; col++) {
    old_to_new_cols[col] = col;
  }

  // Generate mappings.
  std::unordered_map<storage::Tile *, std::vector<oid_t>> tile_to_cols;
  GenerateTileToColMap(old_to_new_cols, tile_to_cols);

  // Create new physical tile.
  std::unique_ptr<storage::Tile> dest_tile(
      storage::TileFactory::GetTempTile(*source_tile_schema, num_tuples));

  // Proceed to materialize logical tile by physical tile at a time.
  MaterializeByTiles(old_to_new_cols, tile_to_cols,
                     dest_tile.get());

  // Wrap physical tile in logical tile.
  return std::move(dest_tile);
}
/**
 * @brief Create projected tuples based on one or two input.
 * Newly-created physical tiles are needed.
 *
 * @return true on success, false otherwise.
 */
bool ProjectionExecutor::DExecute() {
  PL_ASSERT(project_info_);
  PL_ASSERT(schema_);
  PL_ASSERT(children_.size() == 1);

  // NOTE: We only handle 1 child for now
  if (children_.size() == 1) {
    LOG_TRACE("Projection : child 1 ");

    // Execute child
    auto status = children_[0]->Execute();
    if (false == status) return false;

    // Get input from child
    std::unique_ptr<LogicalTile> source_tile(children_[0]->GetOutput());
    auto num_tuples = source_tile->GetTupleCount();

    // Create new physical tile where we store projected tuples
    std::shared_ptr<storage::Tile> dest_tile(
        storage::TileFactory::GetTempTile(*schema_, num_tuples));

    // Create projections tuple-at-a-time from original tile
    oid_t new_tuple_id = 0;
    for (oid_t old_tuple_id : *source_tile) {
      storage::Tuple *buffer = new storage::Tuple(schema_, true);
      expression::ContainerTuple<LogicalTile> tuple(source_tile.get(),
                                                    old_tuple_id);
      project_info_->Evaluate(buffer, &tuple, nullptr, executor_context_);

      // Insert projected tuple into the new tile
      dest_tile.get()->InsertTuple(new_tuple_id, buffer);

      delete buffer;
      new_tuple_id++;
    }

    // Wrap physical tile in logical tile and return it
    SetOutput(LogicalTileFactory::WrapTiles({dest_tile}));

    return true;
  }

  return false;
}
/**
 * @brief Create a physical tile for the given logical tile
 * @param source_tile Source tile from which the physical tile is created
 * @return a logical tile wrapper for the created physical tile
 */
LogicalTile *MaterializationExecutor::Physify(LogicalTile *source_tile) {
  std::unique_ptr<catalog::Schema> source_tile_schema(
      source_tile->GetPhysicalSchema());
  const int num_tuples = source_tile->GetTupleCount();
  const catalog::Schema *output_schema;
  std::unordered_map<oid_t, oid_t> old_to_new_cols;
  auto node = GetRawNode();

  // Create a default identity mapping node if we did not get one
  if (node == nullptr) {
    assert(source_tile_schema.get());
    output_schema = source_tile_schema.get();

    old_to_new_cols = BuildIdentityMapping(output_schema);
  }
  // Else use the mapping in the given plan node
  else {
    const planner::MaterializationPlan &node =
        GetPlanNode<planner::MaterializationPlan>();
    if (node.GetSchema()) {
      output_schema = node.GetSchema();
      old_to_new_cols = node.old_to_new_cols();
    } else {
      output_schema = source_tile_schema.get();
      old_to_new_cols = BuildIdentityMapping(output_schema);
    }
  }

  // Generate mappings.
  std::unordered_map<storage::Tile *, std::vector<oid_t>> tile_to_cols;
  GenerateTileToColMap(old_to_new_cols, source_tile, tile_to_cols);

  // Create new physical tile.
  std::shared_ptr<storage::Tile> dest_tile(
      storage::TileFactory::GetTempTile(*output_schema, num_tuples));

  // Proceed to materialize logical tile by physical tile at a time.
  MaterializeByTiles(source_tile, old_to_new_cols, tile_to_cols,
                     dest_tile.get());

  // Wrap physical tile in logical tile.
  return LogicalTileFactory::WrapTiles({dest_tile});
}