#ifndef TBPLAS_BASE_IO_BASE_H
#define TBPLAS_BASE_IO_BASE_H

#include <string>
#include <vector>

namespace tbplas::base {

// Behaviors:
// 1. read file to cache on opening
// 2. save cache to file on closing
enum class [[nodiscard]] OptTextMode {
    READONLY = 0, // 1: true, 2: false
    UPDATE = 1, // 1: true, 2: true
    RESET = 2, // 1: false, 2: true
};

// Similar as OptTextMode
enum class [[nodiscard]] OptBinMode {
    READONLY = 0,
    UPDATE = 1,
    RESET = 2,
};

enum class [[nodiscard]] OptStatus {
    SUCCESS = 0,
    KEY_NOT_FOUND,
    FORMAT_NOT_MATCH,
    DATASET_NOT_FOUND,
    GROUP_NOT_FOUND,
    ERROR // other errors
};

/**
 * @brief Check if the key is valid.
 *
 * @param[in] key key to check, must be in format of XX.XX.XX
 * @return true if valid
 * @return false otherwise
 */
[[nodiscard]] bool isValidKey(const std::string& key);

/**
 * @brief Check if the string of numbers is valid.
 *
 * @param[in] numbers numbers to check, separated by comma, can have
 *   minus signs and periods
 * @return true if valid
 * @return false otherwise
 *
 * @note This functions is just for testing. But do not remove it.
 */
[[nodiscard]] bool isValidNumbers(const std::string& numbers);

/**
 * @brief Trim heading and trailing characters from string
 *
 * @param[in] stringToTrim string to trim
 * @param[in] charToTrim character which will be trimmed
 * @return trimmed string
 *
 * @note If all the characters in the string is trimmed, an empty string is returned.
 */
[[nodiscard]] std::string trimString(const std::string& stringToTrim, char charToTrim);

/**
 * @brief Split string according to separator.
 *
 * @param[in] stringToSplit string to split
 * @param[in] separator separator
 * @return splitted vector of strings
 *
 * @note The key will be trimmed before splitting. If it consists of all separators, then
 *   an empty vector is returned.
 */
[[nodiscard]] std::vector<std::string> splitString(const std::string& stringToSplit, char separator);

/**
 * @brief Split key into vector of strings according to period.
 *
 * @param[in] key key to split
 * @return splitted vector of strings
 *
 * @note The key will be trimmed before splitting. If it consists of all periods, then
 *   an empty vector is returned.
 */
[[nodiscard]] std::vector<std::string> splitKey(const std::string& key);

/**
 * @brief Split key into vector of doubles according to comma.
 *
 * @param[in] numbers number to split
 * @return splitted vector of doubles
 *
 * @note The key will be trimmed before splitting. If it consists of all commas, then
 *   an empty vector is returned.
 */
[[nodiscard]] std::vector<double> splitDoubles(const std::string& numbers);

/**
 * @brief Split key into vector of integers according to comma.
 *
 * @param[in] numbers number to split
 * @return splitted vector of integers
 *
 * @note The key will be trimmed before splitting. If it consists of all commas, then
 *   an empty vector is returned.
 */
[[nodiscard]] std::vector<int> splitIntegers(const std::string& numbers);

/**
 * @brief Split key in to group and dataset names
 *
 * @param[in] key key to split, must be in format of XX.XX.XX
 * @return vector of 2 strings, with 0th as group name and 1st as dataset name
 *
 * @note
 * 1. If the key consists all of periods, then an empty vector is returned.
 * 2. If key is in format XX, then it is treated as dataset name, while group name
 *    is /.
 */
[[nodiscard]] std::vector<std::string> parseKey(const std::string& key);

/**
 * @brief Base class for XML and HDF5 files.
 *
 */
class BaseFile {
public:
    BaseFile() = default;
    virtual ~BaseFile() = default;

protected:
    /**
     * @brief Check if the file is opened.
     *
     * @return true if opened
     * @return false if closed
     */
    [[nodiscard]] bool isOpen() const;

    /**
     * @brief Check if the file is closed
     *
     * @return true if closed
     * @return false if opened
     */
    [[nodiscard]] bool isClosed() const;

    /**
     * @brief Check if the file is writable
     *
     * @return true if writable
     * @return false if not writable (readonly)
     */
    [[nodiscard]] virtual bool isWritable() const = 0;

    /**
     * @brief Open file for I/O.
     *
     * @param[in] filePath full path to the file
     * @param[in] isOpen if the file is successfully opened
     */
    void open(const std::string& filePath, bool isOpen);

    /**
     * @brief Close the file.
     *
     */
    void close();

    /// @brief Full path to current file, can have directories, pwd by default
    std::string m_CurFilePath;

    /// @brief Whether current file has been opened
    bool m_IsOpen = false;
};

} // namespace tbplas::base

#endif