/*
 * 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.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Net;
using libsecondlife;
using Nini.Config;
using OpenJPEGNet;
using OpenSim.Region.Environment.Interfaces;
using OpenSim.Region.Environment.Scenes;
using Image=System.Drawing.Image;

//using Cairo;

namespace OpenSim.Region.Environment.Modules.Scripting.VectorRender
{
    public class VectorRenderModule : IRegionModule, IDynamicTextureRender
    {
        private string m_name = "VectorRenderModule";
        private Scene m_scene;
        private IDynamicTextureManager m_textureManager;

        public VectorRenderModule()
        {
        }

        #region IDynamicTextureRender Members

        public string GetContentType()
        {
            return ("vector");
        }

        public string GetName()
        {
            return m_name;
        }

        public bool SupportsAsynchronous()
        {
            return true;
        }

        public byte[] ConvertUrl(string url, string extraParams)
        {
            return null;
        }

        public byte[] ConvertStream(Stream data, string extraParams)
        {
            return null;
        }

        public bool AsyncConvertUrl(LLUUID id, string url, string extraParams)
        {
            return false;
        }

        public bool AsyncConvertData(LLUUID id, string bodyData, string extraParams)
        {
            Draw(bodyData, id, extraParams);
            return true;
        }

        #endregion

        #region IRegionModule Members

        public void Initialise(Scene scene, IConfigSource config)
        {
            if (m_scene == null)
            {
                m_scene = scene;
            }
        }

        public void PostInitialise()
        {
            m_textureManager = m_scene.RequestModuleInterface<IDynamicTextureManager>();
            if (m_textureManager != null)
            {
                m_textureManager.RegisterRender(GetContentType(), this);
            }
        }

        public void Close()
        {
        }

        public string Name
        {
            get { return m_name; }
        }

        public bool IsSharedModule
        {
            get { return true; }
        }

        #endregion

        private void Draw(string data, LLUUID id, string extraParams)
        {
            // TODO: this is a brutal hack.  extraParams should actually be parsed reasonably.
            int size = 256;
            try
            {
                size = Convert.ToInt32(extraParams);
            }
            catch (Exception e)
            {
//Ckrinke: Add a WriteLine to remove the warning about 'e' defined but not used
                Console.WriteLine("Problem with Draw. Please verify parameters." + e.ToString());
            }

            if ((size < 128) || (size > 1024))
                size = 256;

            Bitmap bitmap = new Bitmap(size, size, PixelFormat.Format32bppArgb);

            Graphics graph = Graphics.FromImage(bitmap);

            extraParams = extraParams.ToLower();
            int alpha = 255;
            if (extraParams == "setalpha")
            {
                alpha = 0;
            }
            else
            {
                graph.FillRectangle(new SolidBrush(Color.White), 0, 0, size, size);
            }

            for (int w = 0; w < bitmap.Width; w++)
            {
                for (int h = 0; h < bitmap.Height; h++)
                {
                    bitmap.SetPixel(w, h, Color.FromArgb(alpha, bitmap.GetPixel(w, h)));
                }
            }


            GDIDraw(data, graph);

            byte[] imageJ2000 = OpenJPEG.EncodeFromImage(bitmap, true);
            m_textureManager.ReturnData(id, imageJ2000);
        }

/*
        private void CairoDraw(string data, System.Drawing.Graphics graph)
        {
            using (Win32Surface draw = new Win32Surface(graph.GetHdc()))
            {
                Context contex = new Context(draw);

                contex.Antialias = Antialias.None;    //fastest method but low quality
                contex.LineWidth = 7;
                char[] lineDelimiter = { ';' };
                char[] partsDelimiter = { ',' };
                string[] lines = data.Split(lineDelimiter);

                foreach (string line in lines)
                {
                    string nextLine = line.Trim();

                    if (nextLine.StartsWith("MoveTO"))
                    {
                        float x = 0;
                        float y = 0;
                        GetParams(partsDelimiter, ref nextLine, ref x, ref y);
                        contex.MoveTo(x, y);
                    }
                    else if (nextLine.StartsWith("LineTo"))
                    {
                        float x = 0;
                        float y = 0;
                        GetParams(partsDelimiter, ref nextLine, ref x, ref y);
                        contex.LineTo(x, y);
                        contex.Stroke();
                    }
                }
            }
            graph.ReleaseHdc();
        }
*/

        private void GDIDraw(string data, Graphics graph)
        {
            Point startPoint = new Point(0, 0);
            Point endPoint = new Point(0, 0);
            Pen drawPen = new Pen(Color.Black, 7);
            Font myFont = new Font("Times New Roman", 14);
            SolidBrush myBrush = new SolidBrush(Color.Black);
            char[] lineDelimiter = {';'};
            char[] partsDelimiter = {','};
            string[] lines = data.Split(lineDelimiter);

            foreach (string line in lines)
            {
                string nextLine = line.Trim();
                //replace with switch, or even better, do some proper parsing
                if (nextLine.StartsWith("MoveTo"))
                {
                    float x = 0;
                    float y = 0;
                    GetParams(partsDelimiter, ref nextLine, 6, ref x, ref y);
                    startPoint.X = (int) x;
                    startPoint.Y = (int) y;
                }
                else if (nextLine.StartsWith("LineTo"))
                {
                    float x = 0;
                    float y = 0;
                    GetParams(partsDelimiter, ref nextLine, 6, ref x, ref y);
                    endPoint.X = (int) x;
                    endPoint.Y = (int) y;
                    graph.DrawLine(drawPen, startPoint, endPoint);
                    startPoint.X = endPoint.X;
                    startPoint.Y = endPoint.Y;
                }
                else if (nextLine.StartsWith("Text"))
                {
                    nextLine = nextLine.Remove(0, 4);
                    nextLine = nextLine.Trim();
                    graph.DrawString(nextLine, myFont, myBrush, startPoint);
                }
                else if (nextLine.StartsWith("Image"))
                {
                    float x = 0;
                    float y = 0;
                    GetParams(partsDelimiter, ref nextLine, 5, ref x, ref y);
                    endPoint.X = (int) x;
                    endPoint.Y = (int) y;
                    Image image = ImageHttpRequest(nextLine);
                    graph.DrawImage(image, (float) startPoint.X, (float) startPoint.Y, x, y);
                    startPoint.X += endPoint.X;
                    startPoint.Y += endPoint.Y;
                }
                else if (nextLine.StartsWith("Rectangle"))
                {
                    float x = 0;
                    float y = 0;
                    GetParams(partsDelimiter, ref nextLine, 9, ref x, ref y);
                    endPoint.X = (int) x;
                    endPoint.Y = (int) y;
                    graph.DrawRectangle(drawPen, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
                    startPoint.X += endPoint.X;
                    startPoint.Y += endPoint.Y;
                }
                else if (nextLine.StartsWith("FillRectangle"))
                {
                    float x = 0;
                    float y = 0;
                    GetParams(partsDelimiter, ref nextLine, 13, ref x, ref y);
                    endPoint.X = (int) x;
                    endPoint.Y = (int) y;
                    graph.FillRectangle(myBrush, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
                    startPoint.X += endPoint.X;
                    startPoint.Y += endPoint.Y;
                }
                else if (nextLine.StartsWith("Ellipse"))
                {
                    float x = 0;
                    float y = 0;
                    GetParams(partsDelimiter, ref nextLine, 7, ref x, ref y);
                    endPoint.X = (int) x;
                    endPoint.Y = (int) y;
                    graph.DrawEllipse(drawPen, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
                    startPoint.X += endPoint.X;
                    startPoint.Y += endPoint.Y;
                }
                else if (nextLine.StartsWith("FontSize"))
                {
                    nextLine = nextLine.Remove(0, 8);
                    nextLine = nextLine.Trim();
                    float size = Convert.ToSingle(nextLine, CultureInfo.InvariantCulture);
                    myFont = new Font("Times New Roman", size);
                }
                else if (nextLine.StartsWith("PenSize"))
                {
                    nextLine = nextLine.Remove(0, 8);
                    nextLine = nextLine.Trim();
                    float size = Convert.ToSingle(nextLine, CultureInfo.InvariantCulture);
                    drawPen.Width = size;
                }
                else if (nextLine.StartsWith("PenColour"))
                {
                    nextLine = nextLine.Remove(0, 9);
                    nextLine = nextLine.Trim();

                    Color newColour = Color.FromName(nextLine);

                    myBrush.Color = newColour;
                    drawPen.Color = newColour;
                }
            }
        }

        private static void GetParams(char[] partsDelimiter, ref string line, int startLength, ref float x, ref float y)
        {
            line = line.Remove(0, startLength);
            string[] parts = line.Split(partsDelimiter);
            if (parts.Length == 2)
            {
                string xVal = parts[0].Trim();
                string yVal = parts[1].Trim();
                x = Convert.ToSingle(xVal, CultureInfo.InvariantCulture);
                y = Convert.ToSingle(yVal, CultureInfo.InvariantCulture);
            }
            else if (parts.Length > 2)
            {
                string xVal = parts[0].Trim();
                string yVal = parts[1].Trim();
                x = Convert.ToSingle(xVal, CultureInfo.InvariantCulture);
                y = Convert.ToSingle(yVal, CultureInfo.InvariantCulture);

                line = "";
                for (int i = 2; i < parts.Length; i++)
                {
                    line = line + parts[i].Trim();
                    line = line + " ";
                }
            }
        }

        private Bitmap ImageHttpRequest(string url)
        {
            WebRequest request = HttpWebRequest.Create(url);
//Ckrinke: Comment out for now as 'str' is unused. Bring it back into play later when it is used.
//Ckrinke            Stream str = null;
            HttpWebResponse response = (HttpWebResponse) (request).GetResponse();
            if (response.StatusCode == HttpStatusCode.OK)
            {
                Bitmap image = new Bitmap(response.GetResponseStream());
                return image;
            }

            return null;
        }
    }
}