protected override void OnDoWork(DoWorkEventArgs e)
        {
            // get all parameters
            StartOptions = (ElevationDatabaseUpdaterStartOptions)e.Argument;
            this.ReportProgress(0, StartOptions.Name + " started.");
            // name the thread for debugging
            if (String.IsNullOrEmpty(Thread.CurrentThread.Name))
            {
                Thread.CurrentThread.Name = StartOptions.Name + "_" + this.GetType().Name;
            }
            // get update interval
            int interval = (int)Properties.Settings.Default.Datatbase_BackgroundUpdate_Period * 60;

            do
            {
                try
                {
                    int errors = 0;
                    // check if any kind of update is enabled
                    if ((StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNONCE) || (StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY))
                    {
                        // reset status
                        ElevationData.Database.SetDBStatus(StartOptions.Model, DATABASESTATUS.UNDEFINED);
                        this.ReportProgress(1, ElevationData.Database.GetDBStatus(StartOptions.Model));
                        // clear temporary files
                        try
                        {
                            SupportFunctions.DeleteFilesFromDirectory(TmpDirectory, new string[] { "*.tmp", "*.PendingOverwrite" });
                            SupportFunctions.DeleteFilesFromDirectory(ElevationData.Database.DefaultDatabaseDirectory(StartOptions.Model), new string[] { "*.tmp", "*.PendingOverwrite" });
                        }
                        catch (Exception ex)
                        {
                            this.ReportProgress(-1, ex.ToString());
                        }
                        Stopwatch st = new Stopwatch();
                        st.Start();
                        // check last change of database file
                        if (HasDatabaseChanged() || HasUpdateChanged() || HaveBoundsChanged())
                        {
                            // database and/or update has changed --> full check necessary
                            // check if database is complete
                            this.ReportProgress(0, "Checking " + StartOptions.Name + "...");
                            bool b = IsDatabaseComplete();
                            st.Stop();
                            if (b)
                            {
                                // set status
                                ElevationData.Database.SetDBStatusBit(StartOptions.Model, DATABASESTATUS.COMPLETE);
                                // set bounds
                                ElevationData.Database.MinLat = StartOptions.MinLat;
                                ElevationData.Database.MinLon = StartOptions.MinLon;
                                ElevationData.Database.MaxLat = StartOptions.MaxLat;
                                ElevationData.Database.MaxLon = StartOptions.MaxLon;

                                this.ReportProgress(0, StartOptions.Name + " complete: " + st.ElapsedMilliseconds / 1000 + "sec.");
                            }
                            else
                            {
                                ElevationData.Database.ResetDBStatusBit(StartOptions.Model, DATABASESTATUS.COMPLETE);
                            }
                            this.ReportProgress(1, ElevationData.Database.GetDBStatus(StartOptions.Model));
                            if (this.CancellationPending)
                            {
                                break;
                            }
                            st.Restart();
                            // update elevation database if necessary
                            this.ReportProgress(0, "Updating " + StartOptions.Name + "...");
                            // reset database status
                            ElevationData.Database.ResetDBStatusBit(StartOptions.Model, DATABASESTATUS.UPTODATE);
                            ElevationData.Database.ResetDBStatusBit(StartOptions.Model, DATABASESTATUS.ERROR);
                            ElevationData.Database.SetDBStatusBit(StartOptions.Model, DATABASESTATUS.UPDATING);
                            this.ReportProgress(1, ElevationData.Database.GetDBStatus(StartOptions.Model));
                            // start update
                            int er = -UpdateDatabase();
                            // set database status
                            ElevationData.Database.ResetDBStatusBit(StartOptions.Model, DATABASESTATUS.UPDATING);
                            if (er > 0)
                            {
                                ElevationData.Database.SetDBStatusBit(StartOptions.Model, DATABASESTATUS.ERROR);
                            }
                            else
                            {
                                ElevationData.Database.ResetDBStatusBit(StartOptions.Model, DATABASESTATUS.ERROR);
                                ElevationData.Database.SetDBStatusBit(StartOptions.Model, DATABASESTATUS.UPTODATE);
                            }
                            this.ReportProgress(1, ElevationData.Database.GetDBStatus(StartOptions.Model));
                            st.Stop();
                            if (this.CancellationPending)
                            {
                                break;
                            }

                            // display status
                            if (errors == 0)
                            {
                                this.ReportProgress(0, StartOptions.Name + " update completed: " + st.Elapsed.ToString(@"hh\:mm\:ss"));
                            }
                            else
                            {
                                this.ReportProgress(0, StartOptions.Name + " update completed with errors[" + errors.ToString() + "]: " + st.Elapsed.ToString(@"hh\:mm\:ss"));
                            }
                            // store database timestamp & status in settings
                            SaveDatabaseTimeStamp();
                            SaveDatabaseStatus();
                            SaveUpdateTimeStamp();
                            SaveBounds();
                            // sleep once to get all messages to main thread
                            Thread.Sleep(1000);
                        }
                        else
                        {
                            // database and update not changed -> nothing to do
                            // restore database status
                            ElevationData.Database.SetDBStatus(StartOptions.Model, GetSavedDatabaseStatus());
                            this.ReportProgress(1, ElevationData.Database.GetDBStatus(StartOptions.Model));
                            this.ReportProgress(0, StartOptions.Name + " update completed: " + st.Elapsed.ToString(@"hh\:mm\:ss"));
                        }

                        // sleep when running periodically
                        if (StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY)
                        {
                            int i = 0;
                            while (!this.CancellationPending && (i < interval))
                            {
                                Thread.Sleep(1000);
                                i++;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    this.ReportProgress(-1, ex.ToString());
                }
                if (this.CancellationPending)
                {
                    break;
                }
                // sleep when running periodically
                if (StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY)
                {
                    int i = 0;
                    while (!this.CancellationPending && (i < interval))
                    {
                        Thread.Sleep(1000);
                        i++;
                    }
                }
            }while (StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY);
            if (this.CancellationPending)
            {
                this.ReportProgress(0, StartOptions.Name + " cancelled.");
            }
            else
            {
                this.ReportProgress(0, StartOptions.Name + " finished.");
            }
        }
        protected override void OnDoWork(DoWorkEventArgs e)
        {
            StartOptions = (StationDatabaseUpdaterStartOptions)e.Argument;
            this.ReportProgress(0, StartOptions.Name + " started.");
            // name the thread for debugging
            if (String.IsNullOrEmpty(Thread.CurrentThread.Name))
            {
                Thread.CurrentThread.Name = nameof(StationDatabaseUpdater);
            }
            this.ReportProgress(0, "Updating station database...");
            // get current AirScout password phrase for Unzip from website
            try
            {
                WebClient client = new WebClient();
                string    result = client.DownloadString(StartOptions.GetKeyURL +
                                                         "?id=" + StartOptions.InstanceID +
                                                         "&key=zip");
                if (!result.StartsWith("Error:"))
                {
                    result   = result.Trim('\"');
                    Password = Encryption.OpenSSLDecrypt(result, StartOptions.SessionKey);
                }
            }
            catch (Exception ex)
            {
                this.ReportProgress(-1, ex.ToString());
            }
            // get update interval
            int interval = (int)Properties.Settings.Default.Database_BackgroundUpdate_Period * 60;

            do
            {
                try
                {
                    int errors = 0;
                    // check if any kind of update is enabled
                    if ((StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNONCE) || (StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY))
                    {
                        // reset database status
                        StationData.Database.SetDBStatus(DATABASESTATUS.UNDEFINED);
                        this.ReportProgress(1, StationData.Database.GetDBStatus());
                        Stopwatch st = new Stopwatch();
                        st.Start();
                        // clear temporary files
                        try
                        {
                            SupportFunctions.DeleteFilesFromDirectory(TmpDirectory, new string[] { "*.tmp", "*.PendingOverwrite" });
                        }
                        catch (Exception ex)
                        {
                            this.ReportProgress(-1, ex.ToString());
                        }
                        // set status to updating
                        StationData.Database.SetDBStatus(DATABASESTATUS.UPDATING);
                        this.ReportProgress(1, StationData.Database.GetDBStatus());
                        // update location database
                        this.ReportProgress(0, "Updating locations from web database...");
                        // get update from url
                        GetUpdateFromURL(Properties.Settings.Default.Stations_UpdateURL + "locations.zip", Path.Combine(TmpDirectory, "locations.zip"), Path.Combine(TmpDirectory, "locations.json"));
                        if (HasDatabaseChanged() || HasLocationUpdateChanged())
                        {
                            // database and/or update changed --> do full check
                            if (!ReadLocationsFromURL(Properties.Settings.Default.Stations_UpdateURL + "locations.zip", Path.Combine(TmpDirectory, "locations.zip"), Path.Combine(TmpDirectory, "locations.json")))
                            {
                                errors++;
                            }
                            // save status & timestamps
                            SaveDatabaseTimeStamp();
                            SaveDatabaseStatus();
                            SaveLocationUpdateTimeStamp();
                        }
                        else
                        {
                            // dabase and update not changed --> nothing to do
                            // restore database status
                            StationData.Database.SetDBStatus(GetDatabaseStatus());
                        }
                        if (this.CancellationPending)
                        {
                            break;
                        }
                        // update qrv database
                        this.ReportProgress(0, "Updating qrv info from web database...");
                        // get update from url
                        GetUpdateFromURL(Properties.Settings.Default.Stations_UpdateURL + "qrv.zip", Path.Combine(TmpDirectory, "qrv.zip"), Path.Combine(TmpDirectory, "qrv.json"));
                        if (HasDatabaseChanged() || HasQRVUpdateChanged())
                        {
                            if (!ReadQRVFromURL(Properties.Settings.Default.Stations_UpdateURL + "qrv.zip", Path.Combine(TmpDirectory, "qrv.zip"), Path.Combine(TmpDirectory, "qrv.json")))
                            {
                                errors++;
                            }
                            // save status & timestamps
                            SaveDatabaseTimeStamp();
                            SaveDatabaseStatus();
                            SaveQRVUpdateTimeStamp();
                        }
                        else
                        {
                            // dabase and update not changed --> nothing to do
                            // restore database status
                            StationData.Database.SetDBStatus(GetDatabaseStatus());
                        }
                        // silently update locations from V1.2 database if found
                        string appdatadir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.CompanyName, Application.ProductName).TrimEnd(Path.DirectorySeparatorChar);
                        string oldfile    = Path.Combine(appdatadir.Replace("AirScout", "ScoutBase"), "Database", "ScoutBase.db3");
                        if (File.Exists(oldfile))
                        {
                            this.ReportProgress(0, "Import locations from V1.2 database...");
                            if (!ReadLocationsFromV1_2(oldfile))
                            {
                                errors++;
                            }
                        }
                        st.Stop();
                        // display status
                        if (errors == 0)
                        {
                            StationData.Database.SetDBStatus(DATABASESTATUS.UPTODATE);
                            this.ReportProgress(1, StationData.Database.GetDBStatus());
                            this.ReportProgress(0, " Station database update completed: " + st.Elapsed.ToString(@"hh\:mm\:ss"));
                        }
                        else
                        {
                            StationData.Database.SetDBStatus(DATABASESTATUS.ERROR);
                            this.ReportProgress(1, StationData.Database.GetDBStatus());
                            this.ReportProgress(0, " Station database update completed with errors[" + errors.ToString() + "]: " + st.Elapsed.ToString(@"hh\:mm\:ss"));
                        }
                        // sleep once to get all messages to main thread
                        Thread.Sleep(1000);
                        // sleep when running periodically
                        if (StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY)
                        {
                            int i = 0;
                            while (!this.CancellationPending && (i < interval))
                            {
                                Thread.Sleep(1000);
                                i++;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    this.ReportProgress(-1, ex.ToString());
                }
            }while (!this.CancellationPending && (StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY));
            if (this.CancellationPending)
            {
                this.ReportProgress(0, "Cancelled.");
            }
            else
            {
                this.ReportProgress(0, "Finished.");
            }
        }