/*-
 * Copyright 2016 Vsevolod Stakhov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef RSPAMD_RSPAMD_CONTROL_H
#define RSPAMD_RSPAMD_CONTROL_H

#include "config.h"
#include "mem_pool.h"
#include "contrib/libev/ev.h"

#ifdef  __cplusplus
extern "C" {
#endif

struct rspamd_main;
struct rspamd_worker;

enum rspamd_control_type {
	RSPAMD_CONTROL_STAT = 0,
	RSPAMD_CONTROL_RELOAD,
	RSPAMD_CONTROL_RERESOLVE,
	RSPAMD_CONTROL_RECOMPILE,
	RSPAMD_CONTROL_HYPERSCAN_LOADED,
	RSPAMD_CONTROL_LOG_PIPE,
	RSPAMD_CONTROL_FUZZY_STAT,
	RSPAMD_CONTROL_FUZZY_SYNC,
	RSPAMD_CONTROL_MONITORED_CHANGE,
	RSPAMD_CONTROL_CHILD_CHANGE,
	RSPAMD_CONTROL_MAX
};

enum rspamd_srv_type {
	RSPAMD_SRV_SOCKETPAIR = 0,
	RSPAMD_SRV_HYPERSCAN_LOADED,
	RSPAMD_SRV_MONITORED_CHANGE,
	RSPAMD_SRV_LOG_PIPE,
	RSPAMD_SRV_ON_FORK,
	RSPAMD_SRV_HEARTBEAT,
};

enum rspamd_log_pipe_type {
	RSPAMD_LOG_PIPE_SYMBOLS = 0,
};
#define CONTROL_PATHLEN 400
struct rspamd_control_command {
	enum rspamd_control_type type;
	union {
		struct {
			guint unused;
		} stat;
		struct {
			guint unused;
		} reload;
		struct {
			guint unused;
		} reresolve;
		struct {
			guint unused;
		} recompile;
		struct {
			gchar cache_dir[CONTROL_PATHLEN];
			gboolean forced;
		} hs_loaded;
		struct {
			gchar tag[32];
			gboolean alive;
			pid_t sender;
		} monitored_change;
		struct {
			enum rspamd_log_pipe_type type;
		} log_pipe;
		struct {
			guint unused;
		} fuzzy_stat;
		struct {
			guint unused;
		} fuzzy_sync;
		struct {
			enum {
				rspamd_child_offline,
				rspamd_child_online,
				rspamd_child_terminated,
			} what;
			pid_t pid;
			guint additional;
		} child_change;
	} cmd;
};

struct rspamd_control_reply {
	enum rspamd_control_type type;
	union {
		struct {
			guint conns;
			gdouble uptime;
			gdouble utime;
			gdouble systime;
			gulong maxrss;
		} stat;
		struct {
			guint status;
		} reload;
		struct {
			guint status;
		} reresolve;
		struct {
			guint status;
		} recompile;
		struct {
			guint status;
		} hs_loaded;
		struct {
			guint status;
		} monitored_change;
		struct {
			guint status;
		} log_pipe;
		struct {
			guint status;
			gchar storage_id[MEMPOOL_UID_LEN];
		} fuzzy_stat;
		struct {
			guint status;
		} fuzzy_sync;
	} reply;
};

#define PAIR_ID_LEN 16

struct rspamd_srv_command {
	enum rspamd_srv_type type;
	guint64 id;
	union {
		struct {
			gint af;
			gchar pair_id[PAIR_ID_LEN];
			guint pair_num;
		} spair;
		struct {
			gchar cache_dir[CONTROL_PATHLEN];
			gboolean forced;
		} hs_loaded;
		struct {
			gchar tag[32];
			gboolean alive;
			pid_t sender;
		} monitored_change;
		struct {
			enum rspamd_log_pipe_type type;
		} log_pipe;
		struct {
			pid_t ppid;
			pid_t cpid;
			enum {
				child_create = 0,
				child_dead,
			} state;
		} on_fork;
		struct {
			guint status;
			/* TODO: add more fields */
		} heartbeat;
	} cmd;
};

struct rspamd_srv_reply {
	enum rspamd_srv_type type;
	guint64 id;
	union {
		struct {
			gint code;
		} spair;
		struct {
			gint forced;
		} hs_loaded;
		struct {
			gint status;
		};
		struct {
			enum rspamd_log_pipe_type type;
		} log_pipe;
		struct {
			gint status;
		} on_fork;
		struct {
			gint status;
		} heartbeat;
	} reply;
};

typedef gboolean (*rspamd_worker_control_handler) (struct rspamd_main *rspamd_main,
												   struct rspamd_worker *worker,
												   gint fd,
												   gint attached_fd,
												   struct rspamd_control_command *cmd,
												   gpointer ud);

typedef void (*rspamd_srv_reply_handler) (struct rspamd_worker *worker,
										  struct rspamd_srv_reply *rep, gint rep_fd,
										  gpointer ud);

/**
 * Process client socket connection
 */
void rspamd_control_process_client_socket (struct rspamd_main *rspamd_main,
										   gint fd, rspamd_inet_addr_t *addr);

/**
 * Register default handlers for a worker
 */
void rspamd_control_worker_add_default_cmd_handlers (struct rspamd_worker *worker,
													 struct ev_loop *ev_base);

/**
 * Register custom handler for a specific control command for this worker
 */
void rspamd_control_worker_add_cmd_handler (struct rspamd_worker *worker,
											enum rspamd_control_type type,
											rspamd_worker_control_handler handler,
											gpointer ud);

/**
 * Start watching on srv pipe
 */
void rspamd_srv_start_watching (struct rspamd_main *srv,
								struct rspamd_worker *worker,
								struct ev_loop *ev_base);


/**
 * Send command to srv pipe and read reply calling the specified callback at the
 * end
 */
void rspamd_srv_send_command (struct rspamd_worker *worker,
							  struct ev_loop *ev_base,
							  struct rspamd_srv_command *cmd,
							  gint attached_fd,
							  rspamd_srv_reply_handler handler,
							  gpointer ud);

/**
 * Broadcast srv cmd from rspamd_main to workers
 * @param rspamd_main
 * @param cmd
 * @param except_pid
 */
void rspamd_control_broadcast_srv_cmd (struct rspamd_main *rspamd_main,
									   struct rspamd_control_command *cmd,
									   pid_t except_pid);

/**
 * Returns command from a specified string (case insensitive)
 * @param str
 * @return
 */
enum rspamd_control_type rspamd_control_command_from_string (const gchar *str);

/**
 * Returns command name from it's type
 * @param cmd
 * @return
 */
const gchar *rspamd_control_command_to_string (enum rspamd_control_type cmd);

#ifdef  __cplusplus
}
#endif

#endif