using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nft.framework.drawing;
using WinFormsGraphicsDevice;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;
using System.Xml;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
using nft.framework;
using SysColor = System.Drawing.Color;
using SysRect = System.Drawing.Rectangle;
using Microsoft.Xna.Framework;
using nft.util;

namespace nft.xna
{

    public class XnaGraphicManager : IGraphicManager, IDisposable
    {
        internal static XnaGraphicManager theInstance;

        #region static internal Properties
        /// <summary>
        /// このコントロールへの描画に使用できる GraphicsDevice を取得します。
        /// </summary>
        internal static GraphicsDevice GraphicsDevice
        {
            get { return theInstance.graphicsDeviceService.GraphicsDevice; }
        }

        /// <summary>
        /// このサンプルの IGraphicsDeviceService を含む IServiceProvider を取得します。
        /// これは、ContentManager などのコンポーネントで使用できます。これらの
        /// コンポーネントはこのサービスを使用して、GraphicsDevice を取得します。
        /// </summary>
        internal static ServiceContainer Services
        {
            get { return theInstance.services; }
        }

        internal static TextureLoader TextureLoader
        {
            get { return theInstance.texLoader; }
        }

        #endregion

        public IImageSrc LoadImageFromFile(ResourceKey rkey, string path, SysColor colorkey)
        {
            DateTime time_org = File.GetCreationTime(path);
            if (!SysColor.Transparent.Equals(colorkey))
            {
                return LoadCovertedImageFile(rkey.DataDirectory, path, colorkey);
            }
            else
            {
                // Assume GIF or PNG with transparent background.
                return new XnaImageSource(path);
            }
        }

        public IImageSrc LoadImageFromFileWithDefaultColorKey(ResourceKey rkey, string path) {
            string ext = Path.GetExtension(path);
            if (ext.ToLower().Equals(".bmp")) {
                return LoadCovertedImageFile(rkey.DataDirectory, path, SysColor.Transparent);
            } else {
                // Assume GIF or PNG with transparent background.
                return new XnaImageSource(path);
            } 
        }

        private IImageSrc LoadCovertedImageFile(string data_subdir, string path, SysColor colorkey) {
            DateTime time_org = File.GetCreationTime(path);
            string savefile = Path.GetFileName(path) + ".png";
            savefile = Path.Combine(data_subdir, savefile);
            bool create = true;
            // Create png if not exist or older than original.
            if (File.Exists(savefile)) {
                DateTime time_cache = File.GetCreationTime(savefile);
                if (time_cache > time_org)
                    create = false;
            }
            if (create) {
                TextureLoader.SaveAsPng(path, colorkey, savefile);
            }
            return new XnaImageSource(savefile);
        }

        /// <summary>
        /// Create Static Texture using part of an image, specified by region
        /// </summary>
        /// <param name="src"></param>
        /// <param name="region"></param>
        /// <returns></returns>
        public ITexture CreateStaticTexture(ResourceKey rkey, IImageSrc src, SysRect region)
        {
            return new XnaTexture(src, region);
        }

        /// <summary>
        /// Create Static Texture using whole image.
        /// </summary>
        /// <param name="src"></param>
        /// <returns></returns>
        public ITexture CreateStaticTexture(ResourceKey rkey, IImageSrc src)
        {
            return new XnaTexture(src);
        }

        public ISurface CreateOffscreenSurface(IImageSrc src, SysRect region)
        {
            throw new NotImplementedException();
        }

        public ITerrainPlateTemplate CreateTerrainTemplate(ResourceKey rkey, SysColor basecol, Point3D[] vertices, float brightness) {
            Vector3[] v = XnaUtil.ToVector3Array(vertices);
            return new PlainTerrainTemplate(XnaUtil.ToXnaColor(basecol), v, brightness);
        }
        public ITerrainPlateTemplate CreateTerrainTemplate(ResourceKey rkey, ITexture texture, Point3D[] vertices, PointF[] texture_pos, float brightness)
        {
            throw new NotImplementedException();
        }

