/// <summary>
        /// Save an OAR, register for the callback for when it's done, then call the AutoBackupScript (if applicable).
        /// </summary>
        /// <param name="scene"></param>
        private void DoRegionBackup(IScene scene)
        {
            if (scene.RegionStatus != RegionStatus.Up)
            {
                // We won't backup a region that isn't operating normally.
                m_log.Warn("[AUTO BACKUP]: Not backing up region " + scene.RegionInfo.RegionName +
                           " because its status is " + scene.RegionStatus);
                return;
            }

            AutoBackupModuleState state = this.m_states[scene];
            IRegionArchiverModule iram  = scene.RequestModuleInterface <IRegionArchiverModule>();
            string savePath             = BuildOarPath(scene.RegionInfo.RegionName,
                                                       state.BackupDir,
                                                       state.NamingType);

            if (savePath == null)
            {
                m_log.Warn("[AUTO BACKUP]: savePath is null in HandleElapsed");
                return;
            }
            Guid guid = Guid.NewGuid();

            m_pendingSaves.Add(guid, scene);
            state.LiveRequests.Add(guid, savePath);
            ((Scene)scene).EventManager.OnOarFileSaved += new EventManager.OarFileSaved(EventManager_OnOarFileSaved);
            iram.ArchiveRegion(savePath, guid, null);
        }
        /// <summary>
        /// Here we just clean up some resources and stop the OAR backup (if any) for the given scene.
        /// </summary>
        /// <param name="scene">The scene (region) to stop performing AutoBackup on.</param>
        void IRegionModuleBase.RemoveRegion(Scene scene)
        {
            if (!this.m_enabled)
            {
                return;
            }

            if (this.m_states.ContainsKey(scene))
            {
                AutoBackupModuleState abms = this.m_states[scene];

                // Remove this scene out of the timer map list
                Timer         timer = abms.Timer;
                List <IScene> list  = this.m_timerMap[timer];
                list.Remove(scene);

                // Shut down the timer if this was the last scene for the timer
                if (list.Count == 0)
                {
                    this.m_timerMap.Remove(timer);
                    this.m_timers.Remove(timer.Interval);
                    timer.Close();
                }
                this.m_states.Remove(scene);
            }
        }
        /// <summary>
        /// Called when any auto-backup timer expires. This starts the code path for actually performing a backup.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void HandleElapsed(object sender, ElapsedEventArgs e)
        {
            // TODO: heuristic thresholds are per-region, so we should probably run heuristics once per region
            // XXX: Running heuristics once per region could add undue performance penalty for something that's supposed to
            // check whether the region is too busy! Especially on sims with LOTS of regions.
            // Alternative: make heuristics thresholds global to the module rather than per-region. Less flexible,
            //  but would allow us to be semantically correct while being easier on perf.
            // Alternative 2: Run heuristics once per unique set of heuristics threshold parameters! Ay yi yi...
            // Alternative 3: Don't support per-region heuristics at all; just accept them as a global only parameter.
            // Since this is pretty experimental, I haven't decided which alternative makes the most sense.
            if (this.m_closed)
            {
                return;
            }
            bool heuristicsRun    = false;
            bool heuristicsPassed = false;

            if (!this.m_timerMap.ContainsKey((Timer)sender))
            {
                m_log.Debug("Code-up error: timerMap doesn't contain timer " + sender);
            }

            List <IScene> tmap = this.m_timerMap[(Timer)sender];

            if (tmap != null && tmap.Count > 0)
            {
                foreach (IScene scene in tmap)
                {
                    AutoBackupModuleState state = this.m_states[scene];
                    bool heuristics             = state.BusyCheck;

                    // Fast path: heuristics are on; already ran em; and sim is fine; OR, no heuristics for the region.
                    if ((heuristics && heuristicsRun && heuristicsPassed) || !heuristics)
                    {
                        this.DoRegionBackup(scene);
                        // Heuristics are on; ran but we're too busy -- keep going. Maybe another region will have heuristics off!
                    }
                    else if (heuristicsRun)
                    {
                        m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
                                   scene.RegionInfo.RegionName + " right now.");
                        continue;
                        // Logical Deduction: heuristics are on but haven't been run
                    }
                    else
                    {
                        heuristicsPassed = this.RunHeuristics(scene);
                        heuristicsRun    = true;
                        if (!heuristicsPassed)
                        {
                            m_log.Info("[AUTO BACKUP]: Heuristics: too busy to backup " +
                                       scene.RegionInfo.RegionName + " right now.");
                            continue;
                        }
                        this.DoRegionBackup(scene);
                    }
                }
            }
        }
 /// <summary>
 /// Called by the Event Manager when the OnOarFileSaved event is fired.
 /// </summary>
 /// <param name="guid"></param>
 /// <param name="message"></param>
 private void EventManager_OnOarFileSaved(Guid guid, string message)
 {
     // Ignore if the OAR save is being done by some other part of the system
     if (m_pendingSaves.ContainsKey(guid))
     {
         AutoBackupModuleState abms = m_states[(m_pendingSaves[guid])];
         ExecuteScript(abms.Script, abms.LiveRequests[guid]);
         m_pendingSaves.Remove(guid);
         abms.LiveRequests.Remove(guid);
     }
 }
        /// <summary>
        /// Most interesting/complex code paths in AutoBackup begin here.
        /// We read lots of Nini config, maybe set a timer, add members to state tracking Dictionaries, etc.
        /// </summary>
        /// <param name="scene">The scene to (possibly) perform AutoBackup on.</param>
        void IRegionModuleBase.RegionLoaded(Scene scene)
        {
            if (!this.m_enabled)
            {
                return;
            }

            // This really ought not to happen, but just in case, let's pretend it didn't...
            if (scene == null)
            {
                return;
            }

            AutoBackupModuleState abms = this.ParseConfig(scene, false);

            m_log.Debug("[AUTO BACKUP]: Config for " + scene.RegionInfo.RegionName);
            m_log.Debug((abms == null ? "DEFAULT" : abms.ToString()));
        }
        /// <summary>
        /// Called once in the lifetime of the module at startup.
        /// </summary>
        /// <param name="source">The input config source for OpenSim.ini.</param>
        void IRegionModuleBase.Initialise(IConfigSource source)
        {
            // Determine if we have been enabled at all in OpenSim.ini -- this is part and parcel of being an optional module
            this.m_configSource = source;
            IConfig moduleConfig = source.Configs["AutoBackupModule"];

            if (moduleConfig == null)
            {
                this.m_enabled = false;
                return;
            }
            else
            {
                this.m_enabled = moduleConfig.GetBoolean("AutoBackupModuleEnabled", false);
                if (this.m_enabled)
                {
                    m_log.Info("[AUTO BACKUP]: AutoBackupModule enabled");
                }
                else
                {
                    return;
                }
            }

            Timer defTimer = new Timer(43200000);

            this.m_defaultState.Timer = defTimer;
            this.m_timers.Add(43200000, defTimer);
            defTimer.Elapsed  += this.HandleElapsed;
            defTimer.AutoReset = true;
            defTimer.Start();

            AutoBackupModuleState abms = this.ParseConfig(null, true);

            m_log.Debug("[AUTO BACKUP]: Here is the default config:");
            m_log.Debug(abms.ToString());
        }
        /// <summary>
        /// Set up internal state for a given scene. Fairly complex code.
        /// When this method returns, we've started auto-backup timers, put members in Dictionaries, and created a State object for this scene.
        /// </summary>
        /// <param name="scene">The scene to look at.</param>
        /// <param name="parseDefault">Whether this call is intended to figure out what we consider the "default" config (applied to all regions unless overridden by per-region settings).</param>
        /// <returns>An AutoBackupModuleState contains most information you should need to know relevant to auto-backup, as applicable to a single region.</returns>
        private AutoBackupModuleState ParseConfig(IScene scene, bool parseDefault)
        {
            string sRegionName;
            string sRegionLabel;
            //            string prepend;
            AutoBackupModuleState state;

            if (parseDefault)
            {
                sRegionName  = null;
                sRegionLabel = "DEFAULT";
                //                prepend = "";
                state = this.m_defaultState;
            }
            else
            {
                sRegionName  = scene.RegionInfo.RegionName;
                sRegionLabel = sRegionName;
                //                prepend = sRegionName + ".";
                state = null;
            }

            // Read the config settings and set variables.
            IConfig regionConfig = (scene != null ? scene.Config.Configs[sRegionName] : null);
            IConfig config       = this.m_configSource.Configs["AutoBackupModule"];

            if (config == null)
            {
                // defaultState would be disabled too if the section doesn't exist.
                state = this.m_defaultState;
                return(state);
            }

            bool tmpEnabled = ResolveBoolean("AutoBackup", this.m_defaultState.Enabled, config, regionConfig);

            if (state == null && tmpEnabled != this.m_defaultState.Enabled)
            //Varies from default state
            {
                state = new AutoBackupModuleState();
            }

            if (state != null)
            {
                state.Enabled = tmpEnabled;
            }

            // If you don't want AutoBackup, we stop.
            if ((state == null && !this.m_defaultState.Enabled) || (state != null && !state.Enabled))
            {
                return(state);
            }
            else
            {
                m_log.Info("[AUTO BACKUP]: Region " + sRegionLabel + " is AutoBackup ENABLED.");
            }

            // Borrow an existing timer if one exists for the same interval; otherwise, make a new one.
            double interval =
                this.ResolveDouble("AutoBackupInterval", this.m_defaultState.IntervalMinutes,
                                   config, regionConfig) * 60000.0;

            if (state == null && interval != this.m_defaultState.IntervalMinutes * 60000.0)
            {
                state = new AutoBackupModuleState();
            }

            if (this.m_timers.ContainsKey(interval))
            {
                if (state != null)
                {
                    state.Timer = this.m_timers[interval];
                }
                m_log.Debug("[AUTO BACKUP]: Reusing timer for " + interval + " msec for region " +
                            sRegionLabel);
            }
            else
            {
                // 0 or negative interval == do nothing.
                if (interval <= 0.0 && state != null)
                {
                    state.Enabled = false;
                    return(state);
                }
                if (state == null)
                {
                    state = new AutoBackupModuleState();
                }
                Timer tim = new Timer(interval);
                state.Timer = tim;
                //Milliseconds -> minutes
                this.m_timers.Add(interval, tim);
                tim.Elapsed  += this.HandleElapsed;
                tim.AutoReset = true;
                tim.Start();
            }

            // Add the current region to the list of regions tied to this timer.
            if (scene != null)
            {
                if (state != null)
                {
                    if (this.m_timerMap.ContainsKey(state.Timer))
                    {
                        this.m_timerMap[state.Timer].Add(scene);
                    }
                    else
                    {
                        List <IScene> scns = new List <IScene>(1);
                        scns.Add(scene);
                        this.m_timerMap.Add(state.Timer, scns);
                    }
                }
                else
                {
                    if (this.m_timerMap.ContainsKey(this.m_defaultState.Timer))
                    {
                        this.m_timerMap[this.m_defaultState.Timer].Add(scene);
                    }
                    else
                    {
                        List <IScene> scns = new List <IScene>(1);
                        scns.Add(scene);
                        this.m_timerMap.Add(this.m_defaultState.Timer, scns);
                    }
                }
            }

            bool tmpBusyCheck = ResolveBoolean("AutoBackupBusyCheck",
                                               this.m_defaultState.BusyCheck, config, regionConfig);

            if (state == null && tmpBusyCheck != this.m_defaultState.BusyCheck)
            {
                state = new AutoBackupModuleState();
            }

            if (state != null)
            {
                state.BusyCheck = tmpBusyCheck;
            }

            // Set file naming algorithm
            string stmpNamingType = ResolveString("AutoBackupNaming",
                                                  this.m_defaultState.NamingType.ToString(), config, regionConfig);
            NamingType tmpNamingType;

            if (stmpNamingType.Equals("Time", StringComparison.CurrentCultureIgnoreCase))
            {
                tmpNamingType = NamingType.Time;
            }
            else if (stmpNamingType.Equals("Sequential", StringComparison.CurrentCultureIgnoreCase))
            {
                tmpNamingType = NamingType.Sequential;
            }
            else if (stmpNamingType.Equals("Overwrite", StringComparison.CurrentCultureIgnoreCase))
            {
                tmpNamingType = NamingType.Overwrite;
            }
            else
            {
                m_log.Warn("Unknown naming type specified for region " + sRegionLabel + ": " +
                           stmpNamingType);
                tmpNamingType = NamingType.Time;
            }

            if (state == null && tmpNamingType != this.m_defaultState.NamingType)
            {
                state = new AutoBackupModuleState();
            }

            if (state != null)
            {
                state.NamingType = tmpNamingType;
            }

            string tmpScript = ResolveString("AutoBackupScript",
                                             this.m_defaultState.Script, config, regionConfig);

            if (state == null && tmpScript != this.m_defaultState.Script)
            {
                state = new AutoBackupModuleState();
            }

            if (state != null)
            {
                state.Script = tmpScript;
            }

            string tmpBackupDir = ResolveString("AutoBackupDir", ".", config, regionConfig);

            if (state == null && tmpBackupDir != this.m_defaultState.BackupDir)
            {
                state = new AutoBackupModuleState();
            }

            if (state != null)
            {
                state.BackupDir = tmpBackupDir;
                // Let's give the user some convenience and auto-mkdir
                if (state.BackupDir != ".")
                {
                    try
                    {
                        DirectoryInfo dirinfo = new DirectoryInfo(state.BackupDir);
                        if (!dirinfo.Exists)
                        {
                            dirinfo.Create();
                        }
                    }
                    catch (Exception e)
                    {
                        m_log.Warn(
                            "BAD NEWS. You won't be able to save backups to directory " +
                            state.BackupDir +
                            " because it doesn't exist or there's a permissions issue with it. Here's the exception.",
                            e);
                    }
                }
            }

            return(state);
        }