// binds the two events together
        public static void BindEvents(AppointmentItem oitem, Event gitem, string event_property_key)
        {
            // make sure to tag the user property of the google id
            UserProperty oitem_google_prop = oitem.UserProperties.Find(event_property_key);

            if (oitem_google_prop != null)
            {
                oitem_google_prop.Value = gitem.Id;

                System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem_google_prop);
            }
            else
            {
                oitem.UserProperties.Add(event_property_key, OlUserPropertyType.olText).Value = gitem.Id;
            }

            try
            {
                // save the outlook event
                oitem.Save();
            }
            catch (System.Exception)
            {
                // unable to save the outlook item...
                // should report an error, but this will get handled by the main form...
            }

            // make sure to tag the private property of the outlook id
            if (gitem.ExtendedProperties == null)
            {
                gitem.ExtendedProperties        = new Event.ExtendedPropertiesData();
                gitem.ExtendedProperties.Shared = new Dictionary <string, string>();
            }
            else if (gitem.ExtendedProperties.Shared == null)
            {
                gitem.ExtendedProperties.Shared = new Dictionary <string, string>();
            }

            gitem.ExtendedProperties.Shared[event_property_key] = OutlookCalendar.FormatEventID(oitem);
        }
        bool synchronize(List <AppointmentItem> outlook_items, List <Event> google_items)
        {
            // TODO: consider description updates (only needs to be considered when checked... come back and fix this)
            // TODO: optimize comparison algorithms - this one not mine, but should consider for larger sets of data
            // TODO: outlook to google recurrence 90% complete
            //       getting weird error message from outlook: "The operation cannot be performed because the message has changed"
            //       need to determine what happens when deleting just the master recurrence object
            // TODO: google to outlook recurrence 0% complete... need to read up on http://www.ietf.org/rfc/rfc2445
            // TODO: testing on a larger scale...
            // TODO: refactor this function, as it is getting out of control...

            // indicates the number of entries added / updated / removed....
            uint google_entries_added    = 0;
            uint google_entries_updated  = 0;
            uint google_entries_removed  = 0;
            uint outlook_entries_added   = 0;
            uint outlook_entries_updated = 0;
            uint outlook_entries_removed = 0;
            uint bound_entries_found     = 0;
            uint errors = 0;

            // first synchronize outlook -> google

            // run over all the office items and add or update on google
            for (int i = outlook_items.Count - 1; i >= 0; --i)
            {
                // obtain a reference to the outlook item...
                var oitem = outlook_items[i];

                // determine first if this outlook item is associated with a google calendar item...
                UserProperty oitem_google_prop = oitem.UserProperties.Find(EventPropertyKey);

                if (oitem_google_prop == null)
                {
                    // run across the google events to see if there is one that matches
                    Event gitem = null;
                    foreach (var g in google_items)
                    {
                        // lowercase match the signature and
                        // the property is not set or the property matches the oitem, match found...
                        // the user has to type it the same in both cases to make a match...
                        // is there something better here?
                        if (signature(g).ToLower() == signature(oitem).ToLower() &&
                            (g.ExtendedProperties == null ||
                             g.ExtendedProperties.Shared == null ||
                             g.ExtendedProperties.Shared.ContainsKey(EventPropertyKey) == false ||
                             g.ExtendedProperties.Shared[EventPropertyKey] == OutlookCalendar.FormatEventID(oitem)))
                        {
                            gitem = g; break;
                        }
                    }

                    if (gitem != null)
                    {
                        // give some indication of what will take place
                        logboxout("Binding Outlook and Google event: " + oitem.Subject + " (" + oitem.Start + ")");

                        // bind the properties together
                        OutlookCalendar.Instance.Bind(oitem, gitem);

                        // update the instance on the google calendar
                        GoogleCalendar.Instance.updateEntry(gitem);

                        // remove the outlook item and the google item from the lists...
                        // do not need to process them again for future iterations...
                        google_items.Remove(gitem);
                        outlook_items.Remove(oitem);

                        // save and close the outlook item
                        ((_AppointmentItem)oitem).Close(OlInspectorClose.olSave);

                        // remove the com reference
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem);

                        // update the stats
                        ++bound_entries_found;
                    }
                    else
                    {
                        // give some indication of what will take place
                        logboxout("Creating Google event: " + oitem.Subject + " (" + oitem.Start + ")");

                        // the property does not exist... this mean that google calendar
                        // does not have this outlook entry...  after this call, the outlook item
                        // and the google item should be tied together by the use of properties...
                        gitem = GoogleCalendar.Instance.addEntry(oitem, cbAddDescription.Checked, cbAddReminders.Checked, cbAddAttendees.Checked);

                        // updated the stats
                        ++google_entries_added;
                    }
                }
                else
                {
                    // the property exists, so we need to determine if the event should be updated
                    // first, find the event in the list of google items...
                    Event gitem = null;
                    foreach (var g in google_items)
                    {
                        if (oitem_google_prop.Value == g.Id)
                        {
                            gitem = g; break;
                        }
                    }

                    if (gitem == null)
                    {
                        // give some indication of what will take place
                        logboxout("Removing Outlook event: " + oitem.Subject + " (" + oitem.Start + ")");

                        // the item does not exist, so it was removed from google calendar
                        // since it was removed from google, remove it from outlook
                        OutlookCalendar.Instance.deleteCalendarEntry(oitem);

                        // outlook item can no longer be used after it is deleted
                        outlook_items.RemoveAt(i);

                        // need to release the com reference
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem);

                        // update the stats
                        ++outlook_entries_removed;
                    }
                    else
                    {
                        // the item does exist...
                        // determine if the event should be updated...
                        if ((signature(oitem) != signature(gitem) ||
                             OutlookGoogleSync.Utilities.ObtainUserBodyData(oitem.Body) != OutlookGoogleSync.Utilities.ObtainUserBodyData(gitem.Description)) &&
                            oitem.LastModificationTime > gitem.Updated)
                        {
                            // the google event can only be updated if owned by the calendar...
                            if (gitem.Organizer.Self.HasValue && gitem.Organizer.Self.Value)
                            {
                                // give some indication of what will take place
                                if (signature(oitem) != signature(gitem))
                                {
                                    logboxout("Updating Google event: " + gitem.Summary + " (" + GoogleCalendar.FormatTime(gitem.Start) + ") ==> " + oitem.Subject + " (" + oitem.Start + ")");
                                }
                                else
                                {
                                    logboxout("Updating Google event description: " + gitem.Summary + " (" + GoogleCalendar.FormatTime(gitem.Start) + ")");
                                }

                                // update the event based on the outlook item
                                GoogleCalendar.Instance.updateEntry(gitem, oitem, cbAddDescription.Checked, cbAddReminders.Checked, cbAddAttendees.Checked);

                                // update the status
                                ++google_entries_updated;
                            }

                            // this google item has been processed
                            google_items.Remove(gitem);
                        }
                    }

                    // need to release the com reference
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem_google_prop);
                }
            }

            // second synchronize google -> outlook

            // run over all the google items and add or update on outlook
            for (int i = google_items.Count - 1; i >= 0; --i)
            {
                // obtain a reference to the google item...
                var gitem = google_items[i];

                // determine first if this google item is associated with an outlook calendar item...
                string outlook_id = null;
                if (gitem.ExtendedProperties != null &&
                    gitem.ExtendedProperties.Shared != null &&
                    gitem.ExtendedProperties.Shared.ContainsKey(EventPropertyKey))
                {
                    outlook_id = gitem.ExtendedProperties.Shared[EventPropertyKey];
                }

                if (outlook_id == null)
                {
                    // run across the outlook events to see if there is one that matches...
                    // this may not need to be done, as the google pass should have found them all...
                    // lets be on the safe side here and do it anyway...
                    AppointmentItem oitem = null;
                    foreach (var o in outlook_items)
                    {
                        // lowercase match the signature and
                        // the property is not set or the property matches the gitem, match found...
                        // the user has to type it the same in both cases to make a match...
                        // is there something better here?
                        if (signature(o).ToLower() == signature(gitem).ToLower() &&
                            (o.UserProperties == null ||
                             o.UserProperties.Find(EventPropertyKey) == null ||
                             o.UserProperties.Find(EventPropertyKey).Value == gitem.Id))
                        {
                            oitem = o; break;
                        }
                    }

                    if (oitem != null)
                    {
                        // give some indication of what will take place
                        logboxout("Binding Outlook and Google event: " + gitem.Summary + " (" + GoogleCalendar.FormatTime(gitem.Start) + ")");

                        // bind the properties together
                        OutlookCalendar.Instance.Bind(oitem, gitem);

                        // update the instance on the google calendar
                        GoogleCalendar.Instance.updateEntry(gitem);

                        // remove the outlook item and the google item from the lists...
                        // do not need to process them again for future iterations...
                        google_items.Remove(gitem);
                        outlook_items.Remove(oitem);

                        // save and close the outlook item
                        ((_AppointmentItem)oitem).Close(OlInspectorClose.olSave);

                        // need to release the com reference
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem);

                        // update the stats
                        ++bound_entries_found;
                    }
                    else
                    {
                        // give some indication of what will take place
                        logboxout("Creating Outlook event: " + gitem.Summary + " (" + GoogleCalendar.FormatTime(gitem.Start) + ")");

                        // the property does not exist... this means that the outlook calendar
                        // does not have this google entry...  after this call, the outlook item
                        // and the google item should be tied together by the use of properties...
                        oitem = OutlookCalendar.Instance.addEntry(gitem, cbAddDescription.Checked, cbAddReminders.Checked, cbAddAttendees.Checked);

                        try
                        {
                            // google currently does not have an updated id... this needs to be reflected on the server...
                            GoogleCalendar.Instance.updateEntry(gitem);
                        }
                        catch (System.Exception ex)
                        {
                            // unable to update the google entry for some reason...
                            // let the user know and the reason why...
                            logboxout("");
                            logboxout("Error: Google event cannot be bound to Outlook event!");
                            logboxout("Reason: " + ex.Message);
                            logboxout("");

                            // update the stats
                            ++errors;
                        }

                        // need to release the com reference
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem);

                        // update the stats
                        ++outlook_entries_added;
                    }
                }
                else
                {
                    // the property exists, so we need to determine if the event should be updated
                    // first, find the event in the list of outlook items...
                    AppointmentItem oitem = null;
                    foreach (var o in outlook_items)
                    {
                        if (outlook_id == OutlookCalendar.FormatEventID(o))
                        {
                            oitem = o; break;
                        }
                    }

                    if (oitem == null)
                    {
                        // give some indication of what will take place
                        logboxout("Removing Google event: " + gitem.Summary + " (" + GoogleCalendar.FormatTime(gitem.Start) + ")");

                        // the item does not exist, so it was removed from outlook calendar
                        // since it was removed from outlook, remove it from google
                        GoogleCalendar.Instance.deleteCalendarEntry(gitem);

                        // update the stats
                        ++google_entries_removed;
                    }
                    else
                    {
                        // this outlook item is about to be processed... no matter the action
                        // taken, we can remove it from the list of items, so as not
                        // to need to look at it again in future iterations...
                        outlook_items.Remove(oitem);

                        // the item does exist...
                        // determine if the event should be updated...
                        if ((signature(gitem) != signature(oitem) ||
                             OutlookGoogleSync.Utilities.ObtainUserBodyData(oitem.Body) != OutlookGoogleSync.Utilities.ObtainUserBodyData(gitem.Description)) &&
                            gitem.Updated > oitem.LastModificationTime)
                        {
                            // give some indication of what will take place
                            if (signature(gitem) != signature(oitem))
                            {
                                logboxout("Updating Outlook event: " + oitem.Subject + " (" + oitem.Start + ") ==> " + gitem.Summary + " (" + GoogleCalendar.FormatTime(gitem.Start) + ")");
                            }
                            else
                            {
                                logboxout("Updating Outlook event description: " + oitem.Subject + " (" + oitem.Start + ")");
                            }

                            // update the event based on the google item
                            OutlookCalendar.Instance.updateEntry(oitem, gitem, cbAddDescription.Checked, cbAddReminders.Checked, cbAddAttendees.Checked);

                            // update the status
                            ++outlook_entries_updated;
                        }

                        try
                        {
                            // save and close the outlook item
                            ((_AppointmentItem)oitem).Close(OlInspectorClose.olSave);
                        }
                        catch (System.Exception ex)
                        {
                            // unable to save the outlook entry for some reason...
                            // let the user know and the reason why...
                            logboxout("");
                            logboxout("Error: Outlook event cannot be saved!");
                            logboxout("Reason: " + ex.Message);
                            logboxout("");

                            // update the stats
                            ++errors;
                        }

                        // need to release the com reference
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(oitem);
                    }
                }
            }

            // close all the outlook items
            foreach (var o in outlook_items)
            {
                ((_AppointmentItem)o).Close(OlInspectorClose.olSave);

                System.Runtime.InteropServices.Marshal.ReleaseComObject(o);
            }

            // clear out both lists... these items have been processed...
            outlook_items.Clear();
            google_items.Clear();

            // summarize the changes made
            logboxout("--------------------------------------------------");
            logboxout("Google entries added: " + google_entries_added);
            logboxout("Google entries updated: " + google_entries_updated);
            logboxout("Google entries removed: " + google_entries_removed);
            logboxout("");
            logboxout("Outlook entries added: " + outlook_entries_added);
            logboxout("Outlook entries updated: " + outlook_entries_updated);
            logboxout("Outlook entries removed: " + outlook_entries_removed);
            logboxout("");
            logboxout("Bound entries found: " + bound_entries_found);
            logboxout("");
            logboxout("Errors found: " + errors);

            return(errors == 0);
        }