commit e40672906d65ea557f71900132abd6e202b9d64a
Author: pkanavos <pkanavos@gmail.com>
Date:   Tue Jun 5 23:42:53 2012 +0300

    Added server move detection when hashes match or downloading

diff --git a/trunk/Pithos.Core/Agents/PollAgent.cs b/trunk/Pithos.Core/Agents/PollAgent.cs
index ec33aac..7de54d1 100644
--- a/trunk/Pithos.Core/Agents/PollAgent.cs
+++ b/trunk/Pithos.Core/Agents/PollAgent.cs
@@ -503,6 +503,7 @@ namespace Pithos.Core.Agents
             //Don't use the tuple info, it may have been deleted
             var localInfo = FileInfoExtensions.FromPath(localFilePath);
 
+
             // Local file unchanged? If both C and L are null, make sure it's because 
             //both the file is missing and the state checksum is not missing
             if (tuple.C == tuple.L && (localInfo.Exists || tuple.FileState==null))
@@ -512,7 +513,8 @@ namespace Pithos.Core.Agents
                 if (tuple.S == tuple.L)
                 {
                     // No server changes
-                    ;
+                    //Has the file been renamed on the server?
+                    MoveForServerMove(accountInfo, tuple);
                 }
                 else
                 {
@@ -534,17 +536,24 @@ namespace Pithos.Core.Agents
                         {
                             //Server file exists
                             //downloadServerObject() // Result: L = S
-                            StatusKeeper.SetFileState(localFilePath, FileStatus.Modified,
+                            //If the file has moved on the server, move it locally before downloading
+                            var targetPath=MoveForServerMove(accountInfo,tuple);
+
+                            StatusKeeper.SetFileState(targetPath, FileStatus.Modified,
                                                       FileOverlayStatus.Modified, "");
                             NetworkAgent.Downloader.DownloadCloudFile(accountInfo,
                                                                             tuple.ObjectInfo,
-                                                                            localFilePath, token).Wait(token);
+                                                                            targetPath, token).Wait(token);
                             //updateRecord( L = S )
-                            StatusKeeper.UpdateFileChecksum(localFilePath, tuple.ObjectInfo.ETag,
+                            StatusKeeper.UpdateFileChecksum(targetPath, tuple.ObjectInfo.ETag,
                                                             tuple.ObjectInfo.X_Object_Hash);
 
-                            StatusKeeper.SetFileState(localFilePath, FileStatus.Unchanged,
+                            StatusKeeper.StoreInfo(targetPath, tuple.ObjectInfo);
+
+/*
+                            StatusKeeper.SetFileState(targetPath, FileStatus.Unchanged,
                                                       FileOverlayStatus.Normal, "");
+*/
                         }
                     }
                 }
@@ -576,10 +585,9 @@ namespace Pithos.Core.Agents
                                                                "Poll", isUnselected);
                             NetworkAgent.Uploader.UploadCloudFile(action, token).Wait(token);
 
-
                             //updateRecord( S = C )
-                            StatusKeeper.SetFileState(localFilePath, FileStatus.Unchanged,
-                                                      FileOverlayStatus.Normal, "");
+                            //State updated by the uploader
+                            
                             if (isUnselected)
                             {
                                 ProcessChildren(accountInfo, tuple, agent, token);
@@ -595,10 +603,9 @@ namespace Pithos.Core.Agents
                         {
                             // (Identical Changes) Result: L = S
                             //doNothing()
-                            StatusKeeper.UpdateFileChecksum(localFilePath, tuple.ObjectInfo.ETag,
-                                                            tuple.ObjectInfo.X_Object_Hash);
-                            StatusKeeper.SetFileState(localFilePath, FileStatus.Unchanged,
-                                                      FileOverlayStatus.Normal, "");
+                            //Detect server moves
+                            var targetPath=MoveForServerMove(accountInfo, tuple);
+                            StatusKeeper.StoreInfo(targetPath,tuple.ObjectInfo);
                         }
                         else
                         {
@@ -619,6 +626,30 @@ namespace Pithos.Core.Agents
             }
         }
 
