Exemple #1
0
        private static void SyncAppointmentNoOutlook(AppointmentMatch match, AppointmentsSynchronizer sync)
        {
            string outlookAppointmenttId = AppointmentPropertiesUtils.GetGoogleOutlookAppointmentId(sync.SyncProfile, match.GoogleAppointment);

            if (!string.IsNullOrEmpty(outlookAppointmenttId))
            {
                if (sync.SyncOption == SyncOption.GoogleToOutlookOnly || !sync.SyncDelete)
                {
                    return;
                }
                else if (!sync.PromptDelete)
                {
                    sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways;
                }
                else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways &&
                         sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways)
                {
                    using (var r = new ConflictResolver())
                    {
                        sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleAppointment);
                    }
                }
                switch (sync.DeleteGoogleResolution)
                {
                case DeleteResolution.KeepGoogle:
                case DeleteResolution.KeepGoogleAlways:
                    AppointmentPropertiesUtils.ResetGoogleOutlookAppointmentId(sync.SyncProfile, match.GoogleAppointment);
                    break;

                case DeleteResolution.DeleteGoogle:
                case DeleteResolution.DeleteGoogleAlways:
                    //Avoid recreating a OutlookAppointment already existing
                    //==> Delete this e instead if previous match existed but no match exists anymore
                    return;

                default:
                    throw new ApplicationException("Cancelled");
                }
            }


            if (sync.SyncOption == SyncOption.OutlookToGoogleOnly)
            {
                sync.SkippedCount++;
                Logger.Log(string.Format("Google appointment not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleAppointment.Summary), EventType.Information);
                return;
            }

            //create a Outlook appointment from Google appointment
            match.OutlookAppointment = AppointmentsSynchronizer.CreateOutlookAppointmentItem(AppointmentsSynchronizer.SyncAppointmentsFolder);

            sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions);
        }
        public static void SyncAppointment(AppointmentMatch match, Synchronizer sync)
        {
            if (match.GoogleAppointment == null && match.OutlookAppointment != null)
            {
                //no Google appointment
                string googleAppointmentId = AppointmentPropertiesUtils.GetOutlookGoogleAppointmentId(sync, match.OutlookAppointment);
                if (!string.IsNullOrEmpty(googleAppointmentId))
                {
                    //if (match.OutlookAppointment.IsRecurring && match.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster &&
                    //    (Syncronizer.MonthsInPast == 0 || new DateTime(DateTime.Now.AddMonths(-Syncronizer.MonthsInPast).Year, match.OutlookAppointment.End.Month, match.OutlookAppointment.End.Day) >= DateTime.Now.AddMonths(-Syncronizer.MonthsInPast)) &&
                    //    (Syncronizer.MonthsInFuture == 0 || new DateTime(DateTime.Now.AddMonths(-Syncronizer.MonthsInPast).Year, match.OutlookAppointment.Start.Month, match.OutlookAppointment.Start.Day) <= DateTime.Now.AddMonths(Syncronizer.MonthsInFuture))
                    //    ||
                    //    (Syncronizer.MonthsInPast == 0 || match.OutlookAppointment.End >= DateTime.Now.AddMonths(-Syncronizer.MonthsInPast)) &&
                    //    (Syncronizer.MonthsInFuture == 0 || match.OutlookAppointment.Start <= DateTime.Now.AddMonths(Syncronizer.MonthsInFuture))
                    //    )
                    //{

                    //Redundant check if exist, but in case an error occurred in MatchAppointments or not all appointments have been loaded (e.g. because months before/after constraint)
                    Event matchingGoogleAppointment = null;
                    if (sync.AllGoogleAppointments != null)
                    {
                        matchingGoogleAppointment = sync.GetGoogleAppointmentById(googleAppointmentId);
                    }
                    else
                    {
                        matchingGoogleAppointment = sync.LoadGoogleAppointments(googleAppointmentId, 0, 0, null, null);
                    }
                    if (matchingGoogleAppointment == null)
                    {
                        if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete)
                        {
                            return;
                        }
                        else if (!sync.PromptDelete && match.OutlookAppointment.Recipients.Count == 0)
                        {
                            sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways;
                        }
                        else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways &&
                                 sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways)
                        {
                            var r = new ConflictResolver();
                            sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookAppointment);
                        }
                        switch (sync.DeleteOutlookResolution)
                        {
                        case DeleteResolution.KeepOutlook:
                        case DeleteResolution.KeepOutlookAlways:
                            AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment);
                            break;

                        case DeleteResolution.DeleteOutlook:
                        case DeleteResolution.DeleteOutlookAlways:

                            if (match.OutlookAppointment.Recipients.Count > 1)
                            {
                                //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment
                                Logger.Log("Outlook Appointment not deleted, because multiple participants found,  invitation maybe NOT sent by Google: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information);
                                AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment);
                                break;
                            }
                            else
                            {
                                //Avoid recreating a GoogleAppointment already existing
                                //==> Delete this OutlookAppointment instead if previous match existed but no match exists anymore
                                return;
                            }

                        default:
                            throw new ApplicationException("Cancelled");
                        }
                    }
                    else
                    {
                        sync.SkippedCount++;
                        match.GoogleAppointment = matchingGoogleAppointment;
                        Logger.Log("Outlook Appointment not deleted, because still existing on Google side, maybe because months restriction: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information);
                        return;
                    }
                }
                //}

                if (sync.SyncOption == SyncOption.GoogleToOutlookOnly)
                {
                    sync.SkippedCount++;
                    Logger.Log(string.Format("Outlook appointment not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookAppointment.Subject), EventType.Information);
                    return;
                }

                //create a Google appointment from Outlook appointment
                match.GoogleAppointment = Factory.NewEvent();

                sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment);
            }
            else if (match.OutlookAppointment == null && match.GoogleAppointment != null)
            {
                //no Outlook appointment
                string outlookAppointmenttId = AppointmentPropertiesUtils.GetGoogleOutlookAppointmentId(sync.SyncProfile, match.GoogleAppointment);
                if (!string.IsNullOrEmpty(outlookAppointmenttId))
                {
                    if (sync.SyncOption == SyncOption.GoogleToOutlookOnly || !sync.SyncDelete)
                    {
                        return;
                    }
                    else if (!sync.PromptDelete)
                    {
                        sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways;
                    }
                    else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways &&
                             sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways)
                    {
                        var r = new ConflictResolver();
                        sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleAppointment);
                    }
                    switch (sync.DeleteGoogleResolution)
                    {
                    case DeleteResolution.KeepGoogle:
                    case DeleteResolution.KeepGoogleAlways:
                        AppointmentPropertiesUtils.ResetGoogleOutlookAppointmentId(sync.SyncProfile, match.GoogleAppointment);
                        break;

                    case DeleteResolution.DeleteGoogle:
                    case DeleteResolution.DeleteGoogleAlways:
                        //Avoid recreating a OutlookAppointment already existing
                        //==> Delete this googleAppointment instead if previous match existed but no match exists anymore
                        return;

                    default:
                        throw new ApplicationException("Cancelled");
                    }
                }


                if (sync.SyncOption == SyncOption.OutlookToGoogleOnly)
                {
                    sync.SkippedCount++;
                    Logger.Log(string.Format("Google appointment not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleAppointment.Summary), EventType.Information);
                    return;
                }

                //create a Outlook appointment from Google appointment
                match.OutlookAppointment = Synchronizer.CreateOutlookAppointmentItem(Synchronizer.SyncAppointmentsFolder);

                sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions);
            }
            else if (match.OutlookAppointment != null && match.GoogleAppointment != null)
            {
                //ToDo: Check how to overcome appointment recurrences, which need more than 60 seconds to update and therefore get updated again and again because of time tolerance 60 seconds violated again and again


                //merge appointment details

                //determine if this appointment pair were synchronized
                //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookAppointment, sync.OutlookPropertyNameUpdated);
                DateTime?lastSynced = AppointmentPropertiesUtils.GetOutlookLastSync(sync, match.OutlookAppointment);
                if (lastSynced.HasValue)
                {
                    //appointment pair was syncronysed before.

                    //determine if Google appointment was updated since last sync

                    //lastSynced is stored without seconds. take that into account.
                    DateTime lastUpdatedOutlook = match.OutlookAppointment.LastModificationTime.AddSeconds(-match.OutlookAppointment.LastModificationTime.Second);
                    DateTime lastUpdatedGoogle  = match.GoogleAppointment.Updated.Value.AddSeconds(-match.GoogleAppointment.Updated.Value.Second);
                    //consider GoogleAppointmentExceptions, because if they are updated, the master appointment doesn't have a new Saved TimeStamp
                    foreach (Event googleAppointment in match.GoogleAppointmentExceptions)
                    {
                        if (googleAppointment.Updated != null)//happens for cancelled events
                        {
                            DateTime lastUpdatedGoogleException = googleAppointment.Updated.Value.AddSeconds(-googleAppointment.Updated.Value.Second);
                            if (lastUpdatedGoogleException > lastUpdatedGoogle)
                            {
                                lastUpdatedGoogle = lastUpdatedGoogleException;
                            }
                        }
                        else if (match.OutlookAppointment.IsRecurring && match.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster)
                        {
                            Outlook.AppointmentItem outlookRecurrenceException = null;
                            try
                            {
                                var slaveRecurrence = match.OutlookAppointment.GetRecurrencePattern();
                                if (googleAppointment.OriginalStartTime != null && !string.IsNullOrEmpty(googleAppointment.OriginalStartTime.Date))
                                {
                                    outlookRecurrenceException = slaveRecurrence.GetOccurrence(DateTime.Parse(googleAppointment.OriginalStartTime.Date));
                                }
                                else if (googleAppointment.OriginalStartTime != null && googleAppointment.OriginalStartTime.DateTime != null)
                                {
                                    outlookRecurrenceException = slaveRecurrence.GetOccurrence(googleAppointment.OriginalStartTime.DateTime.Value);
                                }
                            }
                            catch (Exception ignored)
                            {
                                Logger.Log("Google Appointment with OriginalEvent found, but Outlook occurrence not found: " + googleAppointment.Summary + " - " + googleAppointment.OriginalStartTime.DateTime + ": " + ignored, EventType.Debug);
                            }


                            if (outlookRecurrenceException != null && outlookRecurrenceException.MeetingStatus != Outlook.OlMeetingStatus.olMeetingCanceled)
                            {
                                lastUpdatedGoogle = DateTime.Now;
                                break; //no need to search further, already newest date set
                            }
                        }
                    }

                    //check if both outlok and Google appointments where updated sync last sync
                    if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                        (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance)
                    {
                        //both appointments were updated.
                        //options: 1) ignore 2) loose one based on SyncOption
                        //throw new Exception("Both appointments were updated!");

                        switch (sync.SyncOption)
                        {
                        case SyncOption.MergeOutlookWins:
                        case SyncOption.OutlookToGoogleOnly:
                            //overwrite Google appointment
                            Logger.Log("Outlook and Google appointment have been updated, Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information);
                            sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment);
                            break;

                        case SyncOption.MergeGoogleWins:
                        case SyncOption.GoogleToOutlookOnly:
                            //overwrite outlook appointment
                            Logger.Log("Outlook and Google appointment have been updated, Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.GoogleAppointment.Summary + ".", EventType.Information);
                            sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions);
                            break;

                        case SyncOption.MergePrompt:
                            //promp for sync option
                            if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways &&
                                sync.ConflictResolution != ConflictResolution.OutlookWinsAlways &&
                                sync.ConflictResolution != ConflictResolution.SkipAlways)
                            {
                                var r = new ConflictResolver();
                                sync.ConflictResolution = r.Resolve(match.OutlookAppointment, match.GoogleAppointment, sync, false);
                            }
                            switch (sync.ConflictResolution)
                            {
                            case ConflictResolution.Skip:
                            case ConflictResolution.SkipAlways:
                                Logger.Log(string.Format("User skipped appointment ({0}).", match.ToString()), EventType.Information);
                                sync.SkippedCount++;
                                break;

                            case ConflictResolution.OutlookWins:
                            case ConflictResolution.OutlookWinsAlways:
                                sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment);
                                break;

                            case ConflictResolution.GoogleWins:
                            case ConflictResolution.GoogleWinsAlways:
                                sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions);
                                break;

                            default:
                                throw new ApplicationException("Cancelled");
                            }
                            break;
                        }
                        return;
                    }


                    //check if Outlook appointment was updated (with X second tolerance)
                    if (sync.SyncOption != SyncOption.GoogleToOutlookOnly &&
                        ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance ||
                         (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                         sync.SyncOption == SyncOption.OutlookToGoogleOnly
                        )
                        )
                    {
                        //Outlook appointment was changed or changed Google appointment will be overwritten

                        if ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                            sync.SyncOption == SyncOption.OutlookToGoogleOnly)
                        {
                            Logger.Log("Google appointment has been updated since last sync, but Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information);
                        }

                        sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment);

                        //at the moment use Outlook as "master" source of appointments - in the event of a conflict Google appointment will be overwritten.
                        //TODO: control conflict resolution by SyncOption
                        return;
                    }

                    //check if Google appointment was updated (with X second tolerance)
                    if (sync.SyncOption != SyncOption.OutlookToGoogleOnly &&
                        ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance ||
                         (int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                         sync.SyncOption == SyncOption.GoogleToOutlookOnly
                        )
                        )
                    {
                        //google appointment was changed or changed Outlook appointment will be overwritten

                        if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                            sync.SyncOption == SyncOption.GoogleToOutlookOnly)
                        {
                            Logger.Log("Outlook appointment has been updated since last sync, but Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information);
                        }

                        sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions);
                    }
                }
                else
                {
                    //appointments were never synced.
                    //merge appointments.
                    switch (sync.SyncOption)
                    {
                    case SyncOption.MergeOutlookWins:
                    case SyncOption.OutlookToGoogleOnly:
                        //overwrite Google appointment
                        sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment);
                        break;

                    case SyncOption.MergeGoogleWins:
                    case SyncOption.GoogleToOutlookOnly:
                        //overwrite outlook appointment
                        sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions);
                        break;

                    case SyncOption.MergePrompt:
                        //promp for sync option
                        if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways &&
                            sync.ConflictResolution != ConflictResolution.OutlookWinsAlways &&
                            sync.ConflictResolution != ConflictResolution.SkipAlways)
                        {
                            var r = new ConflictResolver();
                            sync.ConflictResolution = r.Resolve(match.OutlookAppointment, match.GoogleAppointment, sync, true);
                        }
                        switch (sync.ConflictResolution)
                        {
                        case ConflictResolution.Skip:
                        case ConflictResolution.SkipAlways:         //Keep both, Google AND Outlook
                            sync.Appointments.Add(new AppointmentMatch(match.OutlookAppointment, null));
                            sync.Appointments.Add(new AppointmentMatch(null, match.GoogleAppointment));
                            break;

                        case ConflictResolution.OutlookWins:
                        case ConflictResolution.OutlookWinsAlways:
                            sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment);
                            break;

                        case ConflictResolution.GoogleWins:
                        case ConflictResolution.GoogleWinsAlways:
                            sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions);
                            break;

                        default:
                            throw new ApplicationException("Canceled");
                        }
                        break;
                    }
                }
            }
            else
            {
                throw new ArgumentNullException("AppointmenttMatch has all peers null.");
            }
            //}
            //finally
            //{
            //if (outlookAppointment != null &&
            //    match.OutlookAppointment != null)
            //{
            //    match.OutlookAppointment.Update(outlookAppointment, sync);
            //    Marshal.ReleaseComObject(outlookAppointment);
            //    outlookAppointment = null;
            //}
            //}
        }
