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

#ifndef READER_TSFILE_READER_H
#define READER_TSFILE_READER_H

#include "common/row_record.h"
#include "common/tsfile_common.h"
#include "expression.h"
#include "file/read_file.h"
#include "reader/table_query_executor.h"
namespace storage {
class TsFileExecutor;
class ReadFile;
class ResultSet;
struct MeasurementSchema;
}  // namespace storage

namespace storage {

extern int libtsfile_init();
extern void libtsfile_destroy();
/**
 * @brief TsfileReader provides the ability to query all files with the suffix
 * .tsfile
 *
 * TsfileReader is designed to query .tsfile files, it accepts tree model
 * queries and table model queries, and supports querying metadata such as
 * TableSchema and TimeseriesSchema.
 */
class TsFileReader {
   public:
    TsFileReader();
    ~TsFileReader();
    /**
     * @brief open the tsfile
     *
     * @param file_path the path of the tsfile which will be opened
     * @return Returns 0 on success, or a non-zero error code on failure.
     */
    int open(const std::string& file_path);
    /**
     * @brief close the tsfile, this method should be called after the
     * query is finished
     *
     * @return Returns 0 on success, or a non-zero error code on failure.
     */
    int close();
    /**
     * @brief query the tsfile by the query expression,Users can construct
     * their own query expressions to query tsfile
     *
     * @param [in] qe the query expression
     * @param [out] ret_qds the result set
     * @return Returns 0 on success, or a non-zero error code on failure.
     */
    int query(storage::QueryExpression* qe, ResultSet*& ret_qds);
    /**
     * @brief query the tsfile by the path list, start time and end time
     * this method is used to query the tsfile by the tree model.
     *
     * @param [in] path_list the path list
     * @param [in] start_time the start time
     * @param [in] end_time the end time
     * @param [out] result_set the result set
     */
    int query(std::vector<std::string>& path_list, int64_t start_time,
              int64_t end_time, ResultSet*& result_set);
    /**
     * @brief query the tsfile by the table name, columns names, start time
     * and end time. this method is used to query the tsfile by the table
     * model.
     *
     * @param [in] table_name the table name
     * @param [in] columns_names the columns names
     * @param [in] start_time the start time
     * @param [in] end_time the end time
     * @param [out] result_set the result set
     * @param [in] batch_size <= 0 means row-by-row return mode,
     *             > 0 means return TsBlock with the specified block size.
     */
    int query(const std::string& table_name,
              const std::vector<std::string>& columns_names, int64_t start_time,
              int64_t end_time, ResultSet*& result_set, int batch_size = -1);

    /**
     * @brief query the tsfile by the table name, columns names, start time
     * and end time, tag filter. this method is used to query the tsfile by the
     * table model.
     *
     * @param [in] table_name the table name
     * @param [in] columns_names the columns names
     * @param [in] start_time the start time
     * @param [in] end_time the end time
     * @param [in] tag_filter the tag filter
     * @param [out] result_set the result set
     */
    int query(const std::string& table_name,
              const std::vector<std::string>& columns_names, int64_t start_time,
              int64_t end_time, ResultSet*& result_set, Filter* tag_filter,
              int batch_size = 0);

    /**
     * @brief Query tree-model time series by row with offset and limit.
     *
     * @param path_list  Full paths (device.measurement) to query.
     * @param offset     Number of leading rows to skip (>= 0).
     * @param limit      Maximum rows to return. < 0 means unlimited.
     * @param[out] result_set  The result set containing query results.
     * @return Returns 0 on success, or a non-zero error code on failure.
     */
    int queryByRow(std::vector<std::string>& path_list, int offset, int limit,
                   ResultSet*& result_set);