+        private string MoveForServerMove(AccountInfo accountInfo, StateTuple tuple)
+        {
+            var relativePath = tuple.ObjectInfo.RelativeUrlToFilePath(accountInfo.UserName);
+            var serverPath = Path.Combine(accountInfo.AccountPath, relativePath);
+
+            if (tuple.FilePath == serverPath) return serverPath;
+
+            if (tuple.FileInfo.Exists)
+            {                    
+                var fi = tuple.FileInfo as FileInfo;
+                if (fi != null)
+                    fi.MoveTo(serverPath);
+                var di = tuple.FileInfo as DirectoryInfo;
+                if (di != null)
+                    di.MoveTo(serverPath);
+                StatusKeeper.StoreInfo(serverPath, tuple.ObjectInfo);
+            }
+            else
+            {
+                Debug.Assert(false, "File does not exist");
+            }
+            return serverPath;
+        }
+
         private void DeleteCloudFile(AccountInfo accountInfo, StateTuple tuple)
         {
             StatusKeeper.SetFileState(tuple.FilePath, FileStatus.Deleted,
@@ -647,43 +678,56 @@ namespace Pithos.Core.Agents
             IEnumerable<Tuple<FileSystemInfo, string>> files, 
             IEnumerable<FileState> states)
         {
-            var dct = new Dictionary<string, StateTuple>();
+            var tuplesByPath = new Dictionary<string, StateTuple>();
             foreach (var file in files)
             {
                 var fsInfo = file.Item1;
                 var fileHash = fsInfo is DirectoryInfo? MERKLE_EMPTY:file.Item2;
 
-                dct[fsInfo.FullName] = new StateTuple {FileInfo = fsInfo, MD5 = fileHash};
+                tuplesByPath[fsInfo.FullName] = new StateTuple {FileInfo = fsInfo, MD5 = fileHash};
             }
             foreach (var state in states)
             {
                 StateTuple hashTuple;
-                if (dct.TryGetValue(state.FilePath, out hashTuple))
+                if (tuplesByPath.TryGetValue(state.FilePath, out hashTuple))
                 {
                     hashTuple.FileState = state;
                 }
                 else
                 {
                     var fsInfo = FileInfoExtensions.FromPath(state.FilePath);
-                    dct[state.FilePath] = new StateTuple {FileInfo = fsInfo, FileState = state};
+                    tuplesByPath[state.FilePath] = new StateTuple {FileInfo = fsInfo, FileState = state};
                 }
             }
+
+            var tuplesByID = tuplesByPath.Values
+                .Where(tuple => tuple.FileState != null && tuple.FileState.ObjectID!=null)
+                .ToDictionary(tuple=>tuple.FileState.ObjectID,tuple=>tuple);//new Dictionary<Guid, StateTuple>();
+
             foreach (var info in infos)
             {
                 StateTuple hashTuple;
                 var filePath = info.Item1;
                 var objectInfo = info.Item2;
-                if (dct.TryGetValue(filePath, out hashTuple))
+                var objectID = objectInfo.UUID;
+
+                if (tuplesByID.TryGetValue(objectID, out hashTuple))
+                {
+                    hashTuple.ObjectInfo = objectInfo;                    
+                }
+                else if (tuplesByPath.TryGetValue(filePath, out hashTuple))
                 {
                     hashTuple.ObjectInfo = objectInfo;
                 }
                 else
                 {
                     var fsInfo = FileInfoExtensions.FromPath(filePath);
-                    dct[filePath] = new StateTuple {FileInfo = fsInfo, ObjectInfo = objectInfo};
+                    var tuple = new StateTuple {FileInfo = fsInfo, ObjectInfo = objectInfo};
+                    tuplesByPath[filePath] = tuple;
+                    tuplesByID[objectInfo.UUID] = tuple;
                 }
             }
-            return dct.Values;
+            return tuplesByPath.Values;
         }
 
         /// <summary>