Exemple #3
0
        private static void SyncAppointmentNoGoogle(AppointmentMatch match, AppointmentsSynchronizer sync)
        {
            string googleAppointmentId = AppointmentPropertiesUtils.GetOutlookGoogleAppointmentId(sync, match.OutlookAppointment);

            if (!string.IsNullOrEmpty(googleAppointmentId))
            {
                //Redundant check if exist, but in case an error occurred in MatchAppointments or not all appointments have been loaded (e.g. because months before/after constraint)
                Event matchingGoogleAppointment = null;
                if (sync.AllGoogleAppointments != null)
                {
                    matchingGoogleAppointment = sync.GetGoogleAppointmentById(googleAppointmentId);
                }
                else
                {
                    matchingGoogleAppointment = sync.LoadGoogleAppointments(googleAppointmentId, null, null, null, null);
                }
                if (matchingGoogleAppointment == null)
                {
                    if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete)
                    {
                        return;
                    }
                    else if (!sync.PromptDelete && match.OutlookAppointment.Recipients.Count == 0)
                    {
                        sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways;
                    }
                    else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways &&
                             sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways)
                    {
                        using (var r = new ConflictResolver())
                        {
                            sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookAppointment);
                        }
                    }
                    switch (sync.DeleteOutlookResolution)
                    {
                    case DeleteResolution.KeepOutlook:
                    case DeleteResolution.KeepOutlookAlways:
                        AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment);
                        break;

                    case DeleteResolution.DeleteOutlook:
                    case DeleteResolution.DeleteOutlookAlways:

                        if (match.OutlookAppointment.Recipients.Count > 1)
                        {
                            //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment
                            Logger.Log("Outlook Appointment not deleted, because multiple participants found,  invitation maybe NOT sent by Google: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information);
                            AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment);
                            break;
                        }
                        else
                        {
                            //Avoid recreating a GoogleAppointment already existing
                            //==> Delete this OutlookAppointment instead if previous match existed but no match exists anymore
                            return;
                        }

                    default:
                        throw new ApplicationException("Cancelled");
                    }
                }
                else
                {
                    sync.SkippedCount++;
                    match.GoogleAppointment = matchingGoogleAppointment;
                    Logger.Log("Outlook Appointment not deleted, because still existing on Google side, maybe because months restriction: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information);
                    return;
                }
            }

            if (sync.SyncOption == SyncOption.GoogleToOutlookOnly)
            {
                sync.SkippedCount++;
                Logger.Log(string.Format("Outlook appointment not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookAppointment.Subject), EventType.Information);
                return;
            }

            //create a Google appointment from Outlook appointment
            match.GoogleAppointment = Factory.NewEvent();

            sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment);
        }
        public static void SyncContact(ContactMatch match, Synchronizer sync)
        {
            Outlook.ContactItem outlookContactItem = match.OutlookContact != null ? match.OutlookContact.GetOriginalItemFromOutlook() : null;

            try
            {
                if (match.GoogleContact == null && match.OutlookContact != null)
                {
                    //no google contact
                    string googleContactId = match.OutlookContact.UserProperties.GoogleContactId;
                    if (!string.IsNullOrEmpty(googleContactId))
                    {
                        //Redundant check if exist, but in case an error occurred in MatchContacts
                        Contact matchingGoogleContact = sync.GetGoogleContactById(googleContactId);
                        if (matchingGoogleContact == null)
                        {
                            if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete)
                                return;
                            else if (!sync.PromptDelete)
                                 sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways;
                            else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways &&
                                     sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways)
                            {
                                var r = new ConflictResolver();
                                sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookContact);
                            }
                            switch (sync.DeleteOutlookResolution)
                            {
                                case DeleteResolution.KeepOutlook:
                                case DeleteResolution.KeepOutlookAlways:
                                    ContactPropertiesUtils.ResetOutlookGoogleContactId(sync, outlookContactItem);
                                    break;
                                case DeleteResolution.DeleteOutlook:
                                case DeleteResolution.DeleteOutlookAlways:
                                    //Avoid recreating a GoogleContact already existing
                                    //==> Delete this outlookContact instead if previous match existed but no match exists anymore
                                    return;
                                default:
                                    throw new ApplicationException("Cancelled");
                            }
                        }
                    }

                    if (sync.SyncOption == SyncOption.GoogleToOutlookOnly)
                    {
                        sync.SkippedCount++;
                        Logger.Log(string.Format("Outlook Contact not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookContact.FileAs), EventType.Information);
                        return;
                    }

                    //create a Google contact from Outlook contact
                    match.GoogleContact = new Contact();

                    sync.UpdateContact(outlookContactItem, match.GoogleContact);

                }
                else if (match.OutlookContact == null && match.GoogleContact != null)
                {

                    // no outlook contact
                    string outlookId = ContactPropertiesUtils.GetGoogleOutlookContactId(sync.SyncProfile, match.GoogleContact);
                    if (outlookId != null)
                    {
                        if (sync.SyncOption == SyncOption.GoogleToOutlookOnly || !sync.SyncDelete)
                            return;
                        else if (!sync.PromptDelete)
                            sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways;
                        else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways &&
                                 sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways)
                        {
                            var r = new ConflictResolver();
                            sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleContact);
                        }
                        switch (sync.DeleteGoogleResolution)
                        {
                            case DeleteResolution.KeepGoogle:
                            case DeleteResolution.KeepGoogleAlways:
                                ContactPropertiesUtils.ResetGoogleOutlookContactId(sync.SyncProfile, match.GoogleContact);
                                break;
                            case DeleteResolution.DeleteGoogle:
                            case DeleteResolution.DeleteGoogleAlways:
                                //Avoid recreating a OutlookContact already existing
                                //==> Delete this googleContact instead if previous match existed but no match exists anymore
                                return;
                            default:
                                throw new ApplicationException("Cancelled");
                        }

                    }

                    if (sync.SyncOption == SyncOption.OutlookToGoogleOnly)
                    {
                        sync.SkippedCount++;
                        Logger.Log(string.Format("Google Contact not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleContact.Title), EventType.Information);
                        return;
                    }

                    //create a Outlook contact from Google contact
                    outlookContactItem = Synchronizer.CreateOutlookContactItem(Synchronizer.SyncContactsFolder);

                    sync.UpdateContact(match.GoogleContact, outlookContactItem);
                    match.OutlookContact = new OutlookContactInfo(outlookContactItem, sync);
                }
                else if (match.OutlookContact != null && match.GoogleContact != null)
                {
                    //merge contact details

                    //determine if this contact pair were synchronized
                    //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookContact, sync.OutlookPropertyNameUpdated);
                    DateTime? lastSynced = match.OutlookContact.UserProperties.LastSync;
                    if (lastSynced.HasValue)
                    {
                        //contact pair was syncronysed before.

                        //determine if google contact was updated since last sync

                        //lastSynced is stored without seconds. take that into account.
                        DateTime lastUpdatedOutlook = match.OutlookContact.LastModificationTime.AddSeconds(-match.OutlookContact.LastModificationTime.Second);
                        DateTime lastUpdatedGoogle = match.GoogleContact.Updated.AddSeconds(-match.GoogleContact.Updated.Second);

                        //check if both outlok and google contacts where updated sync last sync
                        if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance
                            && lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance)
                        {
                            //both contacts were updated.
                            //options: 1) ignore 2) loose one based on SyncOption
                            //throw new Exception("Both contacts were updated!");

                            switch (sync.SyncOption)
                            {
                                case SyncOption.MergeOutlookWins:
                                case SyncOption.OutlookToGoogleOnly:
                                    //overwrite google contact
                                    Logger.Log("Outlook and Google contact have been updated, Outlook contact is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookContact.FileAs + ".", EventType.Information);
                                    sync.UpdateContact(outlookContactItem, match.GoogleContact);
                                    break;
                                case SyncOption.MergeGoogleWins:
                                case SyncOption.GoogleToOutlookOnly:
                                    //overwrite outlook contact
                                    Logger.Log("Outlook and Google contact have been updated, Google contact is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookContact.FileAs + ".", EventType.Information);
                                    sync.UpdateContact(match.GoogleContact, outlookContactItem);
                                    break;
                                case SyncOption.MergePrompt:
                                    //promp for sync option
                                    if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways &&
                                        sync.ConflictResolution != ConflictResolution.OutlookWinsAlways &&
                                        sync.ConflictResolution != ConflictResolution.SkipAlways)
                                    {
                                        var r = new ConflictResolver();
                                        sync.ConflictResolution = r.Resolve(match, false);
                                    }
                                    switch (sync.ConflictResolution)
                                    {
                                        case ConflictResolution.Skip:
                                        case ConflictResolution.SkipAlways:
                                            Logger.Log(string.Format("User skipped contact ({0}).", match.ToString()), EventType.Information);
                                            sync.SkippedCount++;
                                            break;
                                        case ConflictResolution.OutlookWins:
                                        case ConflictResolution.OutlookWinsAlways:
                                            sync.UpdateContact(outlookContactItem, match.GoogleContact);
                                            break;
                                        case ConflictResolution.GoogleWins:
                                        case ConflictResolution.GoogleWinsAlways:
                                            sync.UpdateContact(match.GoogleContact, outlookContactItem);
                                            break;
                                        default:
                                            throw new ApplicationException("Cancelled");
                                    }
                                    break;
                            }
                            return;
                        }

                        //check if outlook contact was updated (with X second tolerance)
                        if (sync.SyncOption != SyncOption.GoogleToOutlookOnly &&
                            (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance ||
                             lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                             sync.SyncOption == SyncOption.OutlookToGoogleOnly
                            )
                           )
                        {
                            //outlook contact was changed or changed Google contact will be overwritten

                            if (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                                sync.SyncOption == SyncOption.OutlookToGoogleOnly)
                                Logger.Log("Google contact has been updated since last sync, but Outlook contact is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookContact.FileAs + ".", EventType.Information);

                            sync.UpdateContact(outlookContactItem, match.GoogleContact);

                            //at the moment use outlook as "master" source of contacts - in the event of a conflict google contact will be overwritten.
                            //TODO: control conflict resolution by SyncOption
                            return;
                        }

                        //check if google contact was updated (with X second tolerance)
                        if (sync.SyncOption != SyncOption.OutlookToGoogleOnly &&
                            (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance ||
                             lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                             sync.SyncOption == SyncOption.GoogleToOutlookOnly
                            )
                           )
                        {
                            //google contact was changed or changed Outlook contact will be overwritten

                            if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                                sync.SyncOption == SyncOption.GoogleToOutlookOnly)
                                Logger.Log("Outlook contact has been updated since last sync, but Google contact is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookContact.FileAs + ".", EventType.Information);

                            sync.UpdateContact(match.GoogleContact, outlookContactItem);
                        }
                    }
                    else
                    {
                        //contacts were never synced.
                        //merge contacts.
                        switch (sync.SyncOption)
                        {
                            case SyncOption.MergeOutlookWins:
                            case SyncOption.OutlookToGoogleOnly:
                                //overwrite google contact
                                sync.UpdateContact(outlookContactItem, match.GoogleContact);
                                break;
                            case SyncOption.MergeGoogleWins:
                            case SyncOption.GoogleToOutlookOnly:
                                //overwrite outlook contact
                                sync.UpdateContact(match.GoogleContact, outlookContactItem);
                                break;
                            case SyncOption.MergePrompt:
                                //promp for sync option
                                if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways &&
                                    sync.ConflictResolution != ConflictResolution.OutlookWinsAlways &&
                                    sync.ConflictResolution != ConflictResolution.SkipAlways)
                                {
                                    var r = new ConflictResolver();
                                    sync.ConflictResolution = r.Resolve(match, true);
                                }
                                switch (sync.ConflictResolution)
                                {
                                    case ConflictResolution.Skip:
                                    case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook
                                        sync.Contacts.Add(new ContactMatch(match.OutlookContact, null));
                                        sync.Contacts.Add(new ContactMatch(null, match.GoogleContact));
                                        break;
                                    case ConflictResolution.OutlookWins:
                                    case ConflictResolution.OutlookWinsAlways:
                                        sync.UpdateContact(outlookContactItem, match.GoogleContact);
                                        break;
                                    case ConflictResolution.GoogleWins:
                                    case ConflictResolution.GoogleWinsAlways:
                                        sync.UpdateContact(match.GoogleContact, outlookContactItem);
                                        break;
                                    default:
                                        throw new ApplicationException("Cancelled");
                                }
                                break;
                        }
                    }

                }
                else
                    throw new ArgumentNullException("ContactMatch has all peers null.");
            }
            catch (ArgumentNullException e)
            {
                throw e;
            }
            catch (Exception e)
            {
                throw new Exception("Error syncing contact " + (match.OutlookContact != null ? match.OutlookContact.FileAs : match.GoogleContact.Title) + ": " + e.Message, e);
            }
            finally
            {
                if (outlookContactItem != null &&
                    match.OutlookContact != null)
                {
                    match.OutlookContact.Update(outlookContactItem, sync);
                    Marshal.ReleaseComObject(outlookContactItem);
                    outlookContactItem = null;
                }
            }
        }
        /// <summary>
        /// Matches outlook and google contact by a) google id b) properties.
        /// </summary>
        /// <param name="sync">Syncronizer instance</param>
        /// <param name="duplicatesFound">Exception returned, if duplicates have been found (null else)</param>
        /// <returns>Returns a list of match pairs (outlook contact + google contact) for all contact. Those that weren't matche will have it's peer set to null</returns>
        public static List<ContactMatch> MatchContacts(Synchronizer sync, out DuplicateDataException duplicatesFound)
        {
            Logger.Log("Matching Outlook and Google contacts...", EventType.Information);
            var result = new List<ContactMatch>();

            var duplicateGoogleMatches = string.Empty;
            var duplicateOutlookContacts = string.Empty;
            sync.GoogleContactDuplicates = new Collection<ContactMatch>();
            sync.OutlookContactDuplicates = new Collection<ContactMatch>();

            var skippedOutlookIds = new List<string>();

            //for each outlook contact try to get google contact id from user properties
            //if no match - try to match by properties
            //if no match - create a new match pair without google contact.
            //foreach (Outlook._ContactItem olc in outlookContacts)
            var outlookContactsWithoutOutlookGoogleId = new Collection<OutlookContactInfo>();
            #region Match first all outlookContacts by sync id
            for (int i = 1; i <= sync.OutlookContacts.Count; i++)
            {
                Outlook.ContactItem olc = null;
                try
                {
                    olc = sync.OutlookContacts[i] as Outlook.ContactItem;
                    if (olc == null)
                    {
                        Logger.Log("Empty Outlook contact found (maybe distribution list). Skipping", EventType.Warning);
                        sync.SkippedCount++;
                        sync.SkippedCountNotMatches++;
                        continue;
                    }
                }
                catch (Exception ex)
                {
                    //this is needed because some contacts throw exceptions
                    Logger.Log("Accessing Outlook contact threw and exception. Skipping: " + ex.Message, EventType.Warning);
                    sync.SkippedCount++;
                    sync.SkippedCountNotMatches++;
                    continue;
                }

                try
                {

                    // sometimes contacts throw Exception when accessing their properties, so we give it a controlled try first.
                    try
                    {
                        string email1Address = olc.Email1Address;
                    }
                    catch (Exception ex)
                    {
                        string message = string.Format("Can't access contact details for outlook contact, got {0} - '{1}'. Skipping", ex.GetType().ToString(), ex.Message);
                        try
                        {
                            message = string.Format("{0} {1}.", message, olc.FileAs);
                            //remember skippedOutlookIds to later not delete them if found on Google side
                            skippedOutlookIds.Add(string.Copy(olc.EntryID));

                        }
                        catch
                        {
                            //e.g. if olc.FileAs also fails, ignore, because messge already set
                            //message = null;
                        }

                        //if (olc != null && message != null) // it's useless to say "we couldn't access some contacts properties
                        //{
                        Logger.Log(message, EventType.Warning);
                        //}
                        sync.SkippedCount++;
                        sync.SkippedCountNotMatches++;
                        continue;
                    }

                    if (!IsContactValid(olc))
                    {
                        Logger.Log(string.Format("Invalid outlook contact ({0}). Skipping", olc.FileAs), EventType.Warning);
                        skippedOutlookIds.Add(string.Copy(olc.EntryID));
                        sync.SkippedCount++;
                        sync.SkippedCountNotMatches++;
                        continue;
                    }

                    if (olc.Body != null && olc.Body.Length > 62000)
                    {
                        // notes field too large
                        Logger.Log(string.Format("Skipping outlook contact ({0}). Reduce the notes field to a maximum of 62.000 characters.", olc.FileAs), EventType.Warning);
                        skippedOutlookIds.Add(string.Copy(olc.EntryID));
                        sync.SkippedCount++;
                        sync.SkippedCountNotMatches++;
                        continue;
                    }

                    if (NotificationReceived != null)
                        NotificationReceived(String.Format("Matching contact {0} of {1} by id: {2} ...", i, sync.OutlookContacts.Count, olc.FileAs));

                    // Create our own info object to go into collections/lists, so we can free the Outlook objects and not run out of resources / exceed policy limits.
                    var olci = new OutlookContactInfo(olc, sync);

                    //try to match this contact to one of google contacts
                    Outlook.UserProperties userProperties = olc.UserProperties;
                    Outlook.UserProperty idProp = userProperties[sync.OutlookPropertyNameId];
                    try
                    {
                        if (idProp != null)
                        {
                            string googleContactId = string.Copy((string)idProp.Value);
                            Contact foundContact = sync.GetGoogleContactById(googleContactId);
                            var match = new ContactMatch(olci, null);

                            //Check first, that this is not a duplicate
                            //e.g. by copying an existing Outlook contact
                            //or by Outlook checked this as duplicate, but the user selected "Add new"
                            Collection<OutlookContactInfo> duplicates = sync.OutlookContactByProperty(sync.OutlookPropertyNameId, googleContactId);
                            if (duplicates.Count > 1)
                            {
                                foreach (OutlookContactInfo duplicate in duplicates)
                                {
                                    if (!string.IsNullOrEmpty(googleContactId))
                                    {
                                        Logger.Log("Duplicate Outlook contact found, resetting match and trying to match again: " + duplicate.FileAs, EventType.Warning);
                                        Outlook.ContactItem item = duplicate.GetOriginalItemFromOutlook();
                                        try
                                        {
                                            ContactPropertiesUtils.ResetOutlookGoogleContactId(sync, item);
                                            item.Save();
                                        }
                                        finally
                                        {
                                            if (item != null)
                                            {
                                                Marshal.ReleaseComObject(item);
                                                item = null;
                                            }
                                        }
                                    }
                                }

                                if (foundContact != null && !foundContact.Deleted)
                                {
                                    ContactPropertiesUtils.ResetGoogleOutlookContactId(sync.SyncProfile, foundContact);
                                }

                                outlookContactsWithoutOutlookGoogleId.Add(olci);
                            }
                            else
                            {

                                if (foundContact != null && !foundContact.Deleted)
                                {
                                    //we found a match by google id, that is not deleted yet
                                    match.AddGoogleContact(foundContact);
                                    result.Add(match);
                                    //Remove the contact from the list to not sync it twice
                                    sync.GoogleContacts.Remove(foundContact);
                                }
                                else
                                {
                                    outlookContactsWithoutOutlookGoogleId.Add(olci);
                                }
                            }
                        }
                        else
                            outlookContactsWithoutOutlookGoogleId.Add(olci);
                    }
                    finally
                    {
                        if (idProp != null)
                            Marshal.ReleaseComObject(idProp);
                        Marshal.ReleaseComObject(userProperties);
                    }
                }

                finally
                {
                    Marshal.ReleaseComObject(olc);
                    olc = null;
                }

            }
            #endregion
            #region Match the remaining contacts by properties

            for (int i = 0; i < outlookContactsWithoutOutlookGoogleId.Count; i++)
            {
                OutlookContactInfo olci = outlookContactsWithoutOutlookGoogleId[i];

                if (NotificationReceived != null)
                    NotificationReceived(String.Format("Matching contact {0} of {1} by unique properties: {2} ...", i + 1, outlookContactsWithoutOutlookGoogleId.Count, olci.FileAs));

                //no match found by id => match by common properties
                //create a default match pair with just outlook contact.
                var match = new ContactMatch(olci, null);

                //foreach google contact try to match and create a match pair if found some match(es)
                for (int j=sync.GoogleContacts.Count-1;j>=0;j--)
                {
                    Contact entry = sync.GoogleContacts[j];
                    if (entry.Deleted)
                        continue;

                    // only match if there is either an email or telephone or else
                    // a matching google contact will be created at each sync
                    //1. try to match by FileAs
                    //1.1 try to match by FullName
                    //2. try to match by primary email
                    //3. try to match by mobile phone number, don't match by home or business bumbers, because several people may share the same home or business number
                    //4. try to math Company, if Google Title is null, i.e. the contact doesn't have a name and title, only a company
                    string entryTitleFirstLastAndSuffix = OutlookContactInfo.GetTitleFirstLastAndSuffix(entry);
                    if (!string.IsNullOrEmpty(olci.FileAs) && !string.IsNullOrEmpty(entry.Title) && olci.FileAs.Equals(entry.Title.Replace("\r\n", "\n").Replace("\n", "\r\n"), StringComparison.InvariantCultureIgnoreCase) ||  //Replace twice to not replace a \r\n by \r\r\n. This is necessary because \r\n are saved as \n only to google
                        !string.IsNullOrEmpty(olci.FileAs) && !string.IsNullOrEmpty(entry.Name.FullName) && olci.FileAs.Equals(entry.Name.FullName.Replace("\r\n", "\n").Replace("\n", "\r\n"), StringComparison.InvariantCultureIgnoreCase) ||
                        !string.IsNullOrEmpty(olci.FullName) && !string.IsNullOrEmpty(entry.Name.FullName) && olci.FullName.Equals(entry.Name.FullName.Replace("\r\n", "\n").Replace("\n", "\r\n"), StringComparison.InvariantCultureIgnoreCase) ||
                        !string.IsNullOrEmpty(olci.TitleFirstLastAndSuffix) && !string.IsNullOrEmpty(entryTitleFirstLastAndSuffix) && olci.TitleFirstLastAndSuffix.Equals(entryTitleFirstLastAndSuffix.Replace("\r\n", "\n").Replace("\n", "\r\n"), StringComparison.InvariantCultureIgnoreCase) ||
                        !string.IsNullOrEmpty(olci.Email1Address) && entry.Emails.Count > 0 && olci.Email1Address.Equals(entry.Emails[0].Address, StringComparison.InvariantCultureIgnoreCase) ||
                        //!string.IsNullOrEmpty(olci.Email2Address) && FindEmail(olci.Email2Address, entry.Emails) != null ||
                        //!string.IsNullOrEmpty(olci.Email3Address) && FindEmail(olci.Email3Address, entry.Emails) != null ||
                        olci.MobileTelephoneNumber != null && FindPhone(olci.MobileTelephoneNumber, entry.Phonenumbers) != null ||
                        !string.IsNullOrEmpty(olci.FileAs) && string.IsNullOrEmpty(entry.Title) && entry.Organizations.Count > 0 && olci.FileAs.Equals(entry.Organizations[0].Name, StringComparison.InvariantCultureIgnoreCase)
                        )
                    {
                        match.AddGoogleContact(entry);
                        sync.GoogleContacts.Remove(entry);
                    }

                }

                #region find duplicates not needed now
                //if (match.GoogleContact == null && match.OutlookContact != null)
                //{//If GoogleContact, we have to expect a conflict because of Google insert of duplicates
                //    foreach (Contact entry in sync.GoogleContacts)
                //    {
                //        if (!string.IsNullOrEmpty(olc.FullName) && olc.FullName.Equals(entry.Title, StringComparison.InvariantCultureIgnoreCase) ||
                //         !string.IsNullOrEmpty(olc.FileAs) && olc.FileAs.Equals(entry.Title, StringComparison.InvariantCultureIgnoreCase) ||
                //         !string.IsNullOrEmpty(olc.Email1Address) && FindEmail(olc.Email1Address, entry.Emails) != null ||
                //         !string.IsNullOrEmpty(olc.Email2Address) && FindEmail(olc.Email1Address, entry.Emails) != null ||
                //         !string.IsNullOrEmpty(olc.Email3Address) && FindEmail(olc.Email1Address, entry.Emails) != null ||
                //         olc.MobileTelephoneNumber != null && FindPhone(olc.MobileTelephoneNumber, entry.Phonenumbers) != null
                //         )
                //    }
                //// check for each email 1,2 and 3 if a duplicate exists with same email, because Google doesn't like inserting new contacts with same email
                //Collection<Outlook.ContactItem> duplicates1 = new Collection<Outlook.ContactItem>();
                //Collection<Outlook.ContactItem> duplicates2 = new Collection<Outlook.ContactItem>();
                //Collection<Outlook.ContactItem> duplicates3 = new Collection<Outlook.ContactItem>();
                //if (!string.IsNullOrEmpty(olc.Email1Address))
                //    duplicates1 = sync.OutlookContactByEmail(olc.Email1Address);

                //if (!string.IsNullOrEmpty(olc.Email2Address))
                //    duplicates2 = sync.OutlookContactByEmail(olc.Email2Address);

                //if (!string.IsNullOrEmpty(olc.Email3Address))
                //    duplicates3 = sync.OutlookContactByEmail(olc.Email3Address);

                //if (duplicates1.Count > 1 || duplicates2.Count > 1 || duplicates3.Count > 1)
                //{
                //    if (string.IsNullOrEmpty(duplicatesEmailList))
                //        duplicatesEmailList = "Outlook contacts with the same email have been found and cannot be synchronized. Please delete duplicates of:";

                //    if (duplicates1.Count > 1)
                //        foreach (Outlook.ContactItem duplicate in duplicates1)
                //        {
                //            string str = olc.FileAs + " (" + olc.Email1Address + ")";
                //            if (!duplicatesEmailList.Contains(str))
                //                duplicatesEmailList += Environment.NewLine + str;
                //        }
                //    if (duplicates2.Count > 1)
                //        foreach (Outlook.ContactItem duplicate in duplicates2)
                //        {
                //            string str = olc.FileAs + " (" + olc.Email2Address + ")";
                //            if (!duplicatesEmailList.Contains(str))
                //                duplicatesEmailList += Environment.NewLine + str;
                //        }
                //    if (duplicates3.Count > 1)
                //        foreach (Outlook.ContactItem duplicate in duplicates3)
                //        {
                //            string str = olc.FileAs + " (" + olc.Email3Address + ")";
                //            if (!duplicatesEmailList.Contains(str))
                //                duplicatesEmailList += Environment.NewLine + str;
                //        }
                //    continue;
                //}
                //else if (!string.IsNullOrEmpty(olc.Email1Address))
                //{
                //    ContactMatch dup = result.Find(delegate(ContactMatch match)
                //    {
                //        return match.OutlookContact != null && match.OutlookContact.Email1Address == olc.Email1Address;
                //    });
                //    if (dup != null)
                //    {
                //        Logger.Log(string.Format("Duplicate contact found by Email1Address ({0}). Skipping", olc.FileAs), EventType.Information);
                //        continue;
                //    }
                //}

                //// check for unique mobile phone, because this sync tool uses the also the mobile phone to identify matches between Google and Outlook
                //Collection<Outlook.ContactItem> duplicatesMobile = new Collection<Outlook.ContactItem>();
                //if (!string.IsNullOrEmpty(olc.MobileTelephoneNumber))
                //    duplicatesMobile = sync.OutlookContactByProperty("MobileTelephoneNumber", olc.MobileTelephoneNumber);

                //if (duplicatesMobile.Count > 1)
                //{
                //    if (string.IsNullOrEmpty(duplicatesMobileList))
                //        duplicatesMobileList = "Outlook contacts with the same mobile phone have been found and cannot be synchronized. Please delete duplicates of:";

                //    foreach (Outlook.ContactItem duplicate in duplicatesMobile)
                //    {
                //        sync.OutlookContactDuplicates.Add(olc);
                //        string str = olc.FileAs + " (" + olc.MobileTelephoneNumber + ")";
                //        if (!duplicatesMobileList.Contains(str))
                //            duplicatesMobileList += Environment.NewLine + str;
                //    }
                //    continue;
                //}
                //else if (!string.IsNullOrEmpty(olc.MobileTelephoneNumber))
                //{
                //    ContactMatch dup = result.Find(delegate(ContactMatch match)
                //    {
                //        return match.OutlookContact != null && match.OutlookContact.MobileTelephoneNumber == olc.MobileTelephoneNumber;
                //    });
                //    if (dup != null)
                //    {
                //        Logger.Log(string.Format("Duplicate contact found by MobileTelephoneNumber ({0}). Skipping", olc.FileAs), EventType.Information);
                //        continue;
                //    }
                //}

                #endregion

                if (match.AllGoogleContactMatches == null || match.AllGoogleContactMatches.Count == 0)
                {
                    //Check, if this Outlook contact has a match in the google duplicates
                    bool duplicateFound = false;
                    foreach (ContactMatch duplicate in sync.GoogleContactDuplicates)
                    {
                        string entryTitleFirstLastAndSuffix = OutlookContactInfo.GetTitleFirstLastAndSuffix(duplicate.AllGoogleContactMatches[0]);
                        if (duplicate.AllGoogleContactMatches.Count > 0 &&
                            (!string.IsNullOrEmpty(olci.FileAs) && !string.IsNullOrEmpty(duplicate.AllGoogleContactMatches[0].Title) && olci.FileAs.Equals(duplicate.AllGoogleContactMatches[0].Title.Replace("\r\n", "\n").Replace("\n","\r\n"), StringComparison.InvariantCultureIgnoreCase) ||  //Replace twice to not replace a \r\n by \r\r\n. This is necessary because \r\n are saved as \n only to google
                             !string.IsNullOrEmpty(olci.FileAs) && !string.IsNullOrEmpty(duplicate.AllGoogleContactMatches[0].Name.FullName) && olci.FileAs.Equals(duplicate.AllGoogleContactMatches[0].Name.FullName.Replace("\r\n", "\n").Replace("\n", "\r\n"), StringComparison.InvariantCultureIgnoreCase) ||
                             !string.IsNullOrEmpty(olci.FullName) && !string.IsNullOrEmpty(duplicate.AllGoogleContactMatches[0].Name.FullName) && olci.FullName.Equals(duplicate.AllGoogleContactMatches[0].Name.FullName.Replace("\r\n", "\n").Replace("\n","\r\n"), StringComparison.InvariantCultureIgnoreCase) ||
                             !string.IsNullOrEmpty(olci.TitleFirstLastAndSuffix) && !string.IsNullOrEmpty(entryTitleFirstLastAndSuffix) && olci.TitleFirstLastAndSuffix.Equals(entryTitleFirstLastAndSuffix.Replace("\r\n", "\n").Replace("\n", "\r\n"), StringComparison.InvariantCultureIgnoreCase) ||
                             !string.IsNullOrEmpty(olci.Email1Address) && duplicate.AllGoogleContactMatches[0].Emails.Count > 0 && olci.Email1Address.Equals(duplicate.AllGoogleContactMatches[0].Emails[0].Address, StringComparison.InvariantCultureIgnoreCase) ||
                             //!string.IsNullOrEmpty(olci.Email2Address) && FindEmail(olci.Email2Address, duplicate.AllGoogleContactMatches[0].Emails) != null ||
                             //!string.IsNullOrEmpty(olci.Email3Address) && FindEmail(olci.Email3Address, duplicate.AllGoogleContactMatches[0].Emails) != null ||
                             olci.MobileTelephoneNumber != null && FindPhone(olci.MobileTelephoneNumber, duplicate.AllGoogleContactMatches[0].Phonenumbers) != null ||
                             !string.IsNullOrEmpty(olci.FileAs) && string.IsNullOrEmpty(duplicate.AllGoogleContactMatches[0].Title) && duplicate.AllGoogleContactMatches[0].Organizations.Count > 0 && olci.FileAs.Equals(duplicate.AllGoogleContactMatches[0].Organizations[0].Name, StringComparison.InvariantCultureIgnoreCase)
                            ) ||
                            !string.IsNullOrEmpty(olci.FileAs) && olci.FileAs.Equals(duplicate.OutlookContact.FileAs, StringComparison.InvariantCultureIgnoreCase) ||
                            !string.IsNullOrEmpty(olci.FullName) && olci.FullName.Equals(duplicate.OutlookContact.FullName, StringComparison.InvariantCultureIgnoreCase) ||
                            !string.IsNullOrEmpty(olci.TitleFirstLastAndSuffix) && olci.TitleFirstLastAndSuffix.Equals(duplicate.OutlookContact.TitleFirstLastAndSuffix, StringComparison.InvariantCultureIgnoreCase) ||
                            !string.IsNullOrEmpty(olci.Email1Address) && olci.Email1Address.Equals(duplicate.OutlookContact.Email1Address, StringComparison.InvariantCultureIgnoreCase) ||
                            //                                              olci.Email1Address.Equals(duplicate.OutlookContact.Email2Address, StringComparison.InvariantCultureIgnoreCase) ||
                            //                                              olci.Email1Address.Equals(duplicate.OutlookContact.Email3Address, StringComparison.InvariantCultureIgnoreCase)
                            //                                              ) ||
                            //!string.IsNullOrEmpty(olci.Email2Address) && (olci.Email2Address.Equals(duplicate.OutlookContact.Email1Address, StringComparison.InvariantCultureIgnoreCase) ||
                            //                                              olci.Email2Address.Equals(duplicate.OutlookContact.Email2Address, StringComparison.InvariantCultureIgnoreCase) ||
                            //                                              olci.Email2Address.Equals(duplicate.OutlookContact.Email3Address, StringComparison.InvariantCultureIgnoreCase)
                            //                                              ) ||
                            //!string.IsNullOrEmpty(olci.Email3Address) && (olci.Email3Address.Equals(duplicate.OutlookContact.Email1Address, StringComparison.InvariantCultureIgnoreCase) ||
                            //                                              olci.Email3Address.Equals(duplicate.OutlookContact.Email2Address, StringComparison.InvariantCultureIgnoreCase) ||
                            //                                              olci.Email3Address.Equals(duplicate.OutlookContact.Email3Address, StringComparison.InvariantCultureIgnoreCase)
                            //                                              ) ||
                            olci.MobileTelephoneNumber != null && olci.MobileTelephoneNumber.Equals(duplicate.OutlookContact.MobileTelephoneNumber) ||
                            !string.IsNullOrEmpty(olci.FileAs) && string.IsNullOrEmpty(duplicate.GoogleContact.Title) && duplicate.GoogleContact.Organizations.Count > 0 && olci.FileAs.Equals(duplicate.GoogleContact.Organizations[0].Name, StringComparison.InvariantCultureIgnoreCase)
                           )
                        {
                            duplicateFound = true;
                            duplicate.AddOutlookContact(olci);
                            sync.OutlookContactDuplicates.Add(match);
                            if (string.IsNullOrEmpty(duplicateOutlookContacts))
                                duplicateOutlookContacts = "Outlook contact found that has been already identified as duplicate Google contact (either same email, Mobile or FullName) and cannot be synchronized. Please delete or resolve duplicates of:";

                            string str = olci.FileAs + " (" + olci.Email1Address + ", " + olci.MobileTelephoneNumber + ")";
                            if (!duplicateOutlookContacts.Contains(str))
                                duplicateOutlookContacts += Environment.NewLine + str;

                            break;
                        }
                    }

                    if (!duplicateFound)
                        Logger.Log(string.Format("No match found for outlook contact ({0}) => {1}", olci.FileAs, (olci.UserProperties.GoogleContactId != null?"Delete from Outlook":"Add to Google")), EventType.Information);
                }
                else
                {
                    //Remember Google duplicates to later react to it when resetting matches or syncing
                    //ResetMatches: Also reset the duplicates
                    //Sync: Skip duplicates (don't sync duplicates to be fail safe)
                    if (match.AllGoogleContactMatches.Count > 1)
                    {
                        sync.GoogleContactDuplicates.Add(match);
                        foreach (Contact entry in match.AllGoogleContactMatches)
                        {
                            //Create message for duplicatesFound exception
                            if (string.IsNullOrEmpty(duplicateGoogleMatches))
                                duplicateGoogleMatches = "Outlook contacts matching with multiple Google contacts have been found (either same email, Mobile, FullName or company) and cannot be synchronized. Please delete or resolve duplicates of:";

                            string str = olci.FileAs + " (" + olci.Email1Address + ", " + olci.MobileTelephoneNumber + ")";
                            if (!duplicateGoogleMatches.Contains(str))
                                duplicateGoogleMatches += Environment.NewLine + str;
                        }
                    }

                }

                result.Add(match);
            }
            #endregion

            if (!string.IsNullOrEmpty(duplicateGoogleMatches) || !string.IsNullOrEmpty(duplicateOutlookContacts))
                duplicatesFound = new DuplicateDataException(duplicateGoogleMatches + Environment.NewLine + Environment.NewLine + duplicateOutlookContacts);
            else
                duplicatesFound = null;

            //return result;

            //for each google contact that's left (they will be nonmatched) create a new match pair without outlook contact.
            for (int i=0; i< sync.GoogleContacts.Count;i++)
            {
               Contact entry = sync.GoogleContacts[i];
               if (NotificationReceived != null)
                    NotificationReceived(String.Format("Adding new Google contact {0} of {1} by unique properties: {2} ...", i+1, sync.GoogleContacts.Count, entry.Title));

                // only match if there is either an email or mobile phone or a name or a company
                // otherwise a matching google contact will be created at each sync
                bool mobileExists = false;
                foreach (PhoneNumber phone in entry.Phonenumbers)
                {
                    if (phone.Rel == ContactsRelationships.IsMobile)
                    {
                        mobileExists = true;
                        break;
                    }
                }

                string googleOutlookId = ContactPropertiesUtils.GetGoogleOutlookContactId(sync.SyncProfile, entry);
                if (!String.IsNullOrEmpty(googleOutlookId) && skippedOutlookIds.Contains(googleOutlookId))
                {
                    Logger.Log("Skipped GoogleContact because Outlook contact couldn't be matched because of previous problem (see log): " + entry.Title, EventType.Warning);
                }
                else if (entry.Emails.Count == 0 && !mobileExists && string.IsNullOrEmpty(entry.Title) && (entry.Organizations.Count == 0 || string.IsNullOrEmpty(entry.Organizations[0].Name)))
                {
                    // no telephone and email

                    //ToDo: For now I use the ResolveDelete function, because it is almost the same, maybe we introduce a separate function for this ans also include DeleteGoogleAlways checkbox
                    var r = new ConflictResolver();
                    DeleteResolution res = r.ResolveDelete(entry);
                    if (res == DeleteResolution.DeleteGoogle || res == DeleteResolution.DeleteGoogleAlways)
                    {
                        ContactPropertiesUtils.SetGoogleOutlookContactId(sync.SyncProfile, entry, "-1"); //just set a dummy Id to delete this entry later on
                        sync.SaveContact(new ContactMatch(null, entry));
                    }
                    else
                    {
                        sync.SkippedCount++;
                        sync.SkippedCountNotMatches++;

                        Logger.Log("Skipped GoogleContact because no unique property found (Email1 or mobile or name or company):" + ContactMatch.GetSummary(entry), EventType.Warning);
                    }

                }
                else
                {
                    Logger.Log(string.Format("No match found for Google contact ({0}) => {1}", entry.Title, (!string.IsNullOrEmpty(googleOutlookId) ? "Delete from Google" : "Add to Outlook")), EventType.Information);
                    var match = new ContactMatch(null, entry);
                    result.Add(match);
                }
            }
            return result;
        }
        public static void SyncAppointment(AppointmentMatch match, Synchronizer sync)
        {
            if (match.GoogleAppointment == null && match.OutlookAppointment != null)
            {
                //no Google appointment
                string googleAppointmentId = AppointmentPropertiesUtils.GetOutlookGoogleAppointmentId(sync, match.OutlookAppointment);
                if (!string.IsNullOrEmpty(googleAppointmentId))
                {
                    //if (match.OutlookAppointment.IsRecurring && match.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster &&
                    //    (Syncronizer.MonthsInPast == 0 || new DateTime(DateTime.Now.AddMonths(-Syncronizer.MonthsInPast).Year, match.OutlookAppointment.End.Month, match.OutlookAppointment.End.Day) >= DateTime.Now.AddMonths(-Syncronizer.MonthsInPast)) &&
                    //    (Syncronizer.MonthsInFuture == 0 || new DateTime(DateTime.Now.AddMonths(-Syncronizer.MonthsInPast).Year, match.OutlookAppointment.Start.Month, match.OutlookAppointment.Start.Day) <= DateTime.Now.AddMonths(Syncronizer.MonthsInFuture))
                    //    ||
                    //    (Syncronizer.MonthsInPast == 0 || match.OutlookAppointment.End >= DateTime.Now.AddMonths(-Syncronizer.MonthsInPast)) &&
                    //    (Syncronizer.MonthsInFuture == 0 || match.OutlookAppointment.Start <= DateTime.Now.AddMonths(Syncronizer.MonthsInFuture))
                    //    )
                    //{

                        //Redundant check if exist, but in case an error occurred in MatchAppointments or not all appointments have been loaded (e.g. because months before/after constraint)
                        Event matchingGoogleAppointment = null;
                        if (sync.AllGoogleAppointments != null)
                            matchingGoogleAppointment = sync.GetGoogleAppointmentById(googleAppointmentId);
                        else
                            matchingGoogleAppointment = sync.LoadGoogleAppointments(googleAppointmentId, 0, 0, null, null);
                        if (matchingGoogleAppointment == null)
                        {
                            if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete)
                                return;
                            else if (!sync.PromptDelete && match.OutlookAppointment.Recipients.Count == 0)
                                sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways;
                            else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways &&
                                     sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways)
                            {
                                var r = new ConflictResolver();
                                sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookAppointment);
                            }
                            switch (sync.DeleteOutlookResolution)
                            {
                                case DeleteResolution.KeepOutlook:
                                case DeleteResolution.KeepOutlookAlways:
                                    AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment);
                                    break;
                                case DeleteResolution.DeleteOutlook:
                                case DeleteResolution.DeleteOutlookAlways:

                                    if (match.OutlookAppointment.Recipients.Count > 1)
                                    {
                                        //ToDo:Maybe find as better way, e.g. to ask the user, if he wants to overwrite the invalid appointment
                                        Logger.Log("Outlook Appointment not deleted, because multiple participants found,  invitation maybe NOT sent by Google: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information);
                                        AppointmentPropertiesUtils.ResetOutlookGoogleAppointmentId(sync, match.OutlookAppointment);
                                        break;
                                    }
                                    else
                                        //Avoid recreating a GoogleAppointment already existing
                                        //==> Delete this OutlookAppointment instead if previous match existed but no match exists anymore
                                        return;
                                default:
                                    throw new ApplicationException("Cancelled");
                            }
                        }
                        else
                        {
                            sync.SkippedCount++;
                            match.GoogleAppointment = matchingGoogleAppointment;
                            Logger.Log("Outlook Appointment not deleted, because still existing on Google side, maybe because months restriction: " + match.OutlookAppointment.Subject + " - " + match.OutlookAppointment.Start, EventType.Information);
                            return;
                        }

                    }
                //}

                if (sync.SyncOption == SyncOption.GoogleToOutlookOnly)
                {
                    sync.SkippedCount++;
                    Logger.Log(string.Format("Outlook appointment not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookAppointment.Subject), EventType.Information);
                    return;
                }

                //create a Google appointment from Outlook appointment
                match.GoogleAppointment = Factory.NewEvent();

                sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment);

            }
            else if (match.OutlookAppointment == null && match.GoogleAppointment != null)
            {
                //no Outlook appointment
                string outlookAppointmenttId = AppointmentPropertiesUtils.GetGoogleOutlookAppointmentId(sync.SyncProfile,match.GoogleAppointment);
                if (!string.IsNullOrEmpty(outlookAppointmenttId))
                {
                    if (sync.SyncOption == SyncOption.GoogleToOutlookOnly || !sync.SyncDelete)
                        return;
                    else if (!sync.PromptDelete)
                        sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways;
                    else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways &&
                             sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways)
                    {
                        var r = new ConflictResolver();
                        sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleAppointment);
                    }
                    switch (sync.DeleteGoogleResolution)
                    {
                        case DeleteResolution.KeepGoogle:
                        case DeleteResolution.KeepGoogleAlways:
                            AppointmentPropertiesUtils.ResetGoogleOutlookAppointmentId(sync.SyncProfile,match.GoogleAppointment);
                            break;
                        case DeleteResolution.DeleteGoogle:
                        case DeleteResolution.DeleteGoogleAlways:
                            //Avoid recreating a OutlookAppointment already existing
                            //==> Delete this googleAppointment instead if previous match existed but no match exists anymore
                            return;
                        default:
                            throw new ApplicationException("Cancelled");
                    }
                }

                if (sync.SyncOption == SyncOption.OutlookToGoogleOnly)
                {
                    sync.SkippedCount++;
                    Logger.Log(string.Format("Google appointment not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleAppointment.Summary), EventType.Information);
                    return;
                }

                //create a Outlook appointment from Google appointment
                match.OutlookAppointment = Synchronizer.CreateOutlookAppointmentItem(Synchronizer.SyncAppointmentsFolder);

                sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions);
            }
            else if (match.OutlookAppointment != null && match.GoogleAppointment != null)
            {
                //ToDo: Check how to overcome appointment recurrences, which need more than 60 seconds to update and therefore get updated again and again because of time tolerance 60 seconds violated again and again

                //merge appointment details

                //determine if this appointment pair were synchronized
                //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookAppointment, sync.OutlookPropertyNameUpdated);
                DateTime? lastSynced = AppointmentPropertiesUtils.GetOutlookLastSync(sync, match.OutlookAppointment);
                if (lastSynced.HasValue)
                {
                    //appointment pair was syncronysed before.

                    //determine if Google appointment was updated since last sync

                    //lastSynced is stored without seconds. take that into account.
                    DateTime lastUpdatedOutlook = match.OutlookAppointment.LastModificationTime.AddSeconds(-match.OutlookAppointment.LastModificationTime.Second);
                    DateTime lastUpdatedGoogle = match.GoogleAppointment.Updated.Value.AddSeconds(-match.GoogleAppointment.Updated.Value.Second);
                    //consider GoogleAppointmentExceptions, because if they are updated, the master appointment doesn't have a new Saved TimeStamp
                    foreach (Event googleAppointment in match.GoogleAppointmentExceptions)
                    {
                        if (googleAppointment.Updated != null)//happens for cancelled events
                        {
                            DateTime lastUpdatedGoogleException = googleAppointment.Updated.Value.AddSeconds(-googleAppointment.Updated.Value.Second);
                            if (lastUpdatedGoogleException > lastUpdatedGoogle)
                                lastUpdatedGoogle = lastUpdatedGoogleException;
                        }
                        else if (match.OutlookAppointment.IsRecurring && match.OutlookAppointment.RecurrenceState == Outlook.OlRecurrenceState.olApptMaster)
                        {
                            Outlook.AppointmentItem outlookRecurrenceException = null;
                            try
                            {
                                 var slaveRecurrence = match.OutlookAppointment.GetRecurrencePattern();
                                if (googleAppointment.OriginalStartTime != null && !string.IsNullOrEmpty(googleAppointment.OriginalStartTime.Date))
                                    outlookRecurrenceException = slaveRecurrence.GetOccurrence(DateTime.Parse(googleAppointment.OriginalStartTime.Date));
                                else if (googleAppointment.OriginalStartTime != null && googleAppointment.OriginalStartTime.DateTime != null)
                                    outlookRecurrenceException = slaveRecurrence.GetOccurrence(googleAppointment.OriginalStartTime.DateTime.Value);
                            }
                            catch (Exception ignored)
                            {
                                Logger.Log("Google Appointment with OriginalEvent found, but Outlook occurrence not found: " + googleAppointment.Summary + " - " + googleAppointment.OriginalStartTime.DateTime + ": " + ignored, EventType.Debug);
                            }

                            if (outlookRecurrenceException != null && outlookRecurrenceException.MeetingStatus != Outlook.OlMeetingStatus.olMeetingCanceled)
                            {
                                lastUpdatedGoogle = DateTime.Now;
                                break; //no need to search further, already newest date set
                            }
                        }
                    }

                    //check if both outlok and Google appointments where updated sync last sync
                    if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance
                        && (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance)
                    {
                        //both appointments were updated.
                        //options: 1) ignore 2) loose one based on SyncOption
                        //throw new Exception("Both appointments were updated!");

                        switch (sync.SyncOption)
                        {
                            case SyncOption.MergeOutlookWins:
                            case SyncOption.OutlookToGoogleOnly:
                                //overwrite Google appointment
                                Logger.Log("Outlook and Google appointment have been updated, Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information);
                                sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment);
                                break;
                            case SyncOption.MergeGoogleWins:
                            case SyncOption.GoogleToOutlookOnly:
                                //overwrite outlook appointment
                                Logger.Log("Outlook and Google appointment have been updated, Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.GoogleAppointment.Summary + ".", EventType.Information);
                                sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions);
                                break;
                            case SyncOption.MergePrompt:
                                //promp for sync option
                                if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways &&
                                    sync.ConflictResolution != ConflictResolution.OutlookWinsAlways &&
                                    sync.ConflictResolution != ConflictResolution.SkipAlways)
                                {
                                    var r = new ConflictResolver();
                                    sync.ConflictResolution = r.Resolve(match.OutlookAppointment, match.GoogleAppointment, sync, false);
                                }
                                switch (sync.ConflictResolution)
                                {
                                    case ConflictResolution.Skip:
                                    case ConflictResolution.SkipAlways:
                                        Logger.Log(string.Format("User skipped appointment ({0}).", match.ToString()), EventType.Information);
                                        sync.SkippedCount++;
                                        break;
                                    case ConflictResolution.OutlookWins:
                                    case ConflictResolution.OutlookWinsAlways:
                                        sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment);
                                        break;
                                    case ConflictResolution.GoogleWins:
                                    case ConflictResolution.GoogleWinsAlways:
                                        sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions);
                                        break;
                                    default:
                                        throw new ApplicationException("Cancelled");
                                }
                                break;
                        }
                        return;
                    }

                    //check if Outlook appointment was updated (with X second tolerance)
                    if (sync.SyncOption != SyncOption.GoogleToOutlookOnly &&
                        ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance ||
                         (int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                         sync.SyncOption == SyncOption.OutlookToGoogleOnly
                        )
                       )
                    {
                        //Outlook appointment was changed or changed Google appointment will be overwritten

                        if ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                            sync.SyncOption == SyncOption.OutlookToGoogleOnly)
                            Logger.Log("Google appointment has been updated since last sync, but Outlook appointment is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information);

                        sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment);

                        //at the moment use Outlook as "master" source of appointments - in the event of a conflict Google appointment will be overwritten.
                        //TODO: control conflict resolution by SyncOption
                        return;
                    }

                    //check if Google appointment was updated (with X second tolerance)
                    if (sync.SyncOption != SyncOption.OutlookToGoogleOnly &&
                        ((int)lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance ||
                         (int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                         sync.SyncOption == SyncOption.GoogleToOutlookOnly
                        )
                       )
                    {
                        //google appointment was changed or changed Outlook appointment will be overwritten

                        if ((int)lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                            sync.SyncOption == SyncOption.GoogleToOutlookOnly)
                            Logger.Log("Outlook appointment has been updated since last sync, but Google appointment is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookAppointment.Subject + ".", EventType.Information);

                        sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions);
                    }
                }
                else
                {
                    //appointments were never synced.
                    //merge appointments.
                    switch (sync.SyncOption)
                    {
                        case SyncOption.MergeOutlookWins:
                        case SyncOption.OutlookToGoogleOnly:
                            //overwrite Google appointment
                            sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment);
                            break;
                        case SyncOption.MergeGoogleWins:
                        case SyncOption.GoogleToOutlookOnly:
                            //overwrite outlook appointment
                            sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions);
                            break;
                        case SyncOption.MergePrompt:
                            //promp for sync option
                            if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways &&
                                sync.ConflictResolution != ConflictResolution.OutlookWinsAlways &&
                                    sync.ConflictResolution != ConflictResolution.SkipAlways)
                            {
                                var r = new ConflictResolver();
                                sync.ConflictResolution = r.Resolve(match.OutlookAppointment, match.GoogleAppointment, sync, true);
                            }
                            switch (sync.ConflictResolution)
                            {
                                case ConflictResolution.Skip:
                                case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook
                                    sync.Appointments.Add(new AppointmentMatch(match.OutlookAppointment, null));
                                    sync.Appointments.Add(new AppointmentMatch(null, match.GoogleAppointment));
                                    break;
                                case ConflictResolution.OutlookWins:
                                case ConflictResolution.OutlookWinsAlways:
                                    sync.UpdateAppointment(match.OutlookAppointment, ref match.GoogleAppointment);
                                    break;
                                case ConflictResolution.GoogleWins:
                                case ConflictResolution.GoogleWinsAlways:
                                    sync.UpdateAppointment(ref match.GoogleAppointment, match.OutlookAppointment, match.GoogleAppointmentExceptions);
                                    break;
                                default:
                                    throw new ApplicationException("Canceled");
                            }
                            break;
                    }
                }

            }
            else
                throw new ArgumentNullException("AppointmenttMatch has all peers null.");
            //}
            //finally
            //{
            //if (outlookAppointment != null &&
            //    match.OutlookAppointment != null)
            //{
            //    match.OutlookAppointment.Update(outlookAppointment, sync);
            //    Marshal.ReleaseComObject(outlookAppointment);
            //    outlookAppointment = null;
            //}
            //}
        }
