#ifndef EL__SESSION_SESSION_H
#define EL__SESSION_SESSION_H

#include "bfu/dialog.h"
#include "cache/cache.h"
#include "main/timer.h" /* timer_id_T */
#include "network/state.h"
#include "session/download.h"
#include "session/history.h"
#include "util/lists.h"
#include "viewer/text/vs.h"

struct document_view;
struct link;
struct location;
struct session_status;
struct term_event;
struct terminal_info;
struct terminal;
struct uri;
struct window;

/** Used by delayed_open() and delayed_goto_uri_frame(). */
struct delayed_open {
	struct session *ses;
	struct uri *uri;
	unsigned char *target;
};

enum remote_session_flags {
	SES_REMOTE_NEW_TAB = 1,
	SES_REMOTE_NEW_WINDOW = 2,
	SES_REMOTE_CURRENT_TAB = 4,
	SES_REMOTE_PROMPT_URL = 8,
	SES_REMOTE_PING = 16,
	SES_REMOTE_ADD_BOOKMARK = 32,
	SES_REMOTE_INFO_BOX = 64,
};

/** This is generic frame descriptor, meaningful mainly for ses_*_frame*(). */
struct frame {
	LIST_HEAD(struct frame);

	unsigned char *name;
	int redirect_cnt;

	struct view_state vs;
};

/** Use for keyboard prefixes. */
struct kbdprefix {
	/** This is the repeat count being inserted by user so far.
	 * It is stored intermediately per-session. */
	int repeat_count;

#ifdef CONFIG_MARKS
	/** If the previous key was a mark prefix, this describes what kind
	 * of action are we supposed to do when we receive the next key. */
	enum { KP_MARK_NOTHING, KP_MARK_SET, KP_MARK_GOTO } mark;
#endif
};

struct session;

/** This describes, what are we trying to do right now. We pass this around so
 * that we can use generic scheduler routines and when the control will get
 * back to our subsystem, we will know what are we up to. */
enum task_type {
	TASK_NONE,
	TASK_FORWARD,
	TASK_IMGMAP,
	TASK_RELOAD,
	TASK_HISTORY,
};

struct session_task {
	enum task_type type;
	/* TODO: union --pasky */
	struct {
		unsigned char *frame;
		struct location *location;
	} target;
};

struct session_status {
	unsigned int show_tabs_bar:1;
	unsigned int show_status_bar:1;
	unsigned int show_title_bar:1;

	int force_show_status_bar:2;
	int force_show_title_bar:2;

	unsigned int set_window_title:1;
	unsigned char *last_title;
#ifdef CONFIG_ECMASCRIPT
	unsigned char *window_status;
#endif

#ifdef CONFIG_LEDS
	unsigned int show_leds:1;
	struct led_panel leds;
	struct led *ssl_led;
	struct led *insert_mode_led;
	struct led *ecmascript_led;
	struct led *popup_led;
	struct led *download_led;
#endif
	/** Has the tab been visited yet. */
	unsigned int visited:1;

	/** Is processing file requests. */
	unsigned int processing_file_requests:1;
	unsigned int show_tabs_bar_at_top:1;
};

enum insert_mode {
	INSERT_MODE_LESS,
	INSERT_MODE_ON,
	INSERT_MODE_OFF,
};

enum navigate_mode {
	NAVIGATE_LINKWISE,
	NAVIGATE_CURSOR_ROUTING,
};

/** This is one of the building stones of ELinks architecture --- this structure
 * carries information about the specific ELinks session. Each tab (thus, at
 * least one per terminal, in the normal case) has its own session. Session
 * describes mainly the current browsing and control state, from the currently
 * viewed document through the browsing history of this session to the status
 * bar information. */
struct session {
	LIST_HEAD(struct session);


	/** @name The vital session data
	 * @{ */

	struct window *tab;

	/* Session-specific options */

	struct option *option;


	/** @} @name Browsing history
	 * @{ */

	struct ses_history history;


	/** @} @name The current document
	 * @{ */

	LIST_OF(struct file_to_load) more_files;

	struct download loading;
	struct uri *loading_uri;

	enum cache_mode reloadlevel;
	int redirect_cnt;

	struct document_view *doc_view;
	LIST_OF(struct document_view) scrn_frames;