        private TerrainPlate stock = new TerrainPlate();
        public ITerrainPlate CreateTerrainPlate(UInt32 id, ITerrainPlateTemplate template)
        {
            //return new TerrainPlate(id, (TerrainTemplate)template);
            stock.Init(id, (TerrainTemplate)template);
            return stock;
        }

        public ICubicStructure CreateStructure(UInt32 id, ResourceKey rkey, ITexture texture, Point3D size3d) {
            Vector3 vsz = XnaUtil.ToVector3(size3d);
            return new Structure(id, texture as XnaTexture, vsz);
        }

        public IBillboard CreateBillboard(ITexture texture) {
            throw new NotImplementedException();
        }

        public IOverlay CreateOverlay(ITexture texture) {
            throw new NotImplementedException();
        }

        public ISurface CreateSurfaceForControl(Control parent, out Control eventtarget, SurfaceUsage usage) {
            XnaControl ctrl = new XnaControl(usage);
            ctrl.Dock = DockStyle.Fill;
            parent.Controls.Add(ctrl);
            eventtarget = ctrl;
            return ctrl.Surface;
        }

        public ISurface CreateOffscreenSurface(Size size, SurfaceUsage usage) {
            throw new NotImplementedException();
        }

        public ISurface CreateOffscreenSurface(IImageSrc src) {
            throw new NotImplementedException();
        }

        public IEffectFilter GetFilterByUsage() {
            return new FilterSelected();
        }

        #region constructor and private fields
        TextureLoader texLoader;
        string name, description;
        GraphicsDeviceService graphicsDeviceService;
        ServiceContainer services;

        public XnaGraphicManager(XmlNode node)
        {
            string atr_name, atr_desc;
            XmlAttribute a = node.Attributes["name"];
            if (a != null)
                atr_name = a.Value;
            else
                atr_name = "XNA4.0 GraphicsManager";
            XmlNode n = node.SelectSingleNode("description");
            if (n != null)
                atr_desc = n.InnerText;
            else
                atr_desc = "GraphicManager Powered by Microsoft XNA4.0 (ShaderModel2.0 Required)";

            Initialize(atr_name, atr_desc);
            ConfigureService.OnSystemInfoRequested += new AdditonalSystemInfoHandler(ConfigureService_OnSystemInfoRequested);
        }

        void ConfigureService_OnSystemInfoRequested(ConfigureService.SystemInfoText infoText) {
            string text;
            GraphicsAdapter adp = GraphicsDevice.Adapter;
            text = string.Format("{0} (revidion:{1})", adp.Description, adp.Revision);
            infoText.Append("Display Adapter", text);
            DisplayMode dmode = GraphicsDevice.DisplayMode;
            text = string.Format("{0} state:{1})", dmode, GraphicsDevice.GraphicsDeviceStatus);
            infoText.Append("Display Mode", text);     
        }

        void Initialize(string name, string desc){
            this.name = name;
            this.description = desc;
            Size sz = Main.mainFrame.ClientSize;
            sz.Width = Math.Min(sz.Width, 640);
            sz.Height = Math.Min(sz.Height, 480);
            graphicsDeviceService = GraphicsDeviceService.AddRef(Main.mainFrame.Handle, sz.Width, sz.Height);
            services = new ServiceContainer();
            services.AddService<IGraphicsDeviceService>(graphicsDeviceService);
            texLoader = new TextureLoader(graphicsDeviceService.GraphicsDevice);
            
            // Singleton
            if (theInstance != null)
                theInstance.Dispose();
            theInstance = this;
        }
        #endregion

        #region IGlobalModule implementation
        public Type RegistType
        {
            get {
                Type t = this.GetType().GetInterface("IGraphicManager");
                Debug.Assert((t != null) && (null != this as IGraphicManager));
                return t;
            }
        }

        public string Name
        {
            get { return name; }
        }

        public string Description
        {
            get { return description; }
        }
        #endregion

        #region IDisposable implementation
        public void Dispose()
        {
            texLoader = null;
            if (graphicsDeviceService != null)
            {
                graphicsDeviceService.Release(true);
                graphicsDeviceService = null;
            }
        }

        #endregion

    }
}
