/* 
   MultiSync Opie Plugin - Synchronize Opie/Zaurus Devices
   Copyright (C) 2003 Tom Foottit <tom@foottit.com>

   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: opie_changes.c,v 1.3.2.1 2004/04/12 20:22:37 irix Exp $
 */

#include <glib.h>
#include <stdio.h>
#include <string.h>

#include <multisync.h>
#include "opie_debug.h"
#include "opie_comms.h"
#include "opie_changes.h"
#include "opie_xml.h"
#include "opie_vtype.h"
#include "opie_log.h"


/* TODO: the 3 functions below use the same algorithm. They should be
 * consolidated into one and be passed a parameter (and/or fxn pointers)
 * to deal with the differences between the 3.
 */


/*
 * opie_get_phonebook_changes
 */
gboolean opie_get_phonebook_changes(opie_conn *conn,
                                    GList* contacts,
                                    GList **changes,
                                    GList* categories,
                                    gboolean report_all_data,
                                    gboolean* reset)
{
  gboolean rc = TRUE;
  char* filename;
  FILE* fd;
  GList* old_contacts = NULL;
  GList* changed_contacts = NULL;
  GList* li;
  GList* lio;
  gboolean found_uid = FALSE;
  changed_object* change;
  contact_data* old;
  contact_data* new;

  filename = g_strdup_printf("%s/addressbook.xml",
                             sync_get_datapath(conn->sync_pair));

  parse_contact_data(filename, &old_contacts);
  g_free(filename);

  /* TODO
   * This algorithm seems a bit hackish, and the efficiency is
   * poor because it uses 2 linear searches. Needs improving.
   */

  
  if((g_list_length(old_contacts) > 0) && (!report_all_data))
  {
    /* compare the old list and the new list */

    /* iterate over the new list, finding new/changed items */
    for(li = contacts; li != NULL; li = g_list_next(li))
    {
      /* TODO: improve the efficiency of this code - right now it is
       * just a linear search.
       */      

      new = (contact_data*)li->data;

      found_uid = FALSE;
      for(lio = old_contacts; lio != NULL; lio = g_list_next(lio))
      {
        old = (contact_data*)lio->data;        
        if(!strcmp(new->uid, old->uid))
        {
          /* existing entry - check for changes */
          if(!contact_equals(new, old))
          {
            /* change detected */ 
            OPIE_DEBUG("detected contact data change\n"); 
            change = g_malloc0(sizeof(changed_object));
            change->uid = g_strdup(new->uid);
            change->change_type = SYNC_OBJ_MODIFIED; 
            change->object_type = SYNC_OBJECT_TYPE_PHONEBOOK;
            change->comp = contact_data_to_vcard(new, categories);

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

          found_uid = TRUE;
          break;  
        }
      } 

      if(!found_uid)
      {
         /* new entry */
        OPIE_DEBUG("detected new contact data\n"); 
        change = g_malloc0(sizeof(changed_object));
        change->uid = g_strdup(new->uid);
        change->change_type = SYNC_OBJ_ADDED; 
        change->object_type = SYNC_OBJECT_TYPE_PHONEBOOK;
        change->comp = contact_data_to_vcard(new, categories);

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

    /* iterate over the old list checking for deleted items */
    for(lio = old_contacts; lio != NULL; lio = g_list_next(lio))
    {
      /* TODO: improve the efficiency of this code - right now it is
       * just a linear search.
       */

      old = lio->data;
      found_uid = FALSE;
      for(li = contacts; li != NULL; li = g_list_next(li))
      {
        new = li->data;
        if(!strcmp(old->uid, new->uid))
        {
          found_uid = TRUE;  
        }        
      }

      if(!found_uid)
      {
        /* deleted entry */  
        OPIE_DEBUG("detected deleted contact data\n"); 
        change = g_malloc0(sizeof(changed_object));
        change->uid = g_strdup(old->uid);
        change->change_type = SYNC_OBJ_HARDDELETED; 
        change->object_type = SYNC_OBJECT_TYPE_PHONEBOOK;
        change->comp = contact_data_to_vcard(old, categories);

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

    }
  }
  else
  {  
    /* new database - everything is new */ 
    for(li = contacts; li != NULL; li = g_list_next(li))
    {
      OPIE_DEBUG("detected new contact data\n"); 
      new = li->data;
      change = g_malloc0(sizeof(changed_object));
      change->uid = g_strdup(new->uid);
      change->change_type = SYNC_OBJ_ADDED; 
      change->object_type = SYNC_OBJECT_TYPE_PHONEBOOK;
      change->comp = contact_data_to_vcard(new, categories);

      *changes = g_list_append(*changes, change);        
    }
    
    /* if we haven't been asked to report everything and the
     * opie device has no data then it is effectively 'reset'
     */
    if(!report_all_data)
    {
      *reset = TRUE;
    }
    
  }

  return rc; 
}


/* 
 * opie_get_calendar_changes
 */
gboolean opie_get_calendar_changes(opie_conn *conn,
                                   GList* calendar,
                                   GList **changes,
                                   GList* categories,
                                   gboolean report_all_data,
                                   gboolean* reset)
{
  gboolean rc = TRUE;
  char* filename;
  FILE* fd;
  GList* old_cal = NULL;
  GList* changed_cal = NULL;
  GList* li;
  GList* lio;
  gboolean found_uid = FALSE;
  changed_object* change;
  cal_data* old;
  cal_data* new;

  filename = g_strdup_printf("%s/datebook.xml",
                             sync_get_datapath(conn->sync_pair));

  parse_cal_data(filename, &old_cal);
  g_free(filename);

  /* TODO
   * This algorithm seems a bit hackish, and the efficiency is
   * poor because it uses 2 linear searches. Needs improving.
   */

  if((g_list_length(old_cal) > 0) && (!report_all_data))
  {
    /* compare the old list and the new list */

    /* iterate over the new list, finding new/changed items */
    for(li = calendar; li != NULL; li = g_list_next(li))
    {
      /* TODO: improve the efficiency of this code - right now it is
       * just a linear search.
       */      

      new = (cal_data*)li->data;

      found_uid = FALSE;
      for(lio = old_cal; lio != NULL; lio = g_list_next(lio))
      {
        old = (cal_data*)lio->data;        
        if(!strcmp(new->uid, old->uid))
        {
          /* existing entry - check for changes */
          if(!cal_equals(new, old))
          {
            /* change detected */ 
            OPIE_DEBUG("detected calendar data change\n"); 
            change = g_malloc0(sizeof(changed_object));
            change->uid = g_strdup(new->uid);
            change->change_type = SYNC_OBJ_MODIFIED; 
            change->object_type = SYNC_OBJECT_TYPE_CALENDAR;
            change->comp = cal_data_to_vcal(new, categories);

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

          found_uid = TRUE;
          break;  
        }
      } 

      if(!found_uid)
      {
        /* new entry */
        OPIE_DEBUG("detected new calendar data\n"); 
        change = g_malloc0(sizeof(changed_object));
        change->uid = g_strdup(new->uid);
        change->change_type = SYNC_OBJ_ADDED; 
        change->object_type = SYNC_OBJECT_TYPE_CALENDAR;
        change->comp = cal_data_to_vcal(new, categories);

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

    /* iterate over the old list checking for deleted items */
    for(lio = old_cal; lio != NULL; lio = g_list_next(lio))
    {
      /* TODO: improve the efficiency of this code - right now it is
       * just a linear search.
       */

      old = lio->data;
      found_uid = FALSE;
      for(li = calendar; li != NULL; li = g_list_next(li))
      {
        new = li->data;
        if(!strcmp(old->uid, new->uid))
        {
          found_uid = TRUE;  
        }        
      }

      if(!found_uid)
      {
        /* deleted entry */  
        OPIE_DEBUG("detected deleted calendar data\n"); 
        change = g_malloc0(sizeof(changed_object));
        change->uid = g_strdup(old->uid);
        change->change_type = SYNC_OBJ_HARDDELETED; 
        change->object_type = SYNC_OBJECT_TYPE_CALENDAR;
        change->comp = cal_data_to_vcal(old, categories);

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

    }
  }
  else
  {
    /* new database - everything is new */ 
    for(li = calendar; li != NULL; li = g_list_next(li))
    {
      OPIE_DEBUG("detected new calendar data\n"); 
      new = li->data;
      change = g_malloc0(sizeof(changed_object));
      change->uid = g_strdup(new->uid);
      change->change_type = SYNC_OBJ_ADDED; 
      change->object_type = SYNC_OBJECT_TYPE_CALENDAR;
      change->comp = cal_data_to_vcal(new, categories);

      *changes = g_list_append(*changes, change);        
    }
    
    /* if we haven't been asked to report everything and the
     * opie device has no data then it is effectively 'reset'
     */
    if(!report_all_data)
    {
      *reset = TRUE;
    }
  }

  return rc; 
}


/*
 * opie_get_todo_changes
 */
gboolean opie_get_todo_changes(opie_conn *conn,
                               GList* todos,
                               GList **changes,
                               GList* categories,
                               gboolean report_all_data,
                               gboolean* reset)
{
  gboolean rc = TRUE;
  char* filename;
  FILE* fd;
  GList* old_todos = NULL;
  GList* changed_todos = NULL;
  GList* li;
  GList* lio;
  gboolean found_uid = FALSE;
  changed_object* change;
  todo_data* old;
  todo_data* new;

  filename = g_strdup_printf("%s/todolist.xml",
                             sync_get_datapath(conn->sync_pair));

  parse_todo_data(filename, &old_todos);
  g_free(filename);

  /* TODO
   * This algorithm seems a bit hackish, and the efficiency is
   * poor because it uses 2 linear searches. Needs improving.
   */

  if((g_list_length(old_todos) > 0) && (!report_all_data))
  {
    /* compare the old list and the new list */

    /* iterate over the new list, finding new/changed items */
    for(li = todos; li != NULL; li = g_list_next(li))
    {
      /* TODO: improve the efficiency of this code - right now it is
       * just a linear search.
       */      

      new = (todo_data*)li->data;

      found_uid = FALSE;
      for(lio = old_todos; lio != NULL; lio = g_list_next(lio))
      {
        old = (todo_data*)lio->data;        
        if(!strcmp(new->uid, old->uid))
        {
          /* existing entry - check for changes */
          if(!todo_equals(new, old))
          {
            /* change detected */ 
            OPIE_DEBUG("detected todo data change\n"); 
            change = g_malloc0(sizeof(changed_object));
            change->uid = g_strdup(new->uid);
            change->change_type = SYNC_OBJ_MODIFIED; 
            change->object_type = SYNC_OBJECT_TYPE_TODO;
            change->comp = todo_data_to_vtodo(new, categories);

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

          found_uid = TRUE;
          break;  
        }
      } 

      if(!found_uid)
      {
        /* new entry */
        OPIE_DEBUG("detected new todo data\n"); 
        change = g_malloc0(sizeof(changed_object));
        change->uid = g_strdup(new->uid);
        change->change_type = SYNC_OBJ_ADDED; 
        change->object_type = SYNC_OBJECT_TYPE_TODO;
        change->comp = todo_data_to_vtodo(new, categories);

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

    /* iterate over the old list checking for deleted items */
    for(lio = old_todos; lio != NULL; lio = g_list_next(lio))
    {
      /* TODO: improve the efficiency of this code - right now it is
       * just a linear search.
       */

      old = lio->data;
      found_uid = FALSE;
      for(li = todos; li != NULL; li = g_list_next(li))
      {
        new = li->data;
        if(!strcmp(old->uid, new->uid))
        {
          found_uid = TRUE;  
        }        
      }

      if(!found_uid)
      {
        /* deleted entry */  
        OPIE_DEBUG("detected deleted todo data\n"); 
        change = g_malloc0(sizeof(changed_object));
        change->uid = g_strdup(old->uid);
        change->change_type = SYNC_OBJ_HARDDELETED; 
        change->object_type = SYNC_OBJECT_TYPE_TODO;
        change->comp = todo_data_to_vtodo(old, categories);

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

    }
  }
  else
  {
    /* new database - everything is new */ 
    for(li = todos; li != NULL; li = g_list_next(li))
    {
      OPIE_DEBUG("detected new todo data\n"); 
      new = li->data;
      change = g_malloc0(sizeof(changed_object));
      change->uid = g_strdup(new->uid);
      change->change_type = SYNC_OBJ_ADDED; 
      change->object_type = SYNC_OBJECT_TYPE_TODO;
      change->comp = todo_data_to_vtodo(new, categories);

      *changes = g_list_append(*changes, change);        
    }
    
    /* if we haven't been asked to report everything and the
     * opie device has no data then it is effectively 'reset'
     */
    if(!report_all_data)
    {
      *reset = TRUE;
    }
  }

  return rc; 
}