	/** The URI from which the next start_download() or resume_download()
	 * call should download, or NULL if no such call is pending.
	 *
	 * When the user requests a download, one of those functions
	 * is given as a callback to query_file(), which asks the user
	 * where to save the downloaded file.  The URI cannot be given
	 * to the callback as a parameter because query_file()
	 * supports only one void * parameter for the callback and
	 * that one is already used for the struct session *.
	 * Instead, the URI is saved here before the query_file()
	 * call.  */
	struct uri *download_uri;

	/** The URI which is the referrer to the current loaded document
	 * or NULL if there are no referrer.
	 *
	 * The @c referrer member's sole purpose is to have the information
	 * handy when loading URIs. It is not 'filtered' in anyway at this
	 * level only at the lower ones. */
	struct uri *referrer;


	/** @} @name The current action-in-progress selector
	 * @{ */

	struct session_task task;


	/** @} @name The current browsing state
	 * @{ */

	int search_direction;
	struct kbdprefix kbdprefix;
	int exit_query;
	timer_id_T display_timer;

	/** The text input form insert mode. It is a tristate controlled by the
	 * boolean document.browse.forms.insert_mode option. When disabled we
	 * use modeless insertion and we always insert stuff into the text
	 * input field. When enabled it is possible to switch insertion on and
	 * off using ::ACT_EDIT_ENTER and *_CANCEL. */
	enum insert_mode insert_mode;

	enum navigate_mode navigate_mode;

	unsigned char *search_word;
	unsigned char *last_search_word;


	/** The possibly running type queries (what-to-do-with-that-file?) */
	LIST_OF(struct type_query) type_queries;

	/** The info for status displaying */
	struct session_status status;

	/** @} */
};

extern LIST_OF(struct session) sessions;
extern enum remote_session_flags remote_session_flags;

/** This returns a pointer to the current location inside of the given session.
 * That's nice for encapsulation and already paid out once ;-). */
#define cur_loc(x) ((x)->history.current)

/** Return if we have anything being loaded in this session already.
 * @relates session */
static inline int
have_location(struct session *ses) {
	return !!cur_loc(ses);
}

/** Swaps the current session referrer with the new one passed as @a referrer.
 * @a referrer may be NULL.
 * @relates session */
void set_session_referrer(struct session *ses, struct uri *referrer);

void
print_error_dialog(struct session *ses, struct connection_state state,
		   struct uri *uri, enum connection_priority priority);

void process_file_requests(struct session *);

struct string *encode_session_info(struct string *info,
				   LIST_OF(struct string_list_item) *url_list);

/** @returns zero if the info was remote sessions or if it failed to
 * create any sessions. */
int decode_session_info(struct terminal *term, struct terminal_info *info);

/** Registers a base session and returns its id. Value <= 0 means error. */
int
add_session_info(struct session *ses, struct uri *uri, struct uri *referrer,
		 enum cache_mode cache_mode, enum task_type task);

void done_saved_session_info(void);

struct session *init_session(struct session *ses, struct terminal *term,
	     struct uri *uri, int in_background);

void doc_loading_callback(struct download *, struct session *);

void abort_loading(struct session *, int);
void reload_frame(struct session *, unsigned char *, enum cache_mode);
void reload(struct session *, enum cache_mode);
void load_frames(struct session *, struct document_view *);

struct frame *ses_find_frame(struct session *, unsigned char *);

void free_files(struct session *);
void display_timer(struct session *ses);

/** session_is_loading() is like !!get_current_download() but doesn't take
 * session.req_sent into account.
 * @relates session */
int session_is_loading(struct session *ses);
struct download *get_current_download(struct session *ses);

/** Information about the current document */
unsigned char *get_current_url(struct session *, unsigned char *, size_t);
unsigned char *get_current_title(struct session *, unsigned char *, size_t);

struct link *get_current_session_link(struct session *ses);
struct link *get_current_link_in_view(struct document_view *doc_view);
unsigned char *get_current_link_url(struct session *, unsigned char *, size_t);
unsigned char *get_current_link_name(struct session *, unsigned char *, size_t);

extern LIST_OF(struct questions_entry) questions_queue;
void add_questions_entry(void (*callback)(struct session *, void *), void *data);
void check_questions_queue(struct session *ses);

unsigned char *get_homepage_url(void);

/** Returns current keyboard repeat count and reset it. */
int eat_kbd_repeat_count(struct session *ses);

/** Set current keyboard repeat count to given value and update link
 * highlighting and status bar. */
int set_kbd_repeat_count(struct session *ses, int new_count);

#endif
