/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the OpenSim Project nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* 
*/

using System;
using System.Collections.Generic;
using libsecondlife;
using OpenSim.Framework;
using OpenSim.Framework.Console;

namespace OpenSim.Region.Environment.Scenes
{
    public delegate void RestartSim(RegionInfo thisregion);

    public class SceneManager
    {
        public event RestartSim OnRestartSim;

        private readonly List<Scene> m_localScenes;
        private Scene m_currentScene = null;

        public Scene CurrentScene
        {
            get { return m_currentScene; }
        }

        public Scene CurrentOrFirstScene
        {
            get
            {
                if (m_currentScene == null)
                {
                    return m_localScenes[0];
                }
                else
                {
                    return m_currentScene;
                }
            }
        }

        public SceneManager()
        {
            m_localScenes = new List<Scene>();
        }

        public void Close()
        {
            for (int i = 0; i < m_localScenes.Count; i++)
            {
                m_localScenes[i].Close();
            }
        }

        public void Close(Scene cscene)
        {
            if (m_localScenes.Contains(cscene))
            {
                for (int i = 0; i < m_localScenes.Count; i++)
                {
                    if (m_localScenes[i].Equals(cscene))
                    {

                        m_localScenes[i].Close();
                    }
                }
            }
        }

        public void Add(Scene scene)
        {
            scene.OnRestart += HandleRestart;
            m_localScenes.Add(scene);
        }

        public void HandleRestart(RegionInfo rdata)
        {
            MainLog.Instance.Error("SCENEMANAGER", "Got Restart message for region:" + rdata.RegionName +" Sending up to main");
            int RegionSceneElement = -1;
            for (int i = 0; i < m_localScenes.Count; i++)
            {
                if (rdata.RegionName == m_localScenes[i].RegionInfo.RegionName)
                {
                    RegionSceneElement = i;
                }
            }

            // Now we make sure the region is no longer known about by the SceneManager
            // Prevents duplicates.

            if (RegionSceneElement >= 0)
            {
                m_localScenes.RemoveAt(RegionSceneElement);
            }

            // Send signal to main that we're restarting this sim.
            OnRestartSim(rdata);
        }

        public void SendSimOnlineNotification(ulong regionHandle)
        {
            RegionInfo Result = null;

            for (int i = 0; i < m_localScenes.Count; i++)
            {

                if (m_localScenes[i].RegionInfo.RegionHandle == regionHandle)
                {
                    // Inform other regions to tell their avatar about me
                    Result = m_localScenes[i].RegionInfo;
                }
            }
            if (!(Result.Equals(null)))
            {
                for (int i = 0; i < m_localScenes.Count; i++)
                {
                    if (m_localScenes[i].RegionInfo.RegionHandle != regionHandle)
                    {
                        // Inform other regions to tell their avatar about me
                        //m_localScenes[i].OtherRegionUp(Result);
                    }
                }
            }
            else
            {
                MainLog.Instance.Error("REGION", "Unable to notify Other regions of this Region coming up");
            }
        }

        public void SaveCurrentSceneToXml(string filename)
        {
            CurrentOrFirstScene.SavePrimsToXml(filename);
        }

        public void LoadCurrentSceneFromXml(string filename, bool generateNewIDs, LLVector3 loadOffset)
        {
            CurrentOrFirstScene.LoadPrimsFromXml(filename, generateNewIDs, loadOffset);
        }

        public void SaveCurrentSceneToXml2(string filename)
        {
            CurrentOrFirstScene.SavePrimsToXml2(filename);
        }

        public void LoadCurrentSceneFromXml2(string filename)
        {
            CurrentOrFirstScene.LoadPrimsFromXml2(filename);
        }

        public bool RunTerrainCmdOnCurrentScene(string[] cmdparams, ref string result)
        {
            if (m_currentScene == null)
            {
                bool success = true;
                foreach (Scene scene in m_localScenes)
                {
                    if (!scene.Terrain.RunTerrainCmd(cmdparams, ref result, scene.RegionInfo.RegionName))
                    {
                        success = false;
                    }
                }

                return success;
            }
            else
            {
                return m_currentScene.Terrain.RunTerrainCmd(cmdparams, ref result, m_currentScene.RegionInfo.RegionName);
            }
        }

        public void SendCommandToCurrentSceneScripts(string[] cmdparams)
        {
            ForEachCurrentScene(delegate(Scene scene) { scene.SendCommandToPlugins(cmdparams); });
        }

        public void SetBypassPermissionsOnCurrentScene(bool bypassPermissions)
        {
            ForEachCurrentScene(delegate(Scene scene) { scene.PermissionsMngr.BypassPermissions = bypassPermissions; });
        }

        private void ForEachCurrentScene(Action<Scene> func)
        {
            if (m_currentScene == null)
            {
                m_localScenes.ForEach(func);
            }
            else
            {
                func(m_currentScene);
            }
        }

        public void RestartCurrentScene()
        {
            ForEachCurrentScene(delegate(Scene scene) { scene.RestartNow(); });
        }

        public void BackupCurrentScene()
        {
            ForEachCurrentScene(delegate(Scene scene) { scene.Backup(); });
        }
        
