// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.

// a C++ interface to BOINC GUI RPC

#if !defined(_WIN32) || defined (__CYGWIN__)
#include <stdio.h>
#include <string>
#include <vector>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <locale.h>
#endif

#include "miofile.h"
#include "prefs.h"
#include "hostinfo.h"
#include "common_defs.h"

struct GUI_URL {
    std::string name;
    std::string description;
    std::string url;

    int parse(MIOFILE&);
    void print();
};

// statistics at a specific day
//
struct DAILY_STATS {
    double user_total_credit;
    double user_expavg_credit;
    double host_total_credit;
    double host_expavg_credit;
    double day;

    int parse(MIOFILE&);
};


class PROJECT_LIST_ENTRY {
public:
    std::string name;
    std::string url;
    std::string general_area;
    std::string specific_area;
    std::string description;
    std::string home;
    std::string image;
    double rand;

    PROJECT_LIST_ENTRY();
    ~PROJECT_LIST_ENTRY();

    int parse(XML_PARSER&);
    void clear();

    bool operator<(const PROJECT_LIST_ENTRY& compare);
};

class PROJECT {
public:
    std::string master_url;
    double resource_share;
    std::string project_name;
    std::string user_name;
    std::string team_name;
    std::vector<GUI_URL> gui_urls;
    double user_total_credit;
    double user_expavg_credit;
    double host_total_credit;      // as reported by server
    double host_expavg_credit;     // as reported by server
    double disk_usage;
    int nrpc_failures;          // # of consecutive times we've failed to
                                // contact all scheduling servers
    int master_fetch_failures;
    double min_rpc_time;           // earliest time to contact any server
    double short_term_debt;
    double long_term_debt;
    double duration_correction_factor;

    bool master_url_fetch_pending; // need to fetch and parse the master URL
    int sched_rpc_pending;      // need to contact scheduling server
    int rr_sim_deadlines_missed;
    bool non_cpu_intensive;
    bool suspended_via_gui;
    bool dont_request_more_work;
    bool scheduler_rpc_in_progress;
    bool attached_via_acct_mgr;
    bool detach_when_done;
    bool ended;
    double project_files_downloaded_time;
        // when the last project file download was finished
        // (i.e. the time when ALL project files were finished downloading)
    double last_rpc_time;
        // when the last successful scheduler RPC finished
    std::vector<DAILY_STATS> statistics; // credit data over the last x days

    // NOTE: if you add any data items above,
    // update parse(), copy() and clear() to include them!!

    PROJECT();
    ~PROJECT();

    int parse(MIOFILE&);
    void print();
    void clear();
    void get_name(std::string&);
    void copy(PROJECT&);        // copy to this object

    // temp - keep track of whether or not this record needs to be deleted
    bool flag_for_delete;
};

class APP {
public:
    std::string name;
    std::string user_friendly_name;
    PROJECT* project;

    APP();
    ~APP();

    int parse(MIOFILE&);
    void print();
    void clear();
};

class APP_VERSION {
public:
    std::string app_name;
    int version_num;
    std::string plan_class;
    APP* app;
    PROJECT* project;

    APP_VERSION();
    ~APP_VERSION();

    int parse(MIOFILE&);
    void print();
    void clear();
};

class WORKUNIT {
public:
    std::string name;
    std::string app_name;
    int version_num;
    double rsc_fpops_est;
    double rsc_fpops_bound;
    double rsc_memory_bound;
    double rsc_disk_bound;
    PROJECT* project;
    APP* app;
    APP_VERSION* avp;

    WORKUNIT();
    ~WORKUNIT();

    int parse(MIOFILE&);
    void print();
    void clear();
};

class RESULT {
public:
    std::string name;
    std::string wu_name;
    std::string project_url;
    int report_deadline;
    bool ready_to_report;
    bool got_server_ack;
    double final_cpu_time;
    int state;
    int scheduler_state;
    int exit_status;
    int signal;
    std::string stderr_out;
    bool suspended_via_gui;
    bool project_suspended_via_gui;

