/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#ifndef _GSM_APPLICATION_H_
#define _GSM_APPLICATION_H_

#include <gtkmm.h>
#include <glibtop/cpu.h>
#include <libhandy-1/handy.h>

#include <algorithm>

struct LoadGraph;

#include "smooth_refresh.h"
#include "prettytable.h"
#include "legacy/treeview.h"
#include "util.h"

static const unsigned MIN_UPDATE_INTERVAL =   1 * 1000;
static const unsigned MAX_UPDATE_INTERVAL = 100 * 1000;


enum ProcmanTab
{
    PROCMAN_TAB_PROCESSES,
    PROCMAN_TAB_RESOURCES,
    PROCMAN_TAB_DISKS
};


struct ProcConfig
  : private procman::NonCopyable
{
    ProcConfig()
      : update_interval(0),
	graph_update_interval(0),
	disks_update_interval(0),
	graph_data_points(0),
	mem_color(),
	swap_color(),
	net_in_color(),
	net_out_color(),
	bg_color(),
	frame_color(),
	num_cpus(0),
	solaris_mode(false),
	process_memory_in_iec(true),
	logarithmic_scale(false),
	draw_stacked(false),
	draw_smooth(true),
	resources_memory_in_iec(true),
	network_in_bits(false),
	network_total_unit(false),
	network_total_in_bits(false)
    {
      std::fill(&this->cpu_color[0], &this->cpu_color[GLIBTOP_NCPU], GdkRGBA());
    }

    int             update_interval;
    int             graph_update_interval;
    int             disks_update_interval;
    int             graph_data_points;
    GdkRGBA         cpu_color[GLIBTOP_NCPU];
    GdkRGBA         mem_color;
    GdkRGBA         swap_color;
    GdkRGBA         net_in_color;
    GdkRGBA         net_out_color;
    GdkRGBA         bg_color;
    GdkRGBA         frame_color;
    gint            num_cpus;
    bool solaris_mode;
    bool process_memory_in_iec;
    bool logarithmic_scale;
    bool draw_stacked;
    bool draw_smooth;
    bool resources_memory_in_iec;
    bool network_in_bits;
    bool network_total_unit;
    bool network_total_in_bits;
};



struct MutableProcInfo
  : private procman::NonCopyable
{

MutableProcInfo()
  : vmsize(0UL),
    memres(0UL),
    memshared(0UL),
    memwritable(0UL),
    mem(0UL),
#ifdef HAVE_WNCK
    memxserver(0UL),
#endif
    start_time(0UL),
    cpu_time(0ULL),
    disk_read_bytes_total(0ULL),
    disk_write_bytes_total(0ULL),
    disk_read_bytes_current(0ULL),
    disk_write_bytes_current(0ULL),
    status(0U),
    pcpu(0),
    nice(0)
    {
    }

    std::string user;

    std::string wchan;

    // all these members are filled with libgtop which uses
    // guint64 (to have fixed size data) but we don't need more
    // than an unsigned long (even for 32bit apps on a 64bit
    // kernel) as these data are amounts, not offsets.
    gulong vmsize;
    gulong memres;
    gulong memshared;
    gulong memwritable;
    gulong mem;

#ifdef HAVE_WNCK
    // wnck gives an unsigned long
    gulong memxserver;
#endif

    gulong start_time;
    guint64 cpu_time;
    guint64 disk_read_bytes_total;
    guint64 disk_write_bytes_total;
    guint64 disk_read_bytes_current;
    guint64 disk_write_bytes_current;
    guint status;
    gdouble pcpu;
    gint nice;
    std::string cgroup_name;

    std::string unit;
    std::string session;
    std::string seat;

    std::string owner;
};


class ProcInfo
: public MutableProcInfo
{
  public:
    ProcInfo& operator=(const ProcInfo&) = delete;
    ProcInfo(const ProcInfo&) = delete;
    ProcInfo(pid_t pid);
    // adds one more ref to icon
    void set_icon(Glib::RefPtr<Gdk::Pixbuf> icon);
    void set_user(guint uid);
    std::string lookup_user(guint uid);

    GtkTreeIter     node;
    Glib::RefPtr<Gdk::Pixbuf> pixbuf;
    std::string     tooltip;
    std::string     name;
    std::string     arguments;

    std::string     security_context;

    const pid_t     pid;
    pid_t           ppid;
    guint           uid;
};

class ProcList {
    // TODO: use a set instead
    // sorted by pid. The map has a nice property : it is sorted
    // by pid so this helps a lot when looking for the parent node
    // as ppid is nearly always < pid.
    typedef std::map<pid_t, ProcInfo> List;
    List data;
    std::mutex data_lock;
public:
    std::map<pid_t, unsigned long> cpu_times;
    typedef List::iterator Iterator;
    Iterator begin() { return std::begin(data); }
    Iterator end() { return std::end(data); }
    Iterator erase(Iterator it) {
        std::lock_guard<std::mutex> lg(data_lock);
        return data.erase(it);
    }
    ProcInfo* add(pid_t pid) { return &data.emplace(std::piecewise_construct, std::forward_as_tuple(pid), std::forward_as_tuple(pid)).first->second; }
    void clear() { return data.clear(); }

    ProcInfo* find(pid_t pid);
};

class GsmApplication : public Gtk::Application, private procman::NonCopyable

{
private:
    void load_settings();
    void load_resources();

    void on_preferences_activate(const Glib::VariantBase&);
    void on_lsof_activate(const Glib::VariantBase&);
    void on_help_activate(const Glib::VariantBase&);
    void on_quit_activate(const Glib::VariantBase&);
protected:
    GsmApplication();
public:
    static Glib::RefPtr<GsmApplication> get ();

    void save_config();
    void shutdown();

    ProcList         processes;
    GsmTreeView      *tree;
    GtkRevealer      *proc_actionbar_revealer;
    GtkMenu          *popup_menu;
    GsmTreeView      *disk_list;
    GtkStack         *stack;
    GtkButton        *refresh_button;
    GtkMenuButton    *process_menu_button;
    GtkMenuButton    *window_menu_button;
    GtkButton        *end_process_button;
    GtkButton        *search_button;
    GtkSearchEntry   *search_entry;
    GtkSearchBar     *search_bar;
    ProcConfig        config;
    LoadGraph        *cpu_graph;
    LoadGraph        *mem_graph;
    LoadGraph        *net_graph;
    gint              cpu_label_fixed_width;
    gint              net_label_fixed_width;
    GtkTreeSelection *selection;
    guint             timeout;
    guint             disk_timeout;

    GtkTreePath      *top_of_tree;
    gdouble          last_vscroll_max;
    gdouble          last_vscroll_value;

    PrettyTable      *pretty_table;

    Glib::RefPtr<Gio::Settings> settings;
    HdyApplicationWindow *main_window;

    unsigned         frequency;

    SmoothRefresh    *smooth_refresh;

    guint64           cpu_total_time;
    guint64           cpu_total_time_last;

protected:
    virtual void on_activate();
    virtual int on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine>& command_line);
    virtual void on_startup();
};

#endif /* _GSM_APPLICATION_H_ */
