/*
 * 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 System.IO;
using System.Net.Sockets;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using log4net;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Environment.Interfaces;
using OpenSim.Region.Environment.Scenes;

namespace OpenSim.Region.Environment.Modules.Avatar.Concierge
{
    public class ConciergeModule : IRegionModule
    {
        private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        private const int DEBUG_CHANNEL = 2147483647;

        private int _conciergeChannel = 42;
        private List<Scene> _scenes = new List<Scene>();
        private IConfig _config;
        private string _whoami = "conferencier";

        internal object _syncy = new object();

        #region IRegionModule Members
        public void Initialise(Scene scene, IConfigSource config)
        {
            try
            {
                if ((_config = config.Configs["Concierge"]) == null)
                {
                    _log.InfoFormat("[Concierge] no configuration section [Concierge] in OpenSim.ini: module not configured");
                    return;
                }

                if (!_config.GetBoolean("enabled", false))
                {
                    _log.InfoFormat("[Concierge] module disabled by OpenSim.ini configuration");
                    return;
                }

            }
            catch (Exception)
            {
                _log.Info("[Concierge] module not configured");
                return;
            }

            if (_config != null)
            {
                _conciergeChannel = config.Configs["Concierge"].GetInt("concierge_channel", _conciergeChannel);
                _whoami = _config.GetString("whoami", "conferencier");
            }
            _log.InfoFormat("[Concierge] reporting as \"{0}\" to our users", _whoami);

            lock (_syncy)
            {
                if (!_scenes.Contains(scene))
                {
                    _scenes.Add(scene);
                    // subscribe to NewClient events
                    scene.EventManager.OnNewClient += OnNewClient;
                    scene.EventManager.OnNewClient += OnNewClient;

                    // subscribe to *Chat events and FilterChat* events
                    scene.EventManager.OnChatFromWorld += OnSimChat;
                    scene.EventManager.OnChatBroadcast += OnSimBroadcast;

                    // subscribe to agent change events
                    scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
                    scene.EventManager.OnMakeChildAgent += OnMakeChildAgent;
                }
            }
            _log.InfoFormat("[Concierge] initialized for {0}", scene.RegionInfo.RegionName);
        }

        public void PostInitialise()
        {
        }

        public void Close()
        {
        }

        public string Name
        {
            get { return "ConciergeModule"; }
        }

        public bool IsSharedModule
        {
            get { return true; }
        }

        #endregion

        #region ISimChat Members
        public void OnSimBroadcast(Object sender, OSChatMessage c)
        {
            // log to buffer?
            return;
        }

        public void OnSimChat(Object sender, OSChatMessage c)
        {
            if (_conciergeChannel == c.Channel)
            { 
                // concierge request: interpret
                return;
            }

            if (0 == c.Channel || DEBUG_CHANNEL == c.Channel)
            {
                // if (_amplify)
                // {
                    
                // }

                // log as avatar/prim chat
                return;
            }

            return;
        }

        #endregion


        public void OnNewClient(IClientAPI client)
        {
            client.OnLogout += OnClientLoggedOut;
            client.OnConnectionClosed += OnClientLoggedOut;

            _log.DebugFormat("[Concierge] {0} logs on to {1}", client.Name, client.Scene.RegionInfo.RegionName);
            AnnounceToAgentsRegion(client, String.Format("{0} logs on to {1}", client.Name, client.Scene.RegionInfo.RegionName));
        }

        public void OnClientLoggedOut(IClientAPI client)
        {
            client.OnLogout -= OnClientLoggedOut;
            client.OnConnectionClosed -= OnClientLoggedOut;
            
            _log.DebugFormat("[Concierge] {0} logs off from {1}", client.Name, client.Scene.RegionInfo.RegionName);
            AnnounceToAgentsRegion(client, String.Format("{0} logs off from {1}", client.Name, client.Scene.RegionInfo.RegionName));
        }


        public void OnMakeRootAgent(ScenePresence agent)
        {
            _log.DebugFormat("[Concierge] {0} enters {1}", agent.Name, agent.Scene.RegionInfo.RegionName);
            AnnounceToAgentsRegion(agent, String.Format("{0} enters {1}", agent.Name, agent.Scene.RegionInfo.RegionName));
        }


        public void OnMakeChildAgent(ScenePresence agent)
        {
            _log.DebugFormat("[Concierge] {0} leaves {1}", agent.Name, agent.Scene.RegionInfo.RegionName);
            AnnounceToAgentsRegion(agent, String.Format("{0} leaves {1}", agent.Name, agent.Scene.RegionInfo.RegionName));
        }


        public void ClientLoggedOut(IClientAPI client)
        {
            _log.DebugFormat("[Concierge] {0} logs out of {1}", client.Name, client.Scene.RegionInfo.RegionName);
            AnnounceToAgentsRegion(client, String.Format("{0} logs out of {1}", client.Name, client.Scene.RegionInfo.RegionName));
        }


        static private Vector3 posOfGod = new Vector3(128, 128, 9999);

        protected void AnnounceToAgentsRegion(IClientAPI client, string msg)
        {
            ScenePresence agent = null;
            if ((client.Scene is Scene) && (client.Scene as Scene).TryGetAvatar(client.AgentId, out agent)) 
                AnnounceToAgentsRegion(agent, msg);
            else
                _log.DebugFormat("[Concierge] could not find an agent for client {0}", client.Name);
        }

        protected void AnnounceToAgentsRegion(ScenePresence scenePresence, string msg)
        {
            OSChatMessage c = new OSChatMessage();
            c.Message = msg;
            c.Type = ChatTypeEnum.Say;
            c.Channel = 0;
            c.Position = posOfGod;
            c.From = _whoami;
            c.Sender = null;
            c.SenderUUID = UUID.Zero;
            c.Scene = scenePresence.Scene;

            scenePresence.Scene.EventManager.TriggerOnChatBroadcast(this, c);
        }
    }
}