    // the following defined if active
    bool active_task;
    int active_task_state;
    int app_version_num;
    double checkpoint_cpu_time;
    double current_cpu_time;
    double fraction_done;
    double swap_size;
    double working_set_size_smoothed;
    double estimated_cpu_time_remaining;
    bool supports_graphics;
    int graphics_mode_acked;
    bool too_large;
    bool needs_shmem;
    bool edf_scheduled;
    std::string graphics_exec_path;
    std::string slot_path;
    std::string resources;

    APP* app;
    WORKUNIT* wup;
    PROJECT* project;

    RESULT();
    ~RESULT();

    int parse(MIOFILE&);
    void print();
    void clear();
};

class FILE_TRANSFER {
public:
    std::string name;
    std::string project_url;
    std::string project_name;
    double nbytes;
    bool generated_locally;
    bool uploaded;
    bool upload_when_present;
    bool sticky;
    bool pers_xfer_active;
    bool xfer_active;
    int num_retries;
    int first_request_time;
    int next_request_time;
    int status;
    double time_so_far;
    double bytes_xferred;
    double file_offset;
    double xfer_speed;
    std::string hostname;
    PROJECT* project;

    FILE_TRANSFER();
    ~FILE_TRANSFER();

    int parse(MIOFILE&);
    void print();
    void clear();
};

class MESSAGE {
public:
    std::string project;
    int priority;
    int seqno;
    int timestamp;
    std::string body;

    MESSAGE();
    ~MESSAGE();

    int parse(MIOFILE&);
    void print();
    void clear();
};

class GR_PROXY_INFO {
public:
    bool use_http_proxy;
    bool use_socks_proxy;
    bool use_http_authentication;
    int socks_version;
    std::string socks_server_name;
    std::string http_server_name;
    int socks_server_port;
    int http_server_port;
    std::string http_user_name;
    std::string http_user_passwd;
    std::string socks5_user_name;
    std::string socks5_user_passwd;
	std::string noproxy_hosts;

    GR_PROXY_INFO();
    ~GR_PROXY_INFO();

    int parse(MIOFILE&);
    void print();
    void clear();
};

class CC_STATE {
public:
    std::vector<PROJECT*> projects;
    std::vector<APP*> apps;
    std::vector<APP_VERSION*> app_versions;
    std::vector<WORKUNIT*> wus;
    std::vector<RESULT*> results;

    GLOBAL_PREFS global_prefs;  // working prefs, i.e. network + override
    VERSION_INFO version_info;  // populated only if talking to pre-5.6 CC
    bool executing_as_daemon;   // true if Client is running as a service / daemon

    CC_STATE();
    ~CC_STATE();

    PROJECT* lookup_project(std::string&);
    APP* lookup_app(std::string&, std::string&);
    APP* lookup_app(PROJECT*, std::string&);
    APP_VERSION* lookup_app_version(std::string&, std::string&, int);
    APP_VERSION* lookup_app_version(PROJECT*, std::string&, int);
    WORKUNIT* lookup_wu(std::string&, std::string&);
    WORKUNIT* lookup_wu(PROJECT*, std::string&);
    RESULT* lookup_result(std::string&, std::string&);
    RESULT* lookup_result(PROJECT*, std::string&);

    void print();
    void clear();
};

class ALL_PROJECTS_LIST {
public:
    std::vector<PROJECT_LIST_ENTRY*> projects;

    ALL_PROJECTS_LIST();
    ~ALL_PROJECTS_LIST();

    void clear();
    void shuffle();
};

class PROJECTS {
public:
    std::vector<PROJECT*> projects;

    PROJECTS(){}
    ~PROJECTS();

    void print();
    void clear();
};

struct DISK_USAGE {
    std::vector<PROJECT*> projects;
    double d_total;
    double d_free;
    double d_boinc;     // amount used by BOINC itself, not projects
    double d_allowed;   // amount BOINC is allowed to use, total

    DISK_USAGE(){clear();}
    ~DISK_USAGE();

    void print();
    void clear();
};

class RESULTS {
public:
    std::vector<RESULT*> results;

    RESULTS(){}
    ~RESULTS();