Exemple #7
0
        public static void SyncNote(NoteMatch match, Syncronizer sync)
        {
            Outlook.NoteItem outlookNoteItem = match.OutlookNote;

            //try
            //{
            if (match.GoogleNote == null && match.OutlookNote != null)
            {
                //no google note
                string googleNotetId = NotePropertiesUtils.GetOutlookGoogleNoteId(sync, outlookNoteItem);
                if (!string.IsNullOrEmpty(googleNotetId))
                {
                    //Redundant check if exist, but in case an error occurred in MatchNotes
                    Document matchingGoogleNote = sync.GetGoogleNoteById(googleNotetId);
                    if (matchingGoogleNote == null)
                    {
                        if (!sync.PromptDelete)
                        {
                            sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways;
                        }
                        else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways &&
                                 sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways)
                        {
                            var r = new ConflictResolver();
                            sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookNote);
                        }
                    }
                    switch (sync.DeleteOutlookResolution)
                    {
                    case DeleteResolution.KeepOutlook:
                    case DeleteResolution.KeepOutlookAlways:
                        NotePropertiesUtils.ResetOutlookGoogleNoteId(sync, match.OutlookNote);
                        break;

                    case DeleteResolution.DeleteOutlook:
                    case DeleteResolution.DeleteOutlookAlways:
                        //Avoid recreating a GoogleNote already existing
                        //==> Delete this outlookNote instead if previous match existed but no match exists anymore
                        return;

                    default:
                        throw new ApplicationException("Cancelled");
                    }
                }

                if (sync.SyncOption == SyncOption.GoogleToOutlookOnly)
                {
                    sync.SkippedCount++;
                    Logger.Log(string.Format("Outlook Note not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookNote.Subject), EventType.Information);
                    return;
                }

                //create a Google note from Outlook note
                match.GoogleNote      = new Document();
                match.GoogleNote.Type = Document.DocumentType.Document;
                //match.GoogleNote.Categories.Add(new AtomCategory("http://schemas.google.com/docs/2007#document"));
                //match.GoogleNote.Categories.Add(new AtomCategory("document"));

                sync.UpdateNote(outlookNoteItem, match.GoogleNote);
            }
            else if (match.OutlookNote == null && match.GoogleNote != null)
            {
                // no outlook note
                if (NotePropertiesUtils.NoteFileExists(match.GoogleNote.Id, sync.SyncProfile))
                {
                    if (!sync.PromptDelete)
                    {
                        sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways;
                    }
                    else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways &&
                             sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways)
                    {
                        var r = new ConflictResolver();
                        sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleNote, sync);
                    }
                    switch (sync.DeleteGoogleResolution)
                    {
                    case DeleteResolution.KeepGoogle:
                    case DeleteResolution.KeepGoogleAlways:
                        System.IO.File.Delete(NotePropertiesUtils.GetFileName(match.GoogleNote.Id, sync.SyncProfile));
                        break;

                    case DeleteResolution.DeleteGoogle:
                    case DeleteResolution.DeleteGoogleAlways:
                        //Avoid recreating a OutlookNote already existing
                        //==> Delete this googleNote instead if previous match existed but no match exists anymore
                        return;

                    default:
                        throw new ApplicationException("Cancelled");
                    }
                }


                if (sync.SyncOption == SyncOption.OutlookToGoogleOnly)
                {
                    sync.SkippedCount++;
                    Logger.Log(string.Format("Google Note not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleNote.Title), EventType.Information);
                    return;
                }

                //create a Outlook note from Google note
                outlookNoteItem = Syncronizer.CreateOutlookNoteItem(Syncronizer.SyncNotesFolder);

                sync.UpdateNote(match.GoogleNote, outlookNoteItem);
                match.OutlookNote = outlookNoteItem;
            }
            else if (match.OutlookNote != null && match.GoogleNote != null)
            {
                //merge note details

                //determine if this note pair were syncronized
                //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookNote, sync.OutlookPropertyNameUpdated);
                DateTime?lastSynced = NotePropertiesUtils.GetOutlookLastSync(sync, outlookNoteItem);
                if (lastSynced.HasValue)
                {
                    //note pair was syncronysed before.

                    //determine if google note was updated since last sync

                    //lastSynced is stored without seconds. take that into account.
                    DateTime lastUpdatedOutlook = match.OutlookNote.LastModificationTime.AddSeconds(-match.OutlookNote.LastModificationTime.Second);
                    DateTime lastUpdatedGoogle  = match.GoogleNote.Updated.AddSeconds(-match.GoogleNote.Updated.Second);

                    //check if both outlok and google notes where updated sync last sync
                    if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                        lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance)
                    {
                        //both notes were updated.
                        //options: 1) ignore 2) loose one based on SyncOption
                        //throw new Exception("Both notes were updated!");

                        switch (sync.SyncOption)
                        {
                        case SyncOption.MergeOutlookWins:
                        case SyncOption.OutlookToGoogleOnly:
                            //overwrite google note
                            Logger.Log("Outlook and Google note have been updated, Outlook note is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information);
                            sync.UpdateNote(outlookNoteItem, match.GoogleNote);
                            break;

                        case SyncOption.MergeGoogleWins:
                        case SyncOption.GoogleToOutlookOnly:
                            //overwrite outlook note
                            Logger.Log("Outlook and Google note have been updated, Google note is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information);
                            sync.UpdateNote(match.GoogleNote, outlookNoteItem);
                            break;

                        case SyncOption.MergePrompt:
                            //promp for sync option
                            if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways &&
                                sync.ConflictResolution != ConflictResolution.OutlookWinsAlways &&
                                sync.ConflictResolution != ConflictResolution.SkipAlways)
                            {
                                var r = new ConflictResolver();
                                sync.ConflictResolution = r.Resolve(outlookNoteItem, match.GoogleNote, sync, false);
                            }
                            switch (sync.ConflictResolution)
                            {
                            case ConflictResolution.Skip:
                            case ConflictResolution.SkipAlways:
                                Logger.Log(string.Format("User skipped note ({0}).", match.ToString()), EventType.Information);
                                sync.SkippedCount++;
                                break;

                            case ConflictResolution.OutlookWins:
                            case ConflictResolution.OutlookWinsAlways:
                                sync.UpdateNote(outlookNoteItem, match.GoogleNote);
                                break;

                            case ConflictResolution.GoogleWins:
                            case ConflictResolution.GoogleWinsAlways:
                                sync.UpdateNote(match.GoogleNote, outlookNoteItem);
                                break;

                            default:
                                throw new ApplicationException("Canceled");
                            }
                            break;
                        }
                        return;
                    }


                    //check if outlook note was updated (with X second tolerance)
                    if (sync.SyncOption != SyncOption.GoogleToOutlookOnly &&
                        (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance ||
                         lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                         sync.SyncOption == SyncOption.OutlookToGoogleOnly
                        )
                        )
                    {
                        //outlook note was changed or changed Google note will be overwritten

                        if (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                            sync.SyncOption == SyncOption.OutlookToGoogleOnly)
                        {
                            Logger.Log("Google note has been updated since last sync, but Outlook note is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information);
                        }

                        sync.UpdateNote(outlookNoteItem, match.GoogleNote);

                        //at the moment use outlook as "master" source of notes - in the event of a conflict google note will be overwritten.
                        //TODO: control conflict resolution by SyncOption
                        return;
                    }

                    //check if google note was updated (with X second tolerance)
                    if (sync.SyncOption != SyncOption.OutlookToGoogleOnly &&
                        (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance ||
                         lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                         sync.SyncOption == SyncOption.GoogleToOutlookOnly
                        )
                        )
                    {
                        //google note was changed or changed Outlook note will be overwritten

                        if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                            sync.SyncOption == SyncOption.GoogleToOutlookOnly)
                        {
                            Logger.Log("Outlook note has been updated since last sync, but Google note is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information);
                        }

                        sync.UpdateNote(match.GoogleNote, outlookNoteItem);
                    }
                }
                else
                {
                    //notes were never synced.
                    //merge notes.
                    switch (sync.SyncOption)
                    {
                    case SyncOption.MergeOutlookWins:
                    case SyncOption.OutlookToGoogleOnly:
                        //overwrite google note
                        sync.UpdateNote(outlookNoteItem, match.GoogleNote);
                        break;

                    case SyncOption.MergeGoogleWins:
                    case SyncOption.GoogleToOutlookOnly:
                        //overwrite outlook note
                        sync.UpdateNote(match.GoogleNote, outlookNoteItem);
                        break;

                    case SyncOption.MergePrompt:
                        //promp for sync option
                        if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways &&
                            sync.ConflictResolution != ConflictResolution.OutlookWinsAlways &&
                            sync.ConflictResolution != ConflictResolution.SkipAlways)
                        {
                            var r = new ConflictResolver();
                            sync.ConflictResolution = r.Resolve(outlookNoteItem, match.GoogleNote, sync, true);
                        }
                        switch (sync.ConflictResolution)
                        {
                        case ConflictResolution.Skip:
                        case ConflictResolution.SkipAlways:             //Keep both, Google AND Outlook
                            sync.Notes.Add(new NoteMatch(match.OutlookNote, null));
                            sync.Notes.Add(new NoteMatch(null, match.GoogleNote));
                            break;

                        case ConflictResolution.OutlookWins:
                        case ConflictResolution.OutlookWinsAlways:
                            sync.UpdateNote(outlookNoteItem, match.GoogleNote);
                            break;

                        case ConflictResolution.GoogleWins:
                        case ConflictResolution.GoogleWinsAlways:
                            sync.UpdateNote(match.GoogleNote, outlookNoteItem);
                            break;

                        default:
                            throw new ApplicationException("Canceled");
                        }
                        break;
                    }
                }
            }
            else
            {
                throw new ArgumentNullException("NotetMatch has all peers null.");
            }
            //}
            //finally
            //{
            //if (outlookNoteItem != null &&
            //    match.OutlookNote != null)
            //{
            //    match.OutlookNote.Update(outlookNoteItem, sync);
            //    Marshal.ReleaseComObject(outlookNoteItem);
            //    outlookNoteItem = null;
            //}
            //}
        }
        public static void SyncNote(NoteMatch match, Synchronizer sync)
        {
            Outlook.NoteItem outlookNoteItem = match.OutlookNote;

            //try
            //{
                if (match.GoogleNote == null && match.OutlookNote != null)
                {
                    //no Google note
                    string googleNotetId = NotePropertiesUtils.GetOutlookGoogleNoteId(sync, outlookNoteItem);
                    if (!string.IsNullOrEmpty(googleNotetId))
                    {
                        //Redundant check if exist, but in case an error occurred in MatchNotes
                        Document matchingGoogleNote = sync.GetGoogleNoteById(googleNotetId);
                        if (matchingGoogleNote == null)
                        {
                            if (sync.SyncOption == SyncOption.OutlookToGoogleOnly || !sync.SyncDelete)
                                return;
                            else if (!sync.PromptDelete)
                                sync.DeleteOutlookResolution = DeleteResolution.DeleteOutlookAlways;
                            else if (sync.DeleteOutlookResolution != DeleteResolution.DeleteOutlookAlways &&
                                     sync.DeleteOutlookResolution != DeleteResolution.KeepOutlookAlways)
                            {
                                var r = new ConflictResolver();
                                sync.DeleteOutlookResolution = r.ResolveDelete(match.OutlookNote);
                            }
                            switch (sync.DeleteOutlookResolution)
                            {
                                case DeleteResolution.KeepOutlook:
                                case DeleteResolution.KeepOutlookAlways:
                                    NotePropertiesUtils.ResetOutlookGoogleNoteId(sync, match.OutlookNote);
                                    break;
                                case DeleteResolution.DeleteOutlook:
                                case DeleteResolution.DeleteOutlookAlways:
                                    //Avoid recreating a GoogleNote already existing
                                    //==> Delete this outlookNote instead if previous match existed but no match exists anymore
                                    return;
                                default:
                                    throw new ApplicationException("Cancelled");
                            }
                        }
                    }

                    if (sync.SyncOption == SyncOption.GoogleToOutlookOnly)
                    {
                        sync.SkippedCount++;
                        Logger.Log(string.Format("Outlook Note not added to Google, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.OutlookNote.Subject), EventType.Information);
                        return;
                    }

                    //create a Google note from Outlook note
                    match.GoogleNote = new Document();
                    match.GoogleNote.Type = Document.DocumentType.Document;
                    //match.GoogleNote.Categories.Add(new AtomCategory("http://schemas.google.com/docs/2007#document"));
                    //match.GoogleNote.Categories.Add(new AtomCategory("document"));

                    sync.UpdateNote(outlookNoteItem, match.GoogleNote);

                }
                else if (match.OutlookNote == null && match.GoogleNote != null)
                {

                    // no outlook note
                    if (NotePropertiesUtils.NoteFileExists(match.GoogleNote.Id, sync.SyncProfile))
                    {
                        if (sync.SyncOption == SyncOption.GoogleToOutlookOnly || !sync.SyncDelete)
                            return;
                        else if (!sync.PromptDelete)
                            sync.DeleteGoogleResolution = DeleteResolution.DeleteGoogleAlways;
                        else if (sync.DeleteGoogleResolution != DeleteResolution.DeleteGoogleAlways &&
                                 sync.DeleteGoogleResolution != DeleteResolution.KeepGoogleAlways)
                        {
                            var r = new ConflictResolver();
                            sync.DeleteGoogleResolution = r.ResolveDelete(match.GoogleNote, sync);
                        }
                        switch (sync.DeleteGoogleResolution)
                        {
                            case DeleteResolution.KeepGoogle:
                            case DeleteResolution.KeepGoogleAlways:
                                System.IO.File.Delete(NotePropertiesUtils.GetFileName(match.GoogleNote.Id, sync.SyncProfile));
                                break;
                            case DeleteResolution.DeleteGoogle:
                            case DeleteResolution.DeleteGoogleAlways:
                                //Avoid recreating a OutlookNote already existing
                                //==> Delete this googleNote instead if previous match existed but no match exists anymore
                                return;
                            default:
                                throw new ApplicationException("Cancelled");
                        }
                    }

                    if (sync.SyncOption == SyncOption.OutlookToGoogleOnly)
                    {
                        sync.SkippedCount++;
                        Logger.Log(string.Format("Google Note not added to Outlook, because of SyncOption " + sync.SyncOption.ToString() + ": {0}", match.GoogleNote.Title), EventType.Information);
                        return;
                    }

                    //create a Outlook note from Google note
                    outlookNoteItem = Synchronizer.CreateOutlookNoteItem(Synchronizer.SyncNotesFolder);

                    sync.UpdateNote(match.GoogleNote, outlookNoteItem);
                    match.OutlookNote = outlookNoteItem;
                }
                else if (match.OutlookNote != null && match.GoogleNote != null)
                {
                    //merge note details

                    //determine if this note pair were synchronized
                    //DateTime? lastUpdated = GetOutlookPropertyValueDateTime(match.OutlookNote, sync.OutlookPropertyNameUpdated);
                    DateTime? lastSynced = NotePropertiesUtils.GetOutlookLastSync(sync,outlookNoteItem);
                    if (lastSynced.HasValue)
                    {
                        //note pair was syncronysed before.

                        //determine if google note was updated since last sync

                        //lastSynced is stored without seconds. take that into account.
                        DateTime lastUpdatedOutlook = match.OutlookNote.LastModificationTime.AddSeconds(-match.OutlookNote.LastModificationTime.Second);
                        DateTime lastUpdatedGoogle = match.GoogleNote.Updated.AddSeconds(-match.GoogleNote.Updated.Second);

                        //check if both outlok and google notes where updated sync last sync
                        if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance
                            && lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance)
                        {
                            //both notes were updated.
                            //options: 1) ignore 2) loose one based on SyncOption
                            //throw new Exception("Both notes were updated!");

                            switch (sync.SyncOption)
                            {
                                case SyncOption.MergeOutlookWins:
                                case SyncOption.OutlookToGoogleOnly:
                                    //overwrite google note
                                    Logger.Log("Outlook and Google note have been updated, Outlook note is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information);
                                    sync.UpdateNote(outlookNoteItem, match.GoogleNote);
                                    break;
                                case SyncOption.MergeGoogleWins:
                                case SyncOption.GoogleToOutlookOnly:
                                    //overwrite outlook note
                                    Logger.Log("Outlook and Google note have been updated, Google note is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information);
                                    sync.UpdateNote(match.GoogleNote, outlookNoteItem);
                                    break;
                                case SyncOption.MergePrompt:
                                    //promp for sync option
                                    if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways &&
                                        sync.ConflictResolution != ConflictResolution.OutlookWinsAlways &&
                                        sync.ConflictResolution != ConflictResolution.SkipAlways)
                                    {
                                        var r = new ConflictResolver();
                                        sync.ConflictResolution = r.Resolve(outlookNoteItem, match.GoogleNote, sync, false);
                                    }
                                    switch (sync.ConflictResolution)
                                    {
                                        case ConflictResolution.Skip:
                                        case ConflictResolution.SkipAlways:
                                            Logger.Log(string.Format("User skipped note ({0}).", match.ToString()), EventType.Information);
                                            sync.SkippedCount++;
                                            break;
                                        case ConflictResolution.OutlookWins:
                                        case ConflictResolution.OutlookWinsAlways:
                                            sync.UpdateNote(outlookNoteItem, match.GoogleNote);
                                            break;
                                        case ConflictResolution.GoogleWins:
                                        case ConflictResolution.GoogleWinsAlways:
                                            sync.UpdateNote(match.GoogleNote, outlookNoteItem);
                                            break;
                                        default:
                                            throw new ApplicationException("Canceled");
                                    }
                                    break;
                            }
                            return;
                        }

                        //check if outlook note was updated (with X second tolerance)
                        if (sync.SyncOption != SyncOption.GoogleToOutlookOnly &&
                            (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance ||
                             lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                             sync.SyncOption == SyncOption.OutlookToGoogleOnly
                            )
                           )
                        {
                            //outlook note was changed or changed Google note will be overwritten

                            if (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                                sync.SyncOption == SyncOption.OutlookToGoogleOnly)
                                Logger.Log("Google note has been updated since last sync, but Outlook note is overwriting Google because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information);

                            sync.UpdateNote(outlookNoteItem, match.GoogleNote);

                            //at the moment use outlook as "master" source of notes - in the event of a conflict google note will be overwritten.
                            //TODO: control conflict resolution by SyncOption
                            return;
                        }

                        //check if google note was updated (with X second tolerance)
                        if (sync.SyncOption != SyncOption.OutlookToGoogleOnly &&
                            (lastUpdatedGoogle.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance ||
                             lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                             sync.SyncOption == SyncOption.GoogleToOutlookOnly
                            )
                           )
                        {
                            //google note was changed or changed Outlook note will be overwritten

                            if (lastUpdatedOutlook.Subtract(lastSynced.Value).TotalSeconds > TimeTolerance &&
                                sync.SyncOption == SyncOption.GoogleToOutlookOnly)
                                Logger.Log("Outlook note has been updated since last sync, but Google note is overwriting Outlook because of SyncOption " + sync.SyncOption + ": " + match.OutlookNote.Subject + ".", EventType.Information);

                            sync.UpdateNote(match.GoogleNote, outlookNoteItem);
                        }
                    }
                    else
                    {
                        //notes were never synced.
                        //merge notes.
                        switch (sync.SyncOption)
                        {
                            case SyncOption.MergeOutlookWins:
                            case SyncOption.OutlookToGoogleOnly:
                                //overwrite google note
                                sync.UpdateNote(outlookNoteItem, match.GoogleNote);
                                break;
                            case SyncOption.MergeGoogleWins:
                            case SyncOption.GoogleToOutlookOnly:
                                //overwrite outlook note
                                sync.UpdateNote(match.GoogleNote, outlookNoteItem);
                                break;
                            case SyncOption.MergePrompt:
                                //promp for sync option
                                if (sync.ConflictResolution != ConflictResolution.GoogleWinsAlways &&
                                    sync.ConflictResolution != ConflictResolution.OutlookWinsAlways &&
                                        sync.ConflictResolution != ConflictResolution.SkipAlways)
                                {
                                    var r = new ConflictResolver();
                                    sync.ConflictResolution = r.Resolve(outlookNoteItem, match.GoogleNote, sync, true);
                                }
                                switch (sync.ConflictResolution)
                                {
                                    case ConflictResolution.Skip:
                                    case ConflictResolution.SkipAlways: //Keep both, Google AND Outlook
                                        sync.Notes.Add(new NoteMatch(match.OutlookNote, null));
                                        sync.Notes.Add(new NoteMatch(null, match.GoogleNote));
                                        break;
                                    case ConflictResolution.OutlookWins:
                                    case ConflictResolution.OutlookWinsAlways:
                                        sync.UpdateNote(outlookNoteItem, match.GoogleNote);
                                        break;
                                    case ConflictResolution.GoogleWins:
                                    case ConflictResolution.GoogleWinsAlways:
                                        sync.UpdateNote(match.GoogleNote, outlookNoteItem);
                                        break;
                                    default:
                                        throw new ApplicationException("Canceled");
                                }
                                break;
                        }
                    }

                }
                else
                    throw new ArgumentNullException("NotetMatch has all peers null.");
            //}
            //finally
            //{
                //if (outlookNoteItem != null &&
                //    match.OutlookNote != null)
                //{
                //    match.OutlookNote.Update(outlookNoteItem, sync);
                //    Marshal.ReleaseComObject(outlookNoteItem);
                //    outlookNoteItem = null;
                //}
            //}
        }