private static void ApplyChangeSet(string root, ChangeSet pendingChange, List<string> playlist)
        {
            using (log4net.ThreadContext.Stacks["NDC"].Push("Deferred Removing iTrack"))
            {
                foreach (var track in pendingChange.RemoveChanges)
                {
                    playlist.Remove(track.GetPlaylistLine(root));
                }
            }

            using (log4net.ThreadContext.Stacks["NDC"].Push("Deferred Adding iTrack"))
            {
                foreach (var addChange in pendingChange.AddChanges)
                {
                    if(!playlist.Contains(addChange))
                        playlist.Add(addChange);
                }
            }
        }
        public Response Query([RequestBody]PlaylistRequest request)
        {
            SongDb.Clear();
            PlaylistDb.Clear();

            Response errorResponse;
            if (!request.CheckValidate(out errorResponse)) return errorResponse;

            using (ThreadContext.Stacks["NDC"].Push("Client " + request.DeviceId))
            {
                Status = "Client " + request.DeviceId + " connected.";
                Log.Info("Client " + request.DeviceId + " connected.");
                Log.Debug("Received Data:" + Environment.NewLine + request);

                string playlistName = Path.GetFileNameWithoutExtension(request.PlaylistDevicePath);

                Subscription subscription = DataManager.GetSubscription(request.SafeDeviceId);
                SubscriptionManager subManager = subscription != null ? new SubscriptionManager(subscription) : null;

                string playlistPath = DataManager.GetDevicePlaylistPath(request);

                List<string> reconciledPlaylist;

                // Read and sort the playlists
                Log.Info("Loading Phone Playlist...");
                List<string> devicePlaylist = new List<string>(request.PlaylistData);

                Status = string.Format("Loading iTunes Library ({0})...", playlistName);
                Log.InfoFormat("Loading iTunes Library ({0})...", playlistName);
                List<string> desktopPlaylist;

                XmliTunesLibrary library = CachedXmlLibrary.Library;

                IPlaylist playlist = library.GetFirstPlaylistByName(playlistName);
                if (playlist != null)
                {
                    desktopPlaylist = library.GeneratePlaylist(playlist, request.DeviceMediaRoot);
                }
                else
                {
                    // Playlist was deleted on the server
                    Log.WarnFormat("Fail. Playlist ({0}) does not exist", playlistName);

                    // We're sending an error message, along with a list of track that are no longer subscribed and
                    // can be safely removed.
                    var wipeActions =
                        request.PlaylistData.Select(s => new SyncAction {DeviceLocation = s, Type = SyncType.Remove});

                    if (subManager != null)
                        wipeActions = subManager.FixProposedDeletes(library, wipeActions);

                    return new SyncResponse
                               {
                                   ErrorMessage = "Playlist does not exist",
                                   Error = (int) SyncResponseError.PlaylistNotFound,
                                   PlaylistDevicePath = request.PlaylistDevicePath,
                                   Actions = wipeActions.ToArray()
                               };
                }

                // changes that were made on the DESKTOP. Apply to DEVICE.
                IEnumerable<SyncAction> changesOnDesktop = DiffHandler.Diff(desktopPlaylist, devicePlaylist);

                // Make sure we don't delete any tracks still present in other playlists.
                if (subManager != null)
                    changesOnDesktop = subManager.FixProposedDeletes(library, changesOnDesktop);

                // We use different files for the serving and referencing playlists because this allows us to delay
                // committing a playlist as reference playlist until the client has retrieved the playlist.
                // In situations where the sync is interrupted, it can otherwise lead to the server having incorrect
                // state information and results in improper deletion of tracks from iTunes playlist.
                string playlistRefPath = DataManager.GetReferencePlaylistPath(playlistPath);
                string pendingChangesPath = DataManager.GetChangeSetCollectionPath(playlistPath);

            #if ENABLE_SYNC_TO_ITUNES
                if (File.Exists(playlistRefPath) && !Helper.IsAlbumOrArtistPlaylist(playlistName))
                {
                    // load any pending changes
                    ChangeSetCollection pendingChanges = ChangeSetCollection.DeserializeOrCreate(pendingChangesPath);

                    // apply those changes to the desktopPlaylist, so we can move into the expected state
                    ApplyChangeSet(request.DeviceMediaRoot, pendingChanges, desktopPlaylist);

                    Log.InfoFormat("Loading reference playlist {0}...", playlistRefPath);
                    List<string> referencePlaylist = Helper.LoadPlaylist(playlistRefPath);

                    changesOnDesktop = DiffHandler.Diff(desktopPlaylist, referencePlaylist);
                    IEnumerable<SyncAction> changesOnDevice = DiffHandler.Diff(devicePlaylist, referencePlaylist);  // changes that were made on the DEVICE. Apply to DESKTOP.

                    reconciledPlaylist = new List<string>(desktopPlaylist);

                    var currentChangeSet = new ChangeSet
                    {
                        AddChanges = (from change in changesOnDevice
                                      where change.Type == SyncType.Add
                                      select change.DeviceLocation).ToList(),
                        RemoveChanges = ((from change in changesOnDevice
                                          where change.Type == SyncType.Remove
                                          select library.GetTrack(change.DeviceLocation) as Track).TakeWhile(t => t != null)).ToList()
                    };

                    if(!currentChangeSet.IsEmpty)
                        pendingChanges.Add(currentChangeSet);

                    if (ComiTunesLibrary.IsItunesRunning())
                    {
                        ComiTunesLibrary comLibrary = new ComiTunesLibrary();

                        foreach (var pendingChange in pendingChanges)
                        {
                            using (log4net.ThreadContext.Stacks["NDC"].Push("Removing iTrack"))
                            {
                                foreach (var track in comLibrary.RemoveTracks(playlist, pendingChange.RemoveChanges))
                                {
                                    reconciledPlaylist.Remove(track.GetPlaylistLine(request.DeviceMediaRoot));
                                    Log.Info(track.Location);
                                }
                            }

                            using (log4net.ThreadContext.Stacks["NDC"].Push("Adding iTrack"))
                            {
                                foreach (var deviceLocation in comLibrary.AddTracks(
                                    playlist,
                                    pendingChange.AddChanges,
                                    subscription ??
                                    Subscription.GetQuickSubscription(playlistName, request.DeviceMediaRoot,
                                                                      request.DeviceId),
                                    request.DeviceMediaRoot))
                                {
                                    reconciledPlaylist.Add(deviceLocation);
                                    Log.Info(deviceLocation);
                                }
                            }
                        }

                        pendingChanges.Clear();
                    }else
                    {

                        if(!currentChangeSet.IsEmpty)
                            ApplyChangeSet(request.DeviceMediaRoot, currentChangeSet, reconciledPlaylist);
                    }

                    ChangeSetCollection.Serialize(pendingChanges, pendingChangesPath);

                }
                else
                {
            #endif

                    reconciledPlaylist = desktopPlaylist;

            #if ENABLE_SYNC_TO_ITUNES
                }
            #endif

                    foreach (var change in changesOnDesktop)
                {
                    if (change.Type != SyncType.Add) continue;

                    // Remember songs, we we can serve them when client requests
                    string id = Helper.GetSha1Hash(change.DeviceLocation);
                    change.TrackPath = "/songs/" + id;
                    SongDb.Add(id, library.GetTrack(change.DeviceLocation).Location);
                }

                string playlistKey = Helper.GetSha1Hash(playlistPath);
                Helper.SavePlaylist(reconciledPlaylist, playlistPath);
                PlaylistDb.Add(playlistKey, playlistPath);

                SyncResponse syncResponse = new SyncResponse
                                                {
                                                    PlaylistServerPath = "/playlists/" + playlistKey,
                                                    PlaylistDevicePath = request.PlaylistDevicePath,
                                                    Actions = changesOnDesktop.ToArray()
                                                };
                syncResponse.Actions.UnEscapeAllDeviceLocations();
                Log.Debug("Sending Data:" + Environment.NewLine + syncResponse.ToString());

                ResetStatusLater();
                return syncResponse;
            }
        }