/* SynCE plugin -- SynCE Support for Claws Mail
 *
 * Copyright (C) 2005-2007 Holger Berndt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "addritem.h"
#include "addressbook.h"
#include "alertpanel.h"
#include "prefs_gtk.h"
#include "common/defs.h"

#include "synce_comp.h"
#include "synce_rapi.h"


static void     insert_email_key(gchar*, SynCERecord*);
static gint     collect_claws_email(ItemPerson*, const gchar*);
static void     add_to_claws_addressbook(gpointer, gpointer, gpointer);
static gboolean free_email_in_wince_hash_key(gpointer, gpointer, gpointer);
static void     build_list(gpointer, gpointer, gpointer);
static gboolean free_email_not_in_wince_hash_key(gpointer, gpointer, gpointer);
static void     synce_plugin_save_config(void);
static gboolean compare_email(gpointer, gpointer, gpointer);

static GHashTable *email_in_wince_hash     = NULL;
static GHashTable *email_not_in_wince_hash = NULL;
static GHashTable *repeated_wince_hash     = NULL;
static GHashTable *repeated_local_hash     = NULL;

/* configuration */
static SynCEPluginConfig config;

static PrefParam param[] = {
  {"synce_log_level", "2", &config.synce_log_level,
   P_INT, NULL, NULL, NULL},
  {"offer_add_to_local", "TRUE",
   &config.offer_add_to_local, P_BOOL,
   NULL, NULL, NULL},
  {"warn_double_addresses_local", "TRUE",
   &config.warn_double_addresses_local, P_BOOL,
   NULL, NULL, NULL},
  {"warn_double_addresses_wince", "TRUE",
   &config.warn_double_addresses_wince, P_BOOL,
   NULL, NULL, NULL},
  {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
};


gboolean synce_comp(void)
{
  AlertValue val;
  gchar *message;
  gchar **email_addresses;
  gchar **eap;
  gchar *email_list;
  gchar *rcpath;

  if(email_in_wince_hash == NULL)
    email_in_wince_hash = g_hash_table_new(g_str_hash, g_str_equal);
  if(email_not_in_wince_hash == NULL)
    email_not_in_wince_hash = g_hash_table_new(g_str_hash, g_str_equal);

  rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
  prefs_read_config(param, "SynCEPlugin", rcpath, NULL);
  g_free(rcpath);
  synce_plugin_save_config();


  if(config.warn_double_addresses_wince && (repeated_wince_hash == NULL))
    repeated_wince_hash = g_hash_table_new(g_str_hash, g_str_equal);
  if(config.warn_double_addresses_local && (repeated_local_hash == NULL))
    repeated_local_hash = g_hash_table_new(g_str_hash, g_str_equal);

  if(query_wince(config.synce_log_level) == FALSE) {
    AlertValue val;

    g_hash_table_destroy(email_in_wince_hash);
    email_in_wince_hash = NULL;
    g_hash_table_destroy(email_not_in_wince_hash);
    email_not_in_wince_hash = NULL;
    
    if(repeated_local_hash) {
      g_hash_table_destroy(repeated_local_hash);
      repeated_local_hash = NULL;
    }
    if(repeated_wince_hash) {
      g_hash_table_destroy(repeated_wince_hash);
      repeated_wince_hash = NULL;
    }
    
    val = alertpanel_full("SynCE Plugin",
			  "Error connecting to Windows CE (tm) device",
			  GTK_STOCK_CLOSE, NULL, NULL, FALSE,
			  NULL, ALERT_ERROR, G_ALERTDEFAULT);
    return FALSE;
  }

  if(config.warn_double_addresses_wince) {
    gint number;

    email_addresses = g_new0(gchar*, g_hash_table_size(repeated_wince_hash)+1);
    eap = email_addresses;
    g_hash_table_foreach(repeated_wince_hash, build_list, &eap);
    *eap = NULL;
    if(*email_addresses != NULL) {

      eap = email_addresses;
      while(*eap != NULL) {
	number = GPOINTER_TO_INT(g_hash_table_lookup(repeated_wince_hash, *eap));
	*eap = g_strdup_printf("%s (%d times)", *eap, number);
	eap++;
      }

      email_list = g_strjoinv("\n", email_addresses);

      eap = email_addresses;
      while(*eap != NULL) {
	g_free(*eap);
	eap++;
      }

      message = g_strconcat("The following email address(es) were found "
			    "multiple times in the Windows CE (tm) device:\n",
			    email_list, NULL);
      val = alertpanel_full("SynCE Plugin", message,
			    GTK_STOCK_CLOSE, NULL, NULL, FALSE,
			    NULL, ALERT_WARNING, G_ALERTDEFAULT);
      g_free(email_list);
      g_free(message);
    }
    g_free(email_addresses);
    g_hash_table_destroy(repeated_wince_hash);
    repeated_wince_hash = NULL;
  }

  addrindex_load_person_attribute(NULL, collect_claws_email);

  if(config.warn_double_addresses_local) {
    gint number;

    email_addresses = g_new0(gchar*, g_hash_table_size(repeated_local_hash)+1);
    eap = email_addresses;
    g_hash_table_foreach(repeated_local_hash, build_list, &eap);
    *eap = NULL;
    if(*email_addresses != NULL) {

      eap = email_addresses;
      while(*eap != NULL) {
	number = GPOINTER_TO_INT(g_hash_table_lookup(repeated_local_hash, *eap));
	*eap = g_strdup_printf("%s (%d times)", *eap, number);
	eap++;
      }

      email_list = g_strjoinv("\n", email_addresses);

      eap = email_addresses;
      while(*eap != NULL) {
	g_free(*eap);
	eap++;
      }

      message = g_strconcat("The following email address(es) were found "
			    "multiple times in a local addressbook:\n",
			    email_list, NULL);
      val = alertpanel_full("SynCE Plugin", message,
			    GTK_STOCK_CLOSE, NULL, NULL, FALSE,
			    NULL, ALERT_WARNING, G_ALERTDEFAULT);
      g_free(email_list);
      g_free(message);
    }
    g_free(email_addresses);
    g_hash_table_destroy(repeated_local_hash);
    repeated_local_hash = NULL;
  }

  g_hash_table_foreach_remove(email_not_in_wince_hash,
			      compare_email, NULL);

  /* Now email_not_in_wince_hash holds a list of email addresses from Claws'
     addressbook which are not in wince, and email_in_wince_hash holds a hash
     of email addresses from wince which are not in Claws. */
  if(config.offer_add_to_local) {
    g_hash_table_foreach(email_in_wince_hash, add_to_claws_addressbook, NULL);
  }
  else {
    email_addresses = g_new0(gchar*, g_hash_table_size(email_in_wince_hash)+1);
    eap = email_addresses;
    g_hash_table_foreach(email_in_wince_hash, build_list, &eap);
    *eap = NULL;

    if(*email_addresses != NULL) {
      email_list = g_strjoinv("\n", email_addresses);
      message = g_strconcat("The following email address(es) were not found in "
			    "a local addressbook:\n", email_list, NULL);
      val = alertpanel_full("SynCE Plugin", message,
			    GTK_STOCK_CLOSE, NULL, NULL, FALSE,
			    NULL, ALERT_WARNING, G_ALERTDEFAULT);
      g_free(email_list);
      g_free(message);
    }
    g_free(email_addresses);
    
  }
  g_hash_table_foreach_remove(email_in_wince_hash, free_email_in_wince_hash_key, NULL);
  g_hash_table_destroy(email_in_wince_hash);
  email_in_wince_hash = NULL;

  email_addresses = g_new0(gchar*, g_hash_table_size(email_not_in_wince_hash)+1);
  eap = email_addresses;
  g_hash_table_foreach(email_not_in_wince_hash, build_list, &eap);
  *eap = NULL;

  if(*email_addresses != NULL) {
    email_list = g_strjoinv("\n", email_addresses);
    message = g_strconcat("The following email address(es) were not found in "
			  "the Windows CE (tm) device:\n", email_list, NULL);
    val = alertpanel_full("SynCE Plugin", message,
			  GTK_STOCK_CLOSE, NULL, NULL, FALSE,
			  NULL, ALERT_WARNING, G_ALERTDEFAULT);
    g_free(email_list);
    g_free(message);
  }
  g_free(email_addresses);

  g_hash_table_foreach_remove(email_not_in_wince_hash,
			      free_email_not_in_wince_hash_key, NULL);
  g_hash_table_destroy(email_not_in_wince_hash);
  email_not_in_wince_hash = NULL;

  message = g_strdup("SynCE Plugin done.");
  val = alertpanel_full("SynCE Plugin", message,
			GTK_STOCK_CLOSE, NULL, NULL, FALSE,
			NULL, ALERT_WARNING, G_ALERTDEFAULT);
  g_free(message);

  return TRUE;
}

gboolean collect_record(SynCERecord *record)
{
  GSList *walk;
  gchar *address;

  if(record == NULL)
    return TRUE;

  walk = record->email_addresses;
  while(walk) {
    if((address = (gchar*)walk->data) != NULL) {
      insert_email_key(address, record);
    }
    walk = g_slist_next(walk);
  }
  return TRUE;
}

static gboolean free_email_in_wince_hash_key(gpointer key, gpointer value,
				    gpointer user_data)
{
  SynCEPerson *person;

  person = (SynCEPerson *) value;
  if(person != NULL) {
    if(person->first_name != NULL) {
      g_free(person->first_name);
      person->first_name = NULL;
    }
    if(person->last_name != NULL) {
      g_free(person->last_name);
      person->last_name = NULL;
    }
    g_free(person);
    person = NULL;
  }
  if(key != NULL) {
    g_free(key);
    key = NULL;
  }
  return TRUE;
}

static gboolean free_email_not_in_wince_hash_key(gpointer key, gpointer value,
				    gpointer user_data)
{
  if(key != NULL) {
    g_free(key);
    key = NULL;
  }
  return TRUE;
}

static void insert_email_key(gchar *address, SynCERecord *record)
{
  gchar *addr;
  SynCEPerson *person;

  addr = g_utf8_strdown(address, -1);

  if((person = g_hash_table_lookup(email_in_wince_hash, addr)) != NULL) {
    gpointer origkey;

    if(config.warn_double_addresses_wince) {
      gint number;
      gpointer value;
      
      g_hash_table_lookup_extended(email_in_wince_hash, addr,
				   &origkey, &value);
      value = g_hash_table_lookup(repeated_wince_hash, addr);
      if(value == NULL)
	g_hash_table_insert(repeated_wince_hash, origkey, GINT_TO_POINTER(2));
      else {	  
	number = GPOINTER_TO_INT(value);
	g_hash_table_insert(repeated_wince_hash, origkey, GINT_TO_POINTER(++number));
      }
    }
    g_free(addr);
    return;
  }

  person = g_new0(SynCEPerson, 1);
  if(record->first_name != NULL)
    person->first_name = g_strdup(record->first_name);
  if(record->last_name != NULL)
    person->last_name  = g_strdup(record->last_name);
  g_hash_table_insert(email_in_wince_hash, addr, person);
}

static gint collect_claws_email(ItemPerson *itemperson, const gchar *bookname)
{
  gchar *addr;
  GList *nodeM;
  gpointer value;

  /* Process each E-Mail address */
  nodeM = itemperson->listEMail;
  while(nodeM) {
    ItemEMail *email = nodeM->data;

    addr = g_utf8_strdown(email->address, -1);
    value = g_hash_table_lookup(email_not_in_wince_hash, addr);
    if(value == NULL)
      g_hash_table_insert(email_not_in_wince_hash, addr, GINT_TO_POINTER(1));
    else {
      gpointer origkey;

      if(config.warn_double_addresses_local) {
	gint number;
	gpointer value;
	
	g_hash_table_lookup_extended(email_not_in_wince_hash, addr,
				     &origkey, &value);
	value = g_hash_table_lookup(repeated_local_hash, addr);
	if(value == NULL)
	  g_hash_table_insert(repeated_local_hash, origkey, GINT_TO_POINTER(2));
	else {	  
	  number = GPOINTER_TO_INT(value);
	  g_hash_table_insert(repeated_local_hash, origkey, GINT_TO_POINTER(++number));
	}
      }
      g_free(addr);
    }
    nodeM = g_list_next(nodeM);
  }
  return 0;
}

static void add_to_claws_addressbook(gpointer key, gpointer value,
				     gpointer user_data)
{
  SynCEPerson *person;
  gchar *fromname;

  person = (SynCEPerson*)value;
  fromname = g_strdup_printf("%s%s%s", (person->first_name)?(person->first_name):"",
			               (person->first_name)?" "                 :"",
			               (person->last_name) ?(person->last_name) :"");
  addressbook_add_contact(fromname, (gchar*)key, NULL, NULL);
  g_free(fromname);
}

static void build_list(gpointer key, gpointer value,
				    gpointer user_data)
{
  gchar ***eap;

  eap = user_data;
  **eap = key;
  (*eap)++;
}

static void synce_plugin_save_config(void)
{
  PrefFile *pfile;
  gchar *rcpath;

  debug_print("Saving SynCE Plugin Configuration\n");

  rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
  pfile = prefs_write_open(rcpath);
  g_free(rcpath);
  if (!pfile || (prefs_set_block_label(pfile, "SynCEPlugin") < 0))
    return;
  
  if (prefs_write_param(param, pfile->fp) < 0) {
    g_warning("SynCE Plugin: Failed to write SynCE Plugin configuration to file");
    prefs_file_close_revert(pfile);
    return;
  }
        if (fprintf(pfile->fp, "\n") < 0) {
		FILE_OP_ERROR(rcpath, "fprintf");
		prefs_file_close_revert(pfile);
	} else
	        prefs_file_close(pfile);
}

static gboolean compare_email(gpointer key, gpointer value,
			      gpointer user_data)
{
  SynCEPerson *person;
  gpointer origkey;
  gpointer value_wince;
  
  person = (SynCEPerson*) g_hash_table_lookup(email_in_wince_hash, key);

  /* email is not in pda -> leave it in email_not_in_wince_hash */
  if(person == NULL)
    return FALSE;
    
  /* email is in both -> remove from both hashes */
  g_hash_table_lookup_extended(email_in_wince_hash, key,
			       &origkey, &value_wince);
  g_hash_table_remove(email_in_wince_hash, origkey);
  free_email_in_wince_hash_key(origkey, value_wince, NULL);

  g_hash_table_lookup_extended(email_not_in_wince_hash, key,
			       &origkey, &value_wince);
  g_free(origkey);
  return TRUE;
}
