#ifndef SYNCML_ENGINE_H
#define SYNCML_ENGINE_H

#include "config.h"
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <openssl/ssl.h>
#include <glib.h>
#include <pthread.h>
#include <multisync.h>

#define SYNCML_CMD_UNCONNECTED_TIMEOUT 60
#define SYNCML_CMD_CONNECTED_TIMEOUT 1200

typedef enum {
  ALERT_DISPLAY=100,
  ALERT_TWOWAY=200,
  ALERT_SLOWSYNC=201,
  ALERT_TWOWAYBYSERVER=206,
  ALERT_NEXTMSG=222
} syncml_alert_code;

typedef enum {
  SYNCML_VER_10,
  SYNCML_VER_11
} syncml_version;

typedef enum {
  SYNCML_DISCONNECT_TIMEOUT,
  SYNCML_DISCONNECT_CONNECTIONFAILED,
  SYNCML_DISCONNECT_CLOSED,
  SYNCML_DISCONNECT_DISCONNECT // We ask to disconnect
} syncml_disconnect_reason;

typedef enum {
  SYNCML_STATUS_OK=200,
  SYNCML_STATUS_ADDED=201,
  SYNCML_STATUS_DELETEDWITHOUTARCHIVE=210,
  SYNCML_STATUS_AUTHFORSESSION=212,
  SYNCML_STATUS_INVCRED=401,
  SYNCML_STATUS_NOTFOUND=404,
  SYNCML_STATUS_NOCRED=407,
  SYNCML_STATUS_PERMDENIED=425,
  SYNCML_STATUS_NOTIMPLEMENTED=501,
  SYNCML_STATUS_REFRESHREQ=508,
  SYNCML_STATUS_SYNCERROR=512
} syncml_status_code;

typedef enum {
  SYNCML_ERROR_NOERROR=0,
  SYNCML_ERROR_TIMEOUT=1,
  SYNCML_ERROR_OTHERAUTHFAILED=2,
  SYNCML_ERROR_MYAUTHFAILED=3,
  SYNCML_ERROR_NOPORT=4,
  SYNCML_ERROR_CONNECTIONFAILED=5,
  SYNCML_ERROR_UNKNOWN=6,
  SYNCML_ERROR_BUSY=7
} syncml_error_type;


typedef enum {
  SYNCML_CMD_UNKNOWN,
  SYNCML_CMD_ADD,
  SYNCML_CMD_ALERT,
  SYNCML_CMD_DELETE,
  SYNCML_CMD_GET,
  SYNCML_CMD_MAP,
  SYNCML_CMD_PUT,
  SYNCML_CMD_RESULTS,
  SYNCML_CMD_REPLACE,
  SYNCML_CMD_SYNC,
  SYNCML_CMD_SYNCHDR
} syncml_cmd_type;

typedef enum {
  SYNCML_AUTH_NONE=0,
  SYNCML_AUTH_BASIC,
  SYNCML_AUTH_MD5
} syncml_auth_type;

typedef enum {
  SYNCML_CONN_TYPE_UNKNOWN,
  SYNCML_CONN_TYPE_HTTP,
  SYNCML_CONN_TYPE_HTTPS,
  SYNCML_CONN_TYPE_OBEX,
  SYNCML_CONN_TYPE_WSP
} syncml_conn_type;

typedef struct {
  syncml_auth_type type;
  char *nextnonce;
} syncml_chal;

typedef enum {
  SYNCML_FORMAT_B64
} syncml_format_type;

typedef struct {
  char* myDB;
  char* otherDB;
  char* name; // Display name
  char* lastanchor;
  char* nextanchor;
  char* mylastanchor;
  char* mynextanchor;
  sync_object_type object_type;
  gboolean dosynchronize; // This pair has been marked for synchronization
  gboolean slowsync; // This pair has been marked for slow synchronization
} syncml_db_pair;

// Just for storing DB lastanchors
typedef struct {
  char *db;
  char *mylast;
  char *otherlast;
} syncml_db_anchors;


