#include "evolution_sync.h"

/* 
   MultiSync Evolution2 Plugin
   Copyright (C) 2004 Tom Foottit <tom@foottit.com>
   Copyright (C) 2004 Bo Lincoln <lincoln@lysator.liu.se>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2 as
   published by the Free Software Foundation;

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
   SOFTWARE IS DISCLAIMED.
*/

/*
 *  $Id: evolution_sync.c,v 1.2.2.11 2004/11/26 16:06:20 azrael2 Exp $
 */

evo_environment *evo_env_create(sync_pair *handle, connection_type type)
{
	evo_environment *env;
	env = g_malloc0(sizeof(evo_environment));
	env->handle = handle;
	if (!handle)
		return env;
	env->configfile = g_strdup_printf("%s/%ssettings", sync_get_datapath(handle), (type==CONNECTION_TYPE_LOCAL?"local":"remote"));
	env->type = type;
	return env;
}
 
void evo_env_free(evo_environment *env)
{
	if (env->configfile)
		g_free(env->configfile);
	if (env->adressbook_path)
		g_free(env->adressbook_path);
	if (env->calendar_path)
		g_free(env->calendar_path);
	if (env->tasks_path)
		g_free(env->tasks_path);
	env = NULL;
}

void evo_debug(evo_environment *env, int level, char *message, ...)
{
	va_list arglist;
	char *buffer;
	int debuglevel = env->debuglevel;
	debuglevel = 20;
	if (level > debuglevel) { return; }
	va_start(arglist, message);
	g_vasprintf(&buffer, message, arglist);

	switch (level) {
		case 0:
			//Error
			printf("[evo2-sync] ERROR: %s\n", buffer);
			break;
		case 1:
			//Warning
			printf("[evo2-sync] WARNING: %s\n", buffer);
			break;
		case 2:
			//Information
			printf("[evo2-sync] INFORMATION: %s\n",  buffer);
			break;
		case 3:
			//debug
			printf("[evo2-sync] DEBUG: %s\n", buffer);
			break;
		case 4:
			//fulldebug
			printf("[evo2-sync] FULL DEBUG: %s\n", buffer);
			break;
	}
	va_end(arglist);
}

ESource *find_source(ESourceList *list, char *uri)
{
	GSList *g;
	for (g = e_source_list_peek_groups (list); g; g = g->next) {
		ESourceGroup *group = E_SOURCE_GROUP (g->data);
		GSList *s;
		for (s = e_source_group_peek_sources (group); s; s = s->next) {
			ESource *source = E_SOURCE (s->data);
			if (!strcmp(e_source_get_uri(source), uri))
				return source;
		}
	}
	return NULL;
}

evo_environment *sync_connect(sync_pair* handle, connection_type type, sync_object_type object_types)
{
	evo_environment *env = g_malloc0(sizeof(evo_environment));
	evo_debug(env, 3, "start: sync_connect");
	env->configfile = g_strdup_printf("%s/%ssettings", sync_get_datapath(handle), (type==CONNECTION_TYPE_LOCAL?"local":"remote"));
	load_evo_settings(env);

	env->handle = handle;
	env->change_id = g_strdup_printf("msyncid%s", g_path_get_basename(sync_get_datapath(env->handle)));
	env->commondata.object_types = object_types;
	
	ESource *source;

	if (object_types & SYNC_OBJECT_TYPE_PHONEBOOK && env->adressbook_path && strlen(env->adressbook_path)) {
		if (!evo2_addrbook_open(env)) {
			sync_set_pair_status(env->handle, "Unable to open addressbook");
			sync_set_requestfailed(env->handle);
			return NULL;
		}
	}
	
	if (object_types & SYNC_OBJECT_TYPE_CALENDAR && env->calendar_path && strlen(env->calendar_path)) {
		if (!evo2_calendar_open(env)) {
			sync_set_pair_status(env->handle, "Unable to open calendar");
			sync_set_requestfailed(env->handle);
			return NULL;
		}
	}
	
	if (object_types & SYNC_OBJECT_TYPE_TODO && env->tasks_path && strlen(env->tasks_path)) {
		if (!evo2_tasks_open(env)) {
			sync_set_pair_status(env->handle, "Unable to open tasks");
			sync_set_requestfailed(env->handle);
			return NULL;
		}
	}
	
	srand(time(NULL));
	evo_debug(env, 3, "end: sync_connect");
	sync_set_requestdone(env->handle);
	return env;
}

