/*
* Copyright (c) Contributors, http://www.openmetaverse.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;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using OpenSim.Framework.Servers;
using OpenSim.Framework;
using OpenSim.Framework.Utilities;
using OpenSim.Framework.Types;
using OpenSim.Region.Caches;
using libsecondlife;

namespace OpenSim.Region.Capabilities
{
    public delegate void UpLoadedTexture(LLUUID assetID, LLUUID inventoryItem, byte[] data);

    public class Caps
    {
        private string httpListenerAddress;
        private uint httpListenPort;
        private string capsObjectPath = "00001-";
        private string requestPath = "0000/";
        private string mapLayerPath = "0001/";
        private string newInventory = "0002/";
        private string requestTexture = "0003/";
        private string eventQueue = "0100/";
        private BaseHttpServer httpListener;
        private LLUUID agentID;
        private AssetCache assetCache;
        private int eventQueueCount = 1;
        private Queue<string> CapsEventQueue = new Queue<string>();

        public Caps(AssetCache assetCach, BaseHttpServer httpServer, string httpListen, uint httpPort, string capsPath, LLUUID agent)
        {
            assetCache = assetCach;
            capsObjectPath = capsPath;
            httpListener = httpServer;
            httpListenerAddress = httpListen;
            httpListenPort = httpPort;
            agentID = agent;
        }

        /// <summary>
        /// 
        /// </summary>
        public void RegisterHandlers()
        {
            Console.WriteLine("registering CAPS handlers");
            httpListener.AddRestHandler("POST", "/CAPS/" + capsObjectPath + requestPath, CapsRequest);
            httpListener.AddRestHandler("POST", "/CAPS/" + capsObjectPath + mapLayerPath, MapLayer);
            httpListener.AddRestHandler("POST", "/CAPS/" + capsObjectPath + newInventory, NewAgentInventory);
            httpListener.AddRestHandler("POST", "/CAPS/" + capsObjectPath + eventQueue, ProcessEventQueue);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <param name="path"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public string CapsRequest(string request, string path, string param)
        {
           // Console.WriteLine("Caps Request " + request);
            string result = ""; 
            result = LLSDHelpers.SerialiseLLSDReply(this.GetCapabilities());
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected LLSDCapsDetails GetCapabilities()
        {
           /* string capURLS = "";
            capURLS += "<key>MapLayer</key><string>http://" + httpListenerAddress + ":" + httpListenPort.ToString() + "/CAPS/" + capsObjectPath + mapLayerPath + "</string>";
            capURLS += "<key>NewFileAgentInventory</key><string>http://" + httpListenerAddress + ":" + httpListenPort.ToString() + "/CAPS/" + capsObjectPath + newInventory + "</string>";
            //capURLS += "<key>RequestTextureDownload</key><string>http://" + httpListenerAddress + ":" + httpListenPort.ToString() + "/CAPS/" + capsObjectPath + requestTexture + "</string>";
            //capURLS += "<key>EventQueueGet</key><string>http://" + httpListenerAddress + ":" + httpListenPort.ToString() + "/CAPS/" + capsObjectPath + eventQueue + "</string>";
            return capURLS;*/

            LLSDCapsDetails caps = new LLSDCapsDetails();
            caps.MapLayer = "http://" + httpListenerAddress + ":" + httpListenPort.ToString() + "/CAPS/" + capsObjectPath + mapLayerPath;
            caps.NewFileAgentInventory = "http://" + httpListenerAddress + ":" + httpListenPort.ToString() + "/CAPS/" + capsObjectPath + newInventory;
            return caps;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <param name="path"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public string MapLayer(string request, string path, string param)
        {
            Encoding _enc = System.Text.Encoding.UTF8;
            Hashtable hash =(Hashtable) LLSD.LLSDDeserialize(_enc.GetBytes(request));
            LLSDMapRequest mapReq = new LLSDMapRequest();
            LLSDHelpers.DeserialiseLLSDMap(hash, mapReq );

            LLSDMapLayerResponse mapResponse= new LLSDMapLayerResponse();
            mapResponse.LayerData.Array.Add(this.BuildLLSDMapLayerResponse());
            string res = LLSDHelpers.SerialiseLLSDReply(mapResponse);
            
            //Console.WriteLine(" Maplayer response is " + res);
          
            return res;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected LLSDMapLayer BuildLLSDMapLayerResponse()
        {
            LLSDMapLayer mapLayer = new LLSDMapLayer();
            mapLayer.Right = 5000;
            mapLayer.Top = 5000;
            mapLayer.ImageID = new LLUUID("00000000-0000-0000-9999-000000000006");

            return mapLayer;
        }

        public string ProcessEventQueue(string request, string path, string param)
        {
            string res = "";
            
            if (this.CapsEventQueue.Count > 0)
            {
                lock (this.CapsEventQueue)
                {
                    string item = CapsEventQueue.Dequeue();
                    res = item;
                }
            }
            else
            {
                res = this.CreateEmptyEventResponse();
            }
            return res;
        }

        public string CreateEstablishAgentComms(string caps, string ipAddressPort)
        {
            string res = "<llsd><map><key>id</key><integer>" + eventQueueCount + "</integer>";
            res += "<key>events</key><array><map>";
            res += "<key>message</key><string>EstablishAgentCommunication</string>";
            res += "<key>body</key><map>";
            res += "<key>sim-ip-and-port</key><string>" + ipAddressPort + "</string>";
            res += "<key>seed-capability</key><string>" + caps + "</string>";
            res += "<key>agent-id</key><uuid>" + this.agentID.ToStringHyphenated() + "</uuid>";
            res += "</map>";
            res += "</map></array>";
            res += "</map></llsd>";
            eventQueueCount++;
            this.CapsEventQueue.Enqueue(res);
            return res;
        }

        public string CreateEmptyEventResponse()
        {
            string res = "<llsd><map><key>id</key><integer>" + eventQueueCount + "</integer>";
            res += "<key>events</key><array><map>";
            res += "</map></array>";
            res += "</map></llsd>";
            eventQueueCount++;
            return res;
        }

        public string NewAgentInventory(string request, string path, string param)
        {
            //Console.WriteLine("received upload request:"+ request);
            string res = "";
            LLUUID newAsset = LLUUID.Random();
            LLUUID newInvItem = LLUUID.Random();
            string uploaderPath = capsObjectPath + Util.RandomClass.Next(5000, 8000).ToString("0000");
            AssetUploader uploader = new AssetUploader(newAsset, newInvItem, uploaderPath, this.httpListener);
            httpListener.AddRestHandler("POST", "/CAPS/" + uploaderPath, uploader.uploaderCaps);
            string uploaderURL = "http://" + httpListenerAddress + ":" + httpListenPort.ToString() + "/CAPS/" + uploaderPath;
            //Console.WriteLine("uploader url is " + uploaderURL);
            res += "<llsd><map>";
            res += "<key>uploader</key><string>" + uploaderURL + "</string>";
            //res += "<key>success</key><boolean>true</boolean>";
            res += "<key>state</key><string>upload</string>";
            res += "</map></llsd>";
            uploader.OnUpLoad += this.UploadHandler;
            return res;
        }

        public void UploadHandler(LLUUID assetID, LLUUID inventoryItem, byte[] data)
        {
            // Console.WriteLine("upload handler called");
            AssetBase asset;
            asset = new AssetBase();
            asset.FullID = assetID;
            asset.Type = 0;
            asset.InvType = 0;
            asset.Name = "UploadedTexture" + Util.RandomClass.Next(1, 1000).ToString("000");
            asset.Data = data;
            this.assetCache.AddAsset(asset);
        }

        public class AssetUploader
        {
            public event UpLoadedTexture OnUpLoad;

            private string uploaderPath = "";
            private LLUUID newAssetID;
            private LLUUID inventoryItemID;
            private BaseHttpServer httpListener;
            public AssetUploader(LLUUID assetID, LLUUID inventoryItem, string path, BaseHttpServer httpServer)
            {
                newAssetID = assetID;
                inventoryItemID = inventoryItem;
                uploaderPath = path;
                httpListener = httpServer;

            }

            public string uploaderCaps(string request, string path, string param)
            {
                Encoding _enc = System.Text.Encoding.UTF8;
                byte[] data = _enc.GetBytes(request);
                //Console.WriteLine("recieved upload " + Util.FieldToString(data));
                LLUUID inv = this.inventoryItemID;
                string res = "";
                res += "<llsd><map>";
                res += "<key>new_asset</key><string>" + newAssetID.ToStringHyphenated() + "</string>";
                res += "<key>new_inventory_item</key><uuid>" + inv.ToStringHyphenated() + "</uuid>";
                res += "<key>state</key><string>complete</string>";
                res += "</map></llsd>";

               // Console.WriteLine("asset " + newAssetID.ToStringHyphenated() + " , inventory item " + inv.ToStringHyphenated());
                httpListener.RemoveRestHandler("POST", "/CAPS/" + uploaderPath);
                if (OnUpLoad != null)
                {
                    OnUpLoad(newAssetID, inv, data);
                }

                /*FileStream fs = File.Create("upload.jp2");
                BinaryWriter bw = new BinaryWriter(fs);
                bw.Write(data);
                bw.Close();
                fs.Close();*/
                return res;
            }
        }
    }
}