typedef struct {
  char *lastanchor;
  char *nextanchor;
  char *type;
} syncml_meta;

typedef struct {
  char *targetURI;
  char *sourceURI;
  syncml_meta *meta;
  char *data;
  xmlNodePtr dataptr; // Pointer to the XML doc node for data
} syncml_item;


typedef struct {
  syncml_cmd_type cmd;
  char *cmdID;
  char *data;
  syncml_meta *meta;
  char *targetURI;
  char *sourceURI;
  GList *items;
  GList *mapitems; // List of syncml_items for map commands
  syncml_db_pair *dbpair; // The command refers to this database (do not free)
} syncml_cmd;

typedef struct {
  syncml_cmd_type cmd;
  char *cmdref;
  char *msgref;
  char *sourceref;
  char *targetref;
  syncml_status_code code;
  syncml_meta *meta;
  syncml_chal *chal;
  GList *items;
  syncml_db_pair *dbpair; // The status refers to this database (do not free)
} syncml_status;


typedef enum {
  SYNCML_ENGINE_CMD_NONE = 0,
  SYNCML_ENGINE_CMD_SYNC = 1,
  SYNCML_ENGINE_CMD_SYNC_STATUS = 2,
  SYNCML_ENGINE_CMD_MAP =  3,
  SYNCML_ENGINE_CMD_MAP_STATUS =  4,
  SYNCML_ENGINE_CMD_QUIT = 5,
  SYNCML_ENGINE_CMD_SYNC_SERVERINIT = 6,
  SYNCML_ENGINE_CMD_GETDEVINFO = 7
} syncml_engine_cmd_type;

typedef struct {
  syncml_engine_cmd_type cmd;
  gpointer data;
} syncml_engine_cmd;

typedef enum {
  SYNCML_DATA_TYPE_UNKNOWN = 0,
  SYNCML_DATA_TYPE_VCALENDAR1 = 1,
  SYNCML_DATA_TYPE_VCALENDAR2 = 2,
  SYNCML_DATA_TYPE_VCARD21 = 3,
  SYNCML_DATA_TYPE_VCARD30 = 4
} syncml_data_type;


// An extension of changed_object with some internal data
typedef struct {
  changed_object change;
  syncml_data_type datatype; // A more specific data type
  gboolean sent;  // Has this change yet been sent?
} syncml_changed_object;

// An extension of syncobj_modify_result with some internal data
typedef struct {
  syncobj_modify_result result;
  int listpos; // Integer so that we can sort list in same order as 
               // changed_object list was received.
} syncml_modify_result;

typedef struct {
  char *sourceref;
  syncml_data_type rxpref;
  syncml_data_type txpref;
  GList *tx; // List with syncml_data_type in the data pointer
  GList *rx;
} syncml_datastore;

typedef struct {
  char *manufacturer;
  char *model;
  char *devID;
  GList *datastores; // List of syncml_datastore's
} syncml_devinfo;