    void print();
    void clear();
};

class FILE_TRANSFERS {
public:
    std::vector<FILE_TRANSFER*> file_transfers;

    FILE_TRANSFERS();
    ~FILE_TRANSFERS();

    void print();
    void clear();
};

class MESSAGES {
public:
    std::vector<MESSAGE*> messages;

    MESSAGES();
    ~MESSAGES();

    void print();
    void clear();
};

struct DISPLAY_INFO {
    char window_station[256];   // windows
    char desktop[256];          // windows
    char display[256];          // X11

    DISPLAY_INFO();
    void print_str(char*);
};

struct ACCT_MGR_INFO {
    std::string acct_mgr_name;
    std::string acct_mgr_url;
    bool have_credentials;
    
    ACCT_MGR_INFO();
    ~ACCT_MGR_INFO(){}

    int parse(MIOFILE&);
    void clear();
};

struct PROJECT_ATTACH_REPLY {
    int error_num;
    std::vector<std::string>messages;

    PROJECT_ATTACH_REPLY();
    ~PROJECT_ATTACH_REPLY(){}

    int parse(MIOFILE&);
    void clear();
};

struct ACCT_MGR_RPC_REPLY {
    int error_num;
    std::vector<std::string>messages;

    ACCT_MGR_RPC_REPLY();
    ~ACCT_MGR_RPC_REPLY(){}

    int parse(MIOFILE&);
    void clear();
};

struct PROJECT_INIT_STATUS {
    std::string url;
    std::string name;
    bool has_account_key;

    PROJECT_INIT_STATUS();
    ~PROJECT_INIT_STATUS(){}

    int parse(MIOFILE&);
    void clear();
};

struct PROJECT_CONFIG {
    int error_num;
    std::string name;
    int min_passwd_length;
    bool account_manager;
    bool uses_username;
    bool account_creation_disabled;
    bool client_account_creation_disabled;
	std::string error_msg;
    std::string terms_of_use;
        // if present, show this text in an "accept terms of use?" dialog
        // before allowing attachment to continue.

    PROJECT_CONFIG();
    ~PROJECT_CONFIG();

    int parse(MIOFILE&);
    void clear();
    void print();
};

struct ACCOUNT_IN {
    std::string url;
    std::string email_addr;
        // this is the account identifier (email address or user name)
    std::string user_name;
    std::string passwd;

    ACCOUNT_IN();
    ~ACCOUNT_IN();

    void clear();
};

struct ACCOUNT_OUT {
    int error_num;
	std::string error_msg;
    std::string authenticator;

    ACCOUNT_OUT();
    ~ACCOUNT_OUT();

    int parse(MIOFILE&);
    void clear();
    void print();
};

struct CC_STATUS {
    int network_status;         // values: NETWORK_STATUS_*
    bool ams_password_error;
    bool manager_must_quit;
    int task_suspend_reason;    // bitmap, see common_defs.h
    int network_suspend_reason;
    int task_mode;              // always/auto/never; see common_defs.h
    int network_mode;
    int task_mode_perm;			// same, but permanent version
    int network_mode_perm;
	double task_mode_delay;		// time until perm becomes actual
	double network_mode_delay;
    bool disallow_attach;
    bool simple_gui_only;

    CC_STATUS();
    ~CC_STATUS();

    int parse(MIOFILE&);
    void clear();
    void print();
};

struct SIMPLE_GUI_INFO {
    std::vector<PROJECT*> projects;
    std::vector<RESULT*> results;
    void print();
};

class RPC_CLIENT {
public:
    int sock;
    double start_time;
    double timeout;
    bool retry;
    sockaddr_in addr;

