public void Start(string[] wantedCaches)
        {
            _thread = new Thread(delegate()
            {
            #if !DEBUG
                try
                {
            #endif
                    SendStatus(StatusChangedEnum.Start);
                    //SendStatus(StatusChangedEnum.CacheDownloading, "caches.xml", 0);
                    Log.Info(String.Format("Starting Update process. Wanted caches: {0}",
                                           String.Join(", ", wantedCaches)));
                    //Step 1. Get the remote file.
                    var file = _wc.DownloadString(_server + "caches.xml");
                    var xml = XDocument.Parse(file);
                    _remoteCache = new Caches(xml);
                    Log.Info("Remote caches have been downloaded.");
                    var versions = _remoteCache.Versions();
                    foreach (var a in versions)
                    {
                        Log.Info(String.Format("Cache: {0} Version: {1}", a.Key, a.Value));
                    }
                    SendStatus(StatusChangedEnum.CacheDownloaded, new object[] {"caches.xml", versions}, 0);
                    //populate this with all possible caches
                    var cachesToUpdate = new List<string>(wantedCaches);

                    //step 2. Get the local file, and compare
                    if (_localCacheLocation != "" && File.Exists(_localCacheLocation))
                    {
                        file = File.ReadAllText(_localCacheLocation);
                        xml = XDocument.Parse(file);
                        _localCache = new Caches(xml);
                        Log.Info("Local caches:");
                        foreach (var a in _localCache.Versions())
                        {
                            Log.Info(String.Format("Cache: {0} Version: {1}", a.Key, a.Value));
                        }
                        //step 2a. Compare version numbers.
                        var localVersions = _localCache.Versions();
                        foreach (var wantedCache in wantedCaches)
                        {
                            if (versions.ContainsKey(wantedCache))
                            {
                                if (localVersions.ContainsKey(wantedCache) &&
                                    versions[wantedCache] <= localVersions[wantedCache])
                                {
                                    //update is not needed for this cache.
                                    cachesToUpdate.Remove(wantedCache);
                                }
                            }
                            else
                            {
                                cachesToUpdate.Remove(wantedCache);
                            }
                        }
                    }
                    else
                    {
                        Log.Info("Local caches are not found or unused. Downloading all wanted caches.");
                        SendStatus(StatusChangedEnum.LocalCachesNotFound);
                    }
                    //cachesToUpdate now contains a list of all caches to get.

                    if (cachesToUpdate.Count == 0)
                    {
                        //success - no update needed
                        tasksDone = 0;
                        tasksTotal = 1;
                        Log.Info("no update needed");
                        SendStatus(StatusChangedEnum.NoUpdateNeeded);
                        SendStatus(StatusChangedEnum.Finish);
                        return;
                    }
                    tasksDone = 0;
                    tasksTotal = cachesToUpdate.Count;
                    Log.Info(String.Format("Caches to download: {0}", String.Join(", ", cachesToUpdate.ToArray())));
                    //step 3. Get the CacheInfo for each wanted cache.
                    var cacheContentFileList = new List<CacheContentFile>();
                    foreach (var cache in cachesToUpdate)
                    {
                        SendStatus(StatusChangedEnum.CacheDownloading, cache, 1);
                        file = _wc.DownloadString(_server + cache + "/info.xml");
                        xml = XDocument.Parse(file);
                        var ci = new CacheInfo(this, Location, cache, xml);
                        var files = ci.Files();
                        cacheContentFileList.AddRange(files);
                        SendStatus(StatusChangedEnum.CacheDownloaded, new object[] {cache, files}, 0);
                    }
                    tasksDone = 0;
                    tasksTotal = cacheContentFileList.Sum(cifile => cifile.CompressedSize);
                    if (tasksTotal != 0)
                    {
                        SendStatus(StatusChangedEnum.DownloadsStart);
                        foreach (var cifile in cacheContentFileList)
                        {
                            //step 4. Download and decompress.
                            Log.Info(String.Format("Downloading {0}", cifile.Cache + "/" + cifile.Name));

                            int retries = 0;
                            int maxRetries = 3;
                            int rampUpTime = 2;
                            bool success = false;

                            while (!success && retries <= maxRetries)
                            {
                                //totalProgress = cifile.CompressedSize;
                                SendStatus(StatusChangedEnum.FileStart, cifile.Cache + "/" + cifile.Name, 0);

                                success = Download(cifile);

                                if (!success)
                                {
                                    SendStatus(StatusChangedEnum.FileFailed, cifile.Cache + "/" + cifile.Name,
                                               -cifile.CompressedSize);
                                    //backtrack the progress

                                    retries++;
                                    if (retries <= maxRetries)
                                    {
                                        int waitTime = (int) (Math.Pow(rampUpTime, retries))*1000;
                                        SendStatus(StatusChangedEnum.FileRedownload,
                                                   new object[] {cifile.Cache + "/" + cifile.Name, waitTime}, 0);
                                        Log.Warn(string.Format("Redownloading {0} (sleeping {1} ms)", cifile.Name,
                                                               waitTime));
                                        Thread.Sleep(waitTime);
                                    }
                                }
                            }

                            if (!success)
                            {
                                SendStatus(StatusChangedEnum.Fail, _lastException, 0);
                                return;
                            }
                            SendStatus(StatusChangedEnum.FileFinish, cifile.Cache + "/" + cifile.Name, 0);
                        }
                        SendStatus(StatusChangedEnum.DownloadsFinish);
                    }
                    //step 5. Save remote cache as local cache.
                    if(_localCacheLocation != "")
                    {
                        file = _remoteCache.ToString();
                        var path = Path.Combine(Location, _localCacheLocation);
                        File.WriteAllText(path, file);
                        SendStatus(StatusChangedEnum.LocalCacheSaved);
                    }
                    SendStatus(StatusChangedEnum.Finish);
            #if !DEBUG
                }
                catch (Exception e){
                    SendStatus(StatusChangedEnum.Fail, e, 0);
                }
            #endif

            });
            _thread.Start();
        }