        public void HandleAlertCommandOnCurrentScene(string[] cmdparams)
        {
            ForEachCurrentScene(delegate(Scene scene) { scene.HandleAlertCommand(cmdparams); });
        }

        public void SendGeneralMessage(string msg)
        {
            ForEachCurrentScene(delegate(Scene scene) { scene.SendGeneralAlert(msg); });
        }

        public bool TrySetCurrentScene(string regionName)
        {
            if ((String.Compare(regionName, "root") == 0) || (String.Compare(regionName, "..") == 0))
            {
                m_currentScene = null;
                return true;
            }
            else
            {
                Console.WriteLine("Searching for Region: '" + regionName + "'");

                foreach (Scene scene in m_localScenes)
                {
                    if (String.Compare(scene.RegionInfo.RegionName, regionName, true) == 0)
                    {
                        m_currentScene = scene;
                        return true;
                    }
                }

                return false;
            }
        }

        public bool TryGetScene(string regionName, out Scene scene)
        {
            foreach (Scene mscene in m_localScenes)
            {
                if (String.Compare(mscene.RegionInfo.RegionName, regionName, true) == 0)
                {
                    scene = mscene;
                    return true;
                }
            }
            scene = null;
            return false;
        }

        public bool TryGetScene(LLUUID regionID, out Scene scene)
        {
            foreach (Scene mscene in m_localScenes)
            {
                if (mscene.RegionInfo.RegionID == regionID)
                {
                    scene = mscene;
                    return true;
                }
            }
            scene = null;
            return false;
        }

        public void SetDebugPacketOnCurrentScene(LogBase log, int newDebug)
        {
            ForEachCurrentScene(delegate(Scene scene)
                                    {
                                        List<EntityBase> EntitieList = scene.GetEntities();

                                        foreach (EntityBase entity in EntitieList)
                                        {
                                            if (entity is ScenePresence)
                                            {
                                                ScenePresence scenePrescence = entity as ScenePresence;
                                                if (!scenePrescence.IsChildAgent)
                                                {
                                                    log.Error(String.Format("Packet debug for {0} {1} set to {2}",
                                                                            scenePrescence.Firstname,
                                                                            scenePrescence.Lastname,
                                                                            newDebug));

                                                    scenePrescence.ControllingClient.SetDebug(newDebug);
                                                }
                                            }
                                        }
                                    });
        }

        public List<ScenePresence> GetCurrentSceneAvatars()
        {
            List<ScenePresence> avatars = new List<ScenePresence>();

            ForEachCurrentScene(delegate(Scene scene)
                                    {
                                        List<EntityBase> EntitieList = scene.GetEntities();

                                        foreach (EntityBase entity in EntitieList)
                                        {
                                            if (entity is ScenePresence)
                                            {
                                                ScenePresence scenePrescence = entity as ScenePresence;
                                                if (!scenePrescence.IsChildAgent)
                                                {
                                                    avatars.Add(scenePrescence);
                                                }
                                            }
                                        }
                                    });

            return avatars;
        }

        public RegionInfo GetRegionInfo(ulong regionHandle)
        {
            foreach (Scene scene in m_localScenes)
            {
                if (scene.RegionInfo.RegionHandle == regionHandle)
                {
                    return scene.RegionInfo;
                }
            }

            return null;
        }

        public void SetCurrentSceneTimePhase(int timePhase)
        {
            ForEachCurrentScene(delegate(Scene scene)
                                    {
                                        scene.SetTimePhase(
                                            timePhase)
                                            ;
                                    });
        }

        public void ForceCurrentSceneClientUpdate()
        {
            ForEachCurrentScene(delegate(Scene scene) { scene.ForceClientUpdate(); });
        }

        public void HandleEditCommandOnCurrentScene(string[] cmdparams)
        {
            ForEachCurrentScene(delegate(Scene scene) { scene.HandleEditCommand(cmdparams); });
        }

        public bool TryGetAvatar(LLUUID avatarId, out ScenePresence avatar)
        {
            foreach (Scene scene in m_localScenes)
            {
                if (scene.TryGetAvatar(avatarId, out avatar))
                {
                    return true;
                }
            }

            avatar = null;
            return false;
        }

        public bool TryGetAvatarsScene(LLUUID avatarId, out Scene scene)
        {
            ScenePresence avatar = null;
            foreach (Scene mScene in m_localScenes)
            {
                if (mScene.TryGetAvatar(avatarId, out avatar))
                {
                    scene = mScene;
                    return true;
                }
            }

            scene = null;
            return false;
        }

        public void CloseScene(Scene scene)
        {
            m_localScenes.Remove(scene);
            scene.Close();
        }

        public bool TryGetAvatarByName(string avatarName, out ScenePresence avatar)
        {
            foreach (Scene scene in m_localScenes)
            {
                if (scene.TryGetAvatarByName(avatarName, out avatar))
                {
                    return true;
                }
            }

            avatar = null;
            return false;
        }

        public void ForEachScene(Action<Scene> action)
        {
            m_localScenes.ForEach(action);
        }
    }
}