    /**
     * @brief Query table-model data by row with offset/limit pushdown.
     *
     * For dense devices (all columns have the same row count),
     * offset/limit is pushed down to chunk/page level via SSI,
     * skipping entire chunks/pages without decoding.
     * For sparse devices, offset/limit is applied at the row-merge level.
     * Entire devices can be skipped when their total row count
     * falls within the offset range.
     *
     * @param table_name     Table to query.
     * @param column_names   Columns to select.
     * @param offset         Number of leading rows to skip (>= 0).
     * @param limit          Maximum rows to return. < 0 means unlimited.
     * @param[out] result_set  The result set containing query results.
     * @param tag_filter     Optional tag filter for filtering by tag columns.
     * @return Returns 0 on success, or a non-zero error code on failure.
     */
    int queryByRow(const std::string& table_name,
                   const std::vector<std::string>& column_names, int offset,
                   int limit, ResultSet*& result_set,
                   Filter* tag_filter = nullptr, int batch_size = 0);

    int query_table_on_tree(const std::vector<std::string>& measurement_names,
                            int64_t star_time, int64_t end_time,
                            ResultSet*& result_set);
    /**
     * @brief destroy the result set, this method should be called after the
     * query is finished and result_set
     *
     * @param qds the result set
     */
    void destroy_query_data_set(ResultSet* qds);
    ResultSet* read_timeseries(
        const std::shared_ptr<IDeviceID>& device_id,
        const std::vector<std::string>& measurement_name);
    /**
     * @brief get all devices in the tsfile
     *
     * @param table_name the table name
     * @return std::vector<std::shared_ptr<IDeviceID>> the device id list
     */
    std::vector<std::shared_ptr<IDeviceID>> get_all_devices(
        std::string table_name);

    /**
     * @brief get all devices in the tsfile
     *
     * @return std::vector<std::shared_ptr<IDeviceID>> the device id list
     */
    std::vector<std::shared_ptr<IDeviceID>> get_all_device_ids();

    /**
     * @brief Get all device IDs in the file (same as get_all_device_ids).
     *
     * @return std::vector<std::shared_ptr<IDeviceID>> the device list
     */
    std::vector<std::shared_ptr<IDeviceID>> get_all_devices();

    /**
     * @brief get the timeseries schema by the device id and measurement name
     *
     * @param [in] device_id the device id
     * @param [out] result std::vector<MeasurementSchema> the measurement schema
     * list
     * @return Returns 0 on success, or a non-zero error code on failure.
     */
    int get_timeseries_schema(std::shared_ptr<IDeviceID> device_id,
                              std::vector<MeasurementSchema>& result);

    /**
     * @brief Get timeseries metadata for specified devices.
     *
     * Only devices that exist in the file are included in the result.
     * If device_ids is empty, returns an empty map.
     *
     * @param device_ids device list to query
     * @return map: IDeviceID -> list of timeseries metadata (only existing)
     */
    DeviceTimeseriesMetadataMap get_timeseries_metadata(
        const std::vector<std::shared_ptr<IDeviceID>>& device_ids);

    /**
     * @brief Get timeseries metadata for all devices in the file.
     *
     * @return map: IDeviceID -> list of timeseries metadata
     */
    DeviceTimeseriesMetadataMap get_timeseries_metadata();

    /**
     * @brief get the table schema by the table name
     *
     * @param table_name the table name
     * @return std::shared_ptr<TableSchema> the table schema
     */
    std::shared_ptr<TableSchema> get_table_schema(
        const std::string& table_name);
    /**
     * @brief get all table schemas in the tsfile
     *
     * @return std::vector<std::shared_ptr<TableSchema>> the table schema list
     */
    std::vector<std::shared_ptr<TableSchema>> get_all_table_schemas();

   private:
    int ensure_table_query_executor(int batch_size);
    int get_timeseries_metadata_impl(
        std::shared_ptr<IDeviceID> device_id,
        std::vector<std::shared_ptr<ITimeseriesIndex>>& result);
    int get_all_devices(std::vector<std::shared_ptr<IDeviceID>>& device_ids,
                        std::shared_ptr<MetaIndexNode> index_node,
                        common::PageArena& pa);
    storage::ReadFile* read_file_;
    storage::TsFileExecutor* tsfile_executor_;
    storage::TableQueryExecutor* table_query_executor_;
    int table_query_executor_batch_size_;
    common::PageArena tsfile_reader_meta_pa_;
};

}  // namespace storage

#endif  // READER_TSFILE_READER
