//===----------------------------------------------------------------------===//
//                         DuckDB
//
// duckdb/common/multi_file/base_file_reader.hpp
//
//
//===----------------------------------------------------------------------===//

#pragma once

#include "duckdb/common/common.hpp"
#include "duckdb/common/types/value.hpp"
#include "duckdb/common/column_index.hpp"
#include "duckdb/planner/table_filter.hpp"
#include "duckdb/common/optional_idx.hpp"
#include "duckdb/common/multi_file/multi_file_data.hpp"
#include "duckdb/planner/expression.hpp"
#include "duckdb/common/open_file_info.hpp"

namespace duckdb {

class BaseStatistics;
class BaseUnionData;
struct GlobalTableFunctionState;
struct LocalTableFunctionState;

//! Parent class of single-file readers - this must be inherited from for readers implementing the MultiFileReader
//! interface
class BaseFileReader : public enable_shared_from_this<BaseFileReader> {
public:
	explicit BaseFileReader(OpenFileInfo file_p) : file(std::move(file_p)) {
	}
	DUCKDB_API virtual ~BaseFileReader() = default;

	//! The file we are reading
	OpenFileInfo file;
	//! (Optionally) The file index (generated by the multi file reader)
	optional_idx file_list_idx;
	//! The set of columns for the current file
	vector<MultiFileColumnDefinition> columns;
	//! The column ids to read from the file
	MultiFileLocalColumnIds<MultiFileLocalColumnId> column_ids;
	//! The column indexes to read from the file
	vector<ColumnIndex> column_indexes;
	//! The set of table filters (adjusted to local indexes)
	unique_ptr<TableFilterSet> filters;
	//! Expression to execute for a given column (BEFORE executing the filter)
	//! NOTE: this is only set when we have filters - it can be ignored for readers that don't have filter pushdown
	unordered_map<column_t, unique_ptr<Expression>> expression_map;
	//! The final types for various expressions - this is ONLY used if UseCastMap() is explicitly enabled
	unordered_map<column_t, LogicalType> cast_map;

	//! (Optionally) The deletion filter (generated by the multi file reader)
	unique_ptr<DeleteFilter> deletion_filter;

public:
	const vector<MultiFileColumnDefinition> &GetColumns() const {
		return columns;
	}
	const string &GetFileName() {
		return file.path;
	}
	virtual bool UseCastMap() const {
		//! Whether or not to push casts into the cast map
		return false;
	}
	//! Adds a virtual column to be projected at the end
	virtual void AddVirtualColumn(column_t virtual_column_id) {
		throw InternalException("Reader %s does not support AddVirtualColumn", GetReaderType());
	}

public:
	DUCKDB_API virtual shared_ptr<BaseUnionData> GetUnionData(idx_t file_idx);
	//! Get statistics for a specific column
	DUCKDB_API virtual unique_ptr<BaseStatistics> GetStatistics(ClientContext &context, const string &name);
	//! Prepare reader for scanning
	DUCKDB_API virtual void PrepareReader(ClientContext &context, GlobalTableFunctionState &);

	virtual bool TryInitializeScan(ClientContext &context, GlobalTableFunctionState &gstate,
	                               LocalTableFunctionState &lstate) = 0;
	//! Scan a chunk from the read state
	virtual AsyncResult Scan(ClientContext &context, GlobalTableFunctionState &global_state,
	                         LocalTableFunctionState &local_state, DataChunk &chunk) = 0;
	//! Finish scanning a given file
	DUCKDB_API virtual void FinishFile(ClientContext &context, GlobalTableFunctionState &gstate);
	//! Get progress within a given file
	DUCKDB_API virtual double GetProgressInFile(ClientContext &context);

	virtual string GetReaderType() const = 0;

public:
	template <class TARGET>
	TARGET &Cast() {
		DynamicCastCheck<TARGET>(this);
		return reinterpret_cast<TARGET &>(*this);
	}
	template <class TARGET>
	const TARGET &Cast() const {
		DynamicCastCheck<TARGET>(this);
		return reinterpret_cast<const TARGET &>(*this);
	}
};

//! Parent class of file reader options
class BaseFileReaderOptions {
public:
	DUCKDB_API virtual ~BaseFileReaderOptions() = default;

public:
	template <class TARGET>
	TARGET &Cast() {
		DynamicCastCheck<TARGET>(this);
		return reinterpret_cast<TARGET &>(*this);
	}
	template <class TARGET>
	const TARGET &Cast() const {
		DynamicCastCheck<TARGET>(this);
		return reinterpret_cast<const TARGET &>(*this);
	}
};

//! Parent class of union data - used for the UNION BY NAME. This is effectively a cache that is kept around per file
//! that can be used to speed up opening the same file again afterwards - to avoid doing double work.
class BaseUnionData {
public:
	explicit BaseUnionData(OpenFileInfo file_p) : file(std::move(file_p)) {
	}
	DUCKDB_API virtual ~BaseUnionData() = default;

	OpenFileInfo file;
	shared_ptr<BaseFileReader> reader;
	vector<string> names;
	vector<LogicalType> types;

	const string &GetFileName() {
		return file.path;
	}

	virtual unique_ptr<BaseStatistics> GetStatistics(ClientContext &context, const string &name);

public:
	template <class TARGET>
	TARGET &Cast() {
		DynamicCastCheck<TARGET>(this);
		return reinterpret_cast<TARGET &>(*this);
	}
	template <class TARGET>
	const TARGET &Cast() const {
		DynamicCastCheck<TARGET>(this);
		return reinterpret_cast<const TARGET &>(*this);
	}
};

} // namespace duckdb