    int send_request(const char*);
    int get_reply(char*&);
    RPC_CLIENT();
    ~RPC_CLIENT();
    int init(const char* host, int port=0);
    int init_asynch(
        const char* host, double timeout, bool retry, int port=GUI_RPC_PORT
    );
        // timeout == how long to wait until give up
        //    If the caller (i.e. BOINC Manager) just launched the core client,
        //    this should be large enough to allow the process to
        //    run and open its listening socket (e.g. 60 sec)
        //    If connecting to a remote client, it should be large enough
        //    for the user to deal with a "personal firewall" popup
        //    (e.g. 60 sec)
        // retry: if true, keep retrying until succeed or timeout.
        //    Use this if just launched the core client.
    int init_poll();
    void close();
    int authorize(const char* passwd);
    int exchange_versions(VERSION_INFO&);
    int get_state(CC_STATE&);
    int get_results(RESULTS&);
    int get_file_transfers(FILE_TRANSFERS&);
    int get_simple_gui_info(SIMPLE_GUI_INFO&);
    int get_simple_gui_info(CC_STATE&, RESULTS&);
    int get_project_status(CC_STATE&);
    int get_project_status(PROJECTS&);
    int get_all_projects_list(ALL_PROJECTS_LIST&);
    int get_disk_usage(DISK_USAGE&);
    int show_graphics(
        const char* project, const char* result_name, int graphics_mode,
        DISPLAY_INFO&
    );
    int project_op(PROJECT&, const char* op);
    int set_run_mode(int mode, double duration);
        // if duration is zero, change is permanent.
        // otherwise, after duration expires,
        // restore last permanent mode
    int set_network_mode(int mode, double duration);
    int get_screensaver_tasks(int& suspend_reason, RESULTS&);
    int run_benchmarks();
    int set_proxy_settings(GR_PROXY_INFO&);
    int get_proxy_settings(GR_PROXY_INFO&);
    int get_messages(int seqno, MESSAGES&);
    int file_transfer_op(FILE_TRANSFER&, const char*);
    int result_op(RESULT&, const char*);
    int get_host_info(HOST_INFO&);
    int quit();
    int acct_mgr_info(ACCT_MGR_INFO&);
    const char* mode_name(int mode);
    int get_statistics(PROJECTS&);
    int network_available();
    int get_project_init_status(PROJECT_INIT_STATUS& pis);

    // the following are asynch operations.
    // Make the first call to start the op,
    // call the second one periodically until it returns zero.
    // TODO: do project update
    //
    int get_project_config(std::string url);
    int get_project_config_poll(PROJECT_CONFIG&);
    int lookup_account(ACCOUNT_IN&);
    int lookup_account_poll(ACCOUNT_OUT&);
    int create_account(ACCOUNT_IN&);
    int create_account_poll(ACCOUNT_OUT&);
    int project_attach(
        const char* url, const char* auth, const char* project_name
    );
    int project_attach_from_file();
    int project_attach_poll(PROJECT_ATTACH_REPLY&);
    int acct_mgr_rpc(
        const char* url, const char* name, const char* passwd,
        bool use_config_file=false
    );
    int acct_mgr_rpc_poll(ACCT_MGR_RPC_REPLY&);

    int get_newer_version(std::string&);
    int read_global_prefs_override();
    int read_cc_config();
    int get_cc_status(CC_STATUS&);
    int get_global_prefs_file(std::string&);
    int get_global_prefs_working(std::string&);
    int get_global_prefs_working_struct(GLOBAL_PREFS&, GLOBAL_PREFS_MASK&);
    int get_global_prefs_override(std::string&);
    int set_global_prefs_override(std::string&);
    int get_global_prefs_override_struct(GLOBAL_PREFS&, GLOBAL_PREFS_MASK&);
    int set_global_prefs_override_struct(GLOBAL_PREFS&, GLOBAL_PREFS_MASK&);
    int set_debts(std::vector<PROJECT>);
};

struct RPC {
    char* mbuf;
    MIOFILE fin;
    RPC_CLIENT* rpc_client;

    RPC(RPC_CLIENT*);
    ~RPC();
    int do_rpc(const char*);
    int parse_reply();
};

struct SET_LOCALE {
    std::string locale;
    inline SET_LOCALE() {
        locale = setlocale(LC_ALL, NULL);
        setlocale(LC_ALL, "C");
    }
    inline ~SET_LOCALE() {
        setlocale(LC_ALL, locale.c_str());
    }
};