typedef struct {
  int sessid, msgid;
  char *othermsgid;
  int cmdid;
  gboolean isserver;
  char *otherURI;
  char *myURI;
  char *user;
  char *passwd;
  char *devID; // My "unique" ID
  char *mynextnonce;
  char *othernextnonce; // The nonce we sent to the other side
  GList *dbanchors; // List of syncml_db_anchors loaded with engine state
  char *statefilename;
  syncml_devinfo *otherdevinfo;
  char *sessionidcookie; // If this cookie is returned by a client, 
                         // the session continues even if a new HTTP session
  int othermaxmsgsize;  // Max message size in bytes of the other end

  gboolean inited; // True if we have successfully initialized SyncML
  gboolean syncstatusreceived; // Flag if sync status received
  gboolean syncreceived; // Flags if sync received
  gboolean mapstatusreceived; // Flag if map status received
  gboolean alertreceived; // Flag if we received an alert
  gboolean initalertsuccess; // Flag if our init alert was successful
  gboolean initalertsent; // Flag if we just sent an init alert
  gboolean syncbyserverreceived; // Flag if server sent us "please sync"
  gboolean devinfreceived; // Flag if we received device info
  gboolean finalreceived; // Flag if <Final> received
  gboolean authok; // The other side is authorized
  gboolean myauthok; // I am authorized
  gboolean respwanted; // I have sent a message which I want a reponse to
  gboolean resendpkg; // Flag if last package must be resent (due to auth)
  gboolean chalsent; // Flag if a challenge has been added
  gboolean mapsent; // Flag if a map command has been sent
  gboolean disconnect; // Flag if auth failed and we should disconnect
  gboolean sendfinal; // Should we send a final tag, or do we have more?
  gboolean moresynccmds; // There are more unsent sync commands.
  syncml_engine_cmd_type waitforcmd; // Wait for this cmd before sending
  syncml_engine_cmd_type task; // Perform this higher level task
  syncml_auth_type chal; // Received challenge
  int credsent; // Number of time I've sent credentials

  syncml_version syncmlversion;
  gboolean lastreq; // If last command came through a request, then TRUE
  gboolean wbxml; // True if we should use WBXML.
  syncml_auth_type defaultauth; // The default authorization scheme we use
  syncml_auth_type usedauth; // The authorization scheme actually used
  GList *db_pairs; // List of syncml_db_pair's
  GList *in_cmds; // List of syncml_cmd''
  GList *obj_cmds; // List of syncml_cmd's (incoming Sync commands)
  GList *map_cmds; // List of syncml_cmd's (incoming Map commands)
  GList *engine_cmds; // List of syncml_engine_cmd's
  xmlNodePtr outCmds; // List of outgoing commands (for resending)
  xmlNodePtr oldOutCmds; // List of outgoing commands (for resending)

  GList *changelist; // List of change_object's
  GList *changeresults; // List of syncml_modify_result's

  xmlDocPtr outDoc;
  xmlNodePtr outBody, outSyncML, outHdr;
  int nocmds; // Number of commands in outBody

  pthread_t thread;
  int socketfd; // Socket fd for server
  int readmsg; // Command msg read fd
  int writemsg; // Command msg write fd
  int connfd; // Established connection fd
  syncml_conn_type conntype; // The current connection type
  int unconnectedtimeout; // Timeout in seconds waiting for response 
  // when no transport connection exists
  int connectedtimeout; // Timeout in seconds waiting for response when connected
  gboolean tcpreuseconnection; // If true, this TCP connection has been 
                               //used already (don't reset state)

  // SSL stuff
  SSL_CTX *sslctx;
  SSL *ssl;

  gpointer userdata; // Normally the syncml_connection
} syncml_state;


gboolean syncml_cmp_node_child(xmlNodePtr node, char *name);
gboolean syncml_get_child_value(xmlDocPtr doc, xmlNodePtr node, 
				char *name, char **data);
char *syncml_build_md5_auth(syncml_state *state, char *nonce);
xmlNodePtr syncml_build_header(syncml_state *state);
xmlNodePtr syncml_build_chal(syncml_state *state);
xmlNodePtr syncml_build_devinf(syncml_state *state);
xmlNodePtr syncml_build_devinfput(syncml_state *state, xmlNodePtr parent,
				  syncml_cmd *refcmd);
xmlNodePtr syncml_build_devinfget(syncml_state *state);
xmlNodePtr syncml_build_alert(syncml_state *state, syncml_db_pair *pair,
			      syncml_alert_code code);
xmlNodePtr syncml_build_status(syncml_state *state,
			       syncml_cmd *cmd, int cmdstatus);
gboolean syncml_build_sync(syncml_state *state, syncml_db_pair *pair);
xmlNodePtr syncml_build_map(syncml_state *state, syncml_db_pair *pair,
			    GList *results);
void syncml_save_db_state(syncml_state *state);
syncml_db_pair *syncml_find_dbpair(syncml_state *state, char *target);
void syncml_test(void);
xmlNodePtr xmlNewChildInt(xmlNodePtr parent, xmlNsPtr ns,
			  xmlChar *name, int content);
char* syncml_cmd_string(syncml_cmd_type type);
syncml_cmd_type syncml_string_cmd(char* cmd);
void syncml_add_init(syncml_state *state, syncml_alert_code code);
void syncml_add_sync(syncml_state *state);
void syncml_add_map(syncml_state *state, GList *results);
void syncml_one_sync_done(syncml_state *state);
 
int syncml_get_node_int(xmlDocPtr doc, xmlNodePtr node);
void syncml_get_node_value(xmlDocPtr doc, xmlNodePtr node, char** ptr);
syncml_chal* syncml_parse_chal(syncml_state *state, xmlDocPtr doc, 
			       xmlNodePtr node);
syncml_data_type syncml_str_to_data_type(char *str);
char *syncml_data_type_to_str(syncml_data_type type);
sync_object_type syncml_data_type_to_objtype(syncml_data_type type);
void syncml_parse_devinf(syncml_state *state, xmlDocPtr doc, 
			 xmlNodePtr devinf);
syncml_meta* syncml_parse_meta(syncml_state *state, xmlDocPtr doc, 
			       xmlNodePtr node);
syncml_item* syncml_parse_item(syncml_state *state, xmlDocPtr doc, 
			       xmlNodePtr node);
syncml_cmd* syncml_parse_cmd(syncml_state *state, xmlDocPtr doc, 
			     xmlNodePtr node);
syncml_status* syncml_parse_status(syncml_state *state, xmlDocPtr doc, 
				   xmlNodePtr node);
void syncml_parse_alert(syncml_state *state, xmlDocPtr doc, xmlNodePtr cmd);
int syncml_parse_node_value(xmlDocPtr doc, xmlNodePtr node,  
			    char **keys, int *vals);
void syncml_parse_syncbody(syncml_state *state, xmlDocPtr doc, 
			   xmlNodePtr body);
void syncml_parse_synchdr(syncml_state *state, xmlDocPtr doc, xmlNodePtr hdr);
void syncml_parse(syncml_state *state, xmlDocPtr doc, xmlNodePtr node);
void syncml_generate_session_cookie(syncml_state* state);
syncml_changed_object *syncml_cmd_to_changed_object(syncml_state *state,
						    syncml_cmd *cmd);
int syncml_get_msg_size(syncml_state *state);

int syncml_compare_syncml_modify_results(syncml_modify_result *r1,
					 syncml_modify_result *r2);
void syncml_free_state(syncml_state* state);
void syncml_free_cmd(syncml_cmd *cmd);
void syncml_free_cmds(GList **cmds);
void syncml_free_dbpair(syncml_db_pair *pair);
syncml_db_pair* syncml_db_pair_new(char *localdb, char *remotedb, 
				   char* lastanchor);
void syncml_free_meta(syncml_meta *meta);
void syncml_free_chal(syncml_chal *chal);
void syncml_free_status(syncml_status *status);
void syncml_free_item(syncml_item *item);
void syncml_free_cmd(syncml_cmd *cmd);
void syncml_free_devinfo(syncml_devinfo *info);
void syncml_free_datastore(syncml_datastore *store);
syncml_devinfo* syncml_copy_devinfo(syncml_devinfo *orig);
syncml_datastore* syncml_copy_datastore(syncml_datastore *orig);


void syncml_parse_msg(syncml_state *state, char *msg, int len);
char* syncml_action(syncml_state *state);
void syncml_disconnected(syncml_state *state, syncml_disconnect_reason reason);
void syncml_reset_state(syncml_state *state);

char *syncml_get_URI_file(char *URI);
syncml_conn_type syncml_get_URI_proto(char *URI);
int syncml_get_URI_port(char *URI);
char *syncml_get_URI_host(char *URI);


#endif
