#pragma once
#include <cstdint>
#include <filecomplete.h>
#include <fstream>
#include <map>
#include <memory>
#include <mutex>
#include <net_base.h>
#include <of_util.h>
#include <string>
#include <unordered_map>
#include <util.h>
#include <vector>

using namespace ofen;
struct DownClientInfo {
    std::vector<std::string> files;
    std::string uuid;
    std::string id;
};

enum TransState {
    TRANS_FAILED,
    TRANS_ING,
    TRANS_REDAY,
    TRANS_DONE,
    TRANS_BREAK
};

struct TransInfomation {
    std::string cur_remote_id_;
    std::string cur_remote_file_;
    std::string cur_file_;
    std::fstream file_{};
    uint16_t permissions{};
    uint16_t remote_plat{};
    TransState trans_state_{TRANS_FAILED};
};

constexpr int down_check_wait = 100;   // millsec
class CClient
{
public:
    CClient();
    ~CClient();

public:
    void run(const std::string& ip, const std::string& port, const std::string& config_dir);

public:
    bool get_clients();
    bool cmd_fetch_files(const std::string& param);
    bool cmd_sub_list(const std::string& param);
    bool cmd_clear_submited();
    bool cmd_upload_files(const std::string& param);
    bool down_one_file(int remote_id, const std::string& file, const std::string& local_dir = "");
    void report_trans_ret(TransState state, const std::string& key = "");
    bool cmd_sub_task(const std::string& param, bool is_send);
    bool variable_and_parse_files(const std::string& content, std::map<std::string, std::string>& files);
    bool down_update_file(const std::map<std::string, std::string>& files);
    bool get_dir_files(const std::string& dir, std::string& out, std::string& error);
    bool cmd_ls(const std::string& param);
    bool cmd_down_list(const std::string& param);

private:
    bool send_frame(CFrameBuffer* buf);
    void save_line_his(const std::string& input);
    std::vector<std::string> load_line_his();
    std::string variable_and_reverse_files(const std::string& source);
    bool save_uuid();
    std::string read_uuid();
    void get_id();
    void print_help(bool detail);
    void free_buf_manual(CFrameBuffer* buf);

private:
    void handle_frame(CFrameBuffer* buf);
    void send_file_data_th(const char* keys);
    void hearts();
    void judget_down_active();
    std::string variable_handle(const std::string& task_list_path, const std::string& source, bool is_local);
    std::string handle_user_select(const std::unordered_map<int, std::string>& source, bool is_send);

private:
    std::mutex mutex_;
    std::mutex send_mut_;
    std::string work_key_;
    std::thread hearts_;
    CThreadSleep sleep_;
    bool th_run_{false};
    bool will_receive_{false};
    bool downloading_{false};
    asio::io_context io_context_;
    CMessageInfo msg_info_;
    std::shared_ptr<CTcpClient> client_;
    std::map<int, std::shared_ptr<DownClientInfo>> clients_;
    std::shared_ptr<TransInfomation> down_;
    std::vector<std::thread> ths_;
    std::map<std::string, std::shared_ptr<TransInfomation>> up_;
    std::thread th_down_active_;
    long long cur_file_size_{};
    long long cur_down_size_{};

private:
    std::string list_file_;
    std::string list_server_id_;
    std::string list_server_uuid_;
    std::thread update_list_th_;
    std::string own_id_{};
    std::string config_path_{};
    std::string uuid_path_{};
    std::string uuid_{};
};

class CFileOpr
{
public:
    CFileOpr();
    ~CFileOpr();

public:
    static bool get_file_list(const std::string& input, std::vector<std::string>& out);
};