int evo_get_data(void *object, sync_object_type format, char **data, int *datasize, const char **uid)
{
	char *buffer;
	EBookChange *ebc;
	ECalChange *ecc;
	switch (format) {
		case SYNC_OBJECT_TYPE_CALENDAR:
		case SYNC_OBJECT_TYPE_TODO:
			ecc = (ECalChange *)object;
			//e_cal_component_commit_sequence (ecc->comp);
			//e_cal_component_strip_errors(ecc->comp);
			//FIXME Ugly hack because evo does not add the vcal props
			*data = g_strdup_printf("BEGIN:VCALENDAR\n%sEND:VCALENDAR", e_cal_component_get_as_string (ecc->comp));
			printf("Found a new vevent\n%s\n", *data);
			//char **strarr = g_strsplit(buffer, "\n", 0);
			//*data = g_strjoinv("\r\n", strarr);
			//g_strfreev(strarr);
			//*datasize = strlen(*data) + 1;
			e_cal_component_get_uid(ecc->comp, uid);
			switch (ecc->type) {
				case E_CAL_CHANGE_ADDED:
					return SYNC_OBJ_ADDED;
				case E_CAL_CHANGE_MODIFIED:
					return SYNC_OBJ_MODIFIED;
				case E_CAL_CHANGE_DELETED:
					return SYNC_OBJ_HARDDELETED;
			}
		case SYNC_OBJECT_TYPE_PHONEBOOK:
			ebc = (EBookChange *)object;
			EVCard vcard = ebc->contact->parent;
			*data = e_vcard_to_string(&vcard, EVC_FORMAT_VCARD_30);
			*uid = e_contact_get_const(ebc->contact, E_CONTACT_UID);
			//*datasize = strlen(*data) + 1;
			switch (ebc->change_type) {
				case E_BOOK_CHANGE_CARD_ADDED:
					return SYNC_OBJ_ADDED;
				case E_BOOK_CHANGE_CARD_MODIFIED:
					return SYNC_OBJ_MODIFIED;
				case E_BOOK_CHANGE_CARD_DELETED:
					return SYNC_OBJ_HARDDELETED;
			}
		default:
			return 0;
	}
}

void evo_get_changes(GList **retchanges, GList *changes, sync_object_type format)
{
	GList *l;
	changed_object *change = NULL;
	
	for (l = changes; l != NULL; l = l->next) {
		change = g_malloc0(sizeof(changed_object));
		g_assert(change);
    
		change->change_type = evo_get_data(l->data, format, &change->comp, NULL, (const char **)&(change->uid));
		change->object_type = format;

		*retchanges = g_list_append(*retchanges, change);
	}
}

void get_changes(evo_environment *env, sync_object_type newdbs)
{
	GList *changes = NULL;
	GList *chinfo_list = NULL;
	sync_object_type retnewdbs = 0;
	
	if (env->adressbook && env->commondata.object_types & SYNC_OBJECT_TYPE_PHONEBOOK) {
		if (!e_book_get_changes(env->adressbook, env->change_id, &changes, NULL)) {
			sync_set_pair_status(env->handle, "Could not get changes from addressbook");
			sync_set_requestfailed(env->handle);
			return;
		}
		evo_get_changes(&chinfo_list, changes, SYNC_OBJECT_TYPE_PHONEBOOK);
		//e_cal_free_change_list(changes);
		changes = NULL;
	}
	
	if (env->calendar && env->commondata.object_types & SYNC_OBJECT_TYPE_CALENDAR) {
		if (!e_cal_get_changes(env->calendar, env->change_id, &changes, NULL)) {
			sync_set_pair_status(env->handle, "Could not get changes from calendar");
			sync_set_requestfailed(env->handle);
			return;
		}
		evo_get_changes(&chinfo_list, changes, SYNC_OBJECT_TYPE_CALENDAR);
		//e_cal_free_change_list(changes);
		changes = NULL;
	}
	
	if (env->tasks && env->commondata.object_types & SYNC_OBJECT_TYPE_TODO) {
		if (!e_cal_get_changes(env->tasks, env->change_id, &changes, NULL)) {
			sync_set_pair_status(env->handle, "Could not get changes from tasks");
			sync_set_requestfailed(env->handle);
			return;
		}
		printf("tasks length %s %i\n", env->change_id, g_list_length(changes));
		evo_get_changes(&chinfo_list, changes, SYNC_OBJECT_TYPE_TODO);
		//e_cal_free_change_list(changes);
		changes = NULL;
	}
	
	evo_debug(env, 2, "Done searching for changes. Found %i changes", g_list_length(chinfo_list));
	change_info *chinfo = NULL;
	chinfo = g_malloc0(sizeof(change_info));
	chinfo->changes = chinfo_list;
	
	/* Did we detect any reset databases? */
	chinfo->newdbs = retnewdbs;
	sync_set_requestdata(chinfo, env->handle);
}

void evo_print_binary(unsigned char *data, int len) {
  int t;
  for (t = 0; t < len; t++) {
    if (data[t] >= ' ' && data[t] <= 'z')
      printf("%c", data[t]);
    else
      printf(" %02x ", data[t]);
  }
  printf("\n");
}

void syncobj_modify(evo_environment *env, char* object, char *uid, sync_object_type objtype, char *returnuid, int *returnuidlen)
{
	GError *error = NULL;
	evo_debug(env, 2, "start: syncobj_modify");

	if (!uid && !object) {
		sync_set_pair_status(env->handle, "Got wrong data from engine");
		sync_set_requestfailed(env->handle);
		return;
	}
	
	switch (objtype) {
		case SYNC_OBJECT_TYPE_PHONEBOOK:
			if (!env->adressbook) {
				sync_set_requestdone(env->handle);
				return;
			}
			if (!evo2_addrbook_modify(env, object, uid, returnuid, returnuidlen)) {
				sync_set_pair_status(env->handle, "Unable to modify addressbook");
				sync_set_requestfailed(env->handle);
				return;
			}
			break;
		case SYNC_OBJECT_TYPE_CALENDAR:
			if (!env->calendar) {
				sync_set_requestdone(env->handle);
				return;
			}
			if (!evo2_calendar_modify(env, object, uid, returnuid, returnuidlen)) {
				sync_set_pair_status(env->handle, "Unable to modify calendar");
				sync_set_requestfailed(env->handle);
				return;
			}
			break;
		case SYNC_OBJECT_TYPE_TODO:
			if (!env->tasks) {
				sync_set_requestdone(env->handle);
				return;
			}
			if (!evo2_tasks_modify(env, object, uid, returnuid, returnuidlen)) {
				sync_set_pair_status(env->handle, "Unable to modify task");
				sync_set_requestfailed(env->handle);
				return;
			}
			break;
		default:
			printf("Error2\n");
	}

	sync_set_requestdone(env->handle);
}

void syncobj_delete(evo_environment *env, char *uid, sync_object_type objtype, int softdelete)
{
	evo_debug(env, 2, "start: delete");
	
	switch (objtype) {
		case SYNC_OBJECT_TYPE_PHONEBOOK:
			if (!evo2_addrbook_modify(env, NULL, uid, NULL, NULL)) {
				sync_set_pair_status(env->handle, "Unable to delete addressbook entry");
				sync_set_requestfailed(env->handle);
				return;
			}
			break;
		case SYNC_OBJECT_TYPE_CALENDAR:
			if (!evo2_calendar_modify(env, NULL, uid, NULL, NULL)) {
				sync_set_pair_status(env->handle, "Unable to delete calendar entry");
				sync_set_requestfailed(env->handle);
				return;
			}
			break;
		case SYNC_OBJECT_TYPE_TODO:
			if (!evo2_tasks_modify(env, NULL, uid, NULL, NULL)) {
				sync_set_pair_status(env->handle, "Unable to delete task");
				sync_set_requestfailed(env->handle);
				return;
			}
			break;
		default:
			printf("Error2\n");
	}
	sync_set_requestdone(env->handle);
}

void sync_disconnect(evo_environment *env)
{
	printf ("%s\n", __func__);
	
	evo_debug(env, 2, "start: disconnect");
	sync_pair *sync_pair = env->handle;
	g_free(env);
	sync_set_requestdone(sync_pair);
}

void sync_done(evo_environment *env, gboolean success)
{
	GList *changes;
	printf ("%s\n", __func__);
	if (env->adressbook)
		e_book_get_changes(env->adressbook, env->change_id, &changes, NULL);
	if (env->calendar)
		e_cal_get_changes(env->calendar, env->change_id, &changes, NULL);
	if (env->tasks)
		e_cal_get_changes(env->tasks, env->change_id, &changes, NULL);
	//e_cal_free_change_list (changed);

	sync_set_requestdone(env->handle);
}

/*
 * sync_connect
 */
gboolean always_connected()
{
  printf ("%s\n", __func__);
  return TRUE;
}


/*
 * short_name
 */
char *short_name()
{
  printf ("%s\n", __func__);
  return("evolution2-sync");
}


/*
 * long_name
 */
char *long_name()
{
  printf ("%s\n", __func__);
  return "Ximan Evolution 2";
}


/*
 * plugin_info
 */
char *plugin_info(void)
{
  printf ("%s\n", __func__);
  return "This plugin allows you to sync with Ximian Evolution 2.";
}


/*
 * plugin_init
 */
void plugin_init(void)
{
  printf ("%s\n", __func__);
  //evo2_init_config();
}


/*
 * object_types
 */
sync_object_type object_types()
{
  printf ("%s\n", __func__);
  return SYNC_OBJECT_TYPE_CALENDAR | SYNC_OBJECT_TYPE_PHONEBOOK | SYNC_OBJECT_TYPE_TODO;
}


/*
 * plugin_API_version
 */
int plugin_API_version(void)
{
  printf ("%s\n", __func__);
  return 3; 
}
