#region Disclaimer / License
// Copyright (C) 2013, Jackie Ng
// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie@gmail.com
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
// 
#endregion
using OSGeo.MapGuide.MaestroAPI;
using OSGeo.MapGuide.MaestroAPI.Resource;
using OSGeo.MapGuide.ObjectModels;
using OSGeo.MapGuide.ObjectModels.ApplicationDefinition;
using OSGeo.MapGuide.ObjectModels.LayerDefinition;
using OSGeo.MapGuide.ObjectModels.MapDefinition;
using OSGeo.MapGuide.ObjectModels.WatermarkDefinition;
using OSGeo.MapGuide.ObjectModels.WebLayout;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Maestro.Editors.Preview
{
    public class ResourcePreviewEngine
    {
        private string _rootUrl;
        private IEditorService _edSvc;

        public ResourcePreviewEngine(string rootUrl, IEditorService edSvc)
        {
            _rootUrl = rootUrl;
            _edSvc = edSvc;
        }

        static string GetLocale(string locale)
        {
            return string.IsNullOrEmpty(locale) ? "en" : locale; //NOXLATE
        }

        private string GenerateFeatureSourcePreviewUrl(IResource res, string locale)
        {
            string url = _rootUrl;
            if (!url.EndsWith("/")) //NOXLATE
                url += "/"; //NOXLATE

            var resId = res.ResourceID;
            var sessionId = _edSvc.SessionID;
            url += "schemareport/describeschema.php?viewer=basic&schemaName=&className=&resId=" + resId + "&sessionId=" + sessionId + "&locale=" + GetLocale(locale); //NOXLATE

            return url;
        }

        private string GenerateLayerPreviewUrl(IResource res, string locale)
        {
            string url = _rootUrl;
            if (!url.EndsWith("/")) //NOXLATE
                url += "/"; //NOXLATE

            var ldf = (ILayerDefinition)res;
            var sessionId = _edSvc.SessionID;
            var conn = res.CurrentConnection;

            string layerName = string.Empty;
            //Use feature source as name/label if new and unsaved
            if (_edSvc.IsNew)
                layerName = ResourceIdentifier.GetName(ldf.SubLayer.ResourceId);
            else
                layerName = ResourceIdentifier.GetName(_edSvc.ResourceID);

            var mdf = CreateLayerPreviewMapDefinition(ldf, sessionId, layerName, conn);
            //if (PropertyService.Get(ConfigProperties.PreviewViewerType, "AJAX").Equals("AJAX")) //NOXLATE
            if (PreviewSettings.UseAjaxViewer)
            {
                //Create temp web layout to house this map
                var wl = ObjectFactory.CreateWebLayout(_edSvc.GetEditedResource().CurrentConnection, new Version(1, 0, 0), mdf.ResourceID);

                //Add a custom zoom command (to assist previews of layers that aren't [0, infinity] scale)
                AttachPreviewCommands(wl);

                var resId = "Session:" + sessionId + "//" + Guid.NewGuid() + ".WebLayout"; //NOXLATE

                conn.ResourceService.SaveResourceAs(wl, resId);
                url += "mapviewerajax/?WEBLAYOUT=" + resId + "&SESSION=" + sessionId + "&LOCALE=" + GetLocale(locale); //NOXLATE
            }
            else
            {
                throw new NotImplementedException();
                ////Create temp flex layout
                //var appDef = ObjectFactory.CreatePreviewFlexLayout(conn);
                //var resId = "Session:" + sessionId + "//" + Guid.NewGuid() + ".ApplicationDefinition"; //NOXLATE
                //appDef.AddMapGroup("previewmap", true, mdfId); //NOXLATE

                //conn.ResourceService.SaveResourceAs(appDef, resId);
                //url += "fusion/templates/mapguide/preview/index.html?Session=" + sessionId + "&ApplicationDefinition=" + resId; //NOXLATE
            }

            return url;
        }

        internal static IMapDefinition CreateLayerPreviewMapDefinition(ILayerDefinition ldf, string sessionId, string layerName, IServerConnection conn)
        {
            //Create temp map definition to house our current layer
            var mdfId = "Session:" + sessionId + "//" + Guid.NewGuid() + ".MapDefinition"; //NOXLATE
            string csWkt;
            var extent = ldf.GetSpatialExtent(true, out csWkt);
            if (extent == null)
                throw new ApplicationException(Strings.FailedToCalculateFeatureSourceExtents);

            //TODO: Based on the visible scales in this layer, size this extents accordingly
            var mdf = ObjectFactory.CreateMapDefinition(conn, Strings.PreviewMap, csWkt, extent);

            var layer = mdf.AddLayer(null, layerName, ldf.ResourceID);
            conn.ResourceService.SaveResourceAs(mdf, mdfId);
            mdf.ResourceID = mdfId;
            return mdf;
        }

        private static void AttachPreviewCommands(IWebLayout wl)
        {
            var cmd = wl.CreateInvokeScriptCommand();
            cmd.Name = "ZoomScale"; //NOXLATE
            cmd.Label = Strings.Label_ZoomToScale;
            cmd.Tooltip = Strings.Desc_ZoomToScale;
            cmd.Script = @"
                var map = parent.parent.GetMapFrame();
                var center = map.GetCenter();
                var scale = parseFloat(prompt('Enter the scale:'));
                map.ZoomToView(center.X, center.Y, scale, true);
                "; //NOXLATE

            cmd.TargetViewer = TargetViewerType.Ajax;
            cmd.ImageURL = "../stdicons/icon_zoom.gif"; //NOXLATE

            wl.CommandSet.AddCommand(cmd);

            var cmd2 = wl.CreateInvokeScriptCommand();
            cmd2.Name = "GetMapKml"; //NOXLATE
            cmd2.Label = Strings.Label_GetMapKml;
            cmd2.Description = Strings.Desc_GetMapKml;
            cmd2.Tooltip = Strings.Desc_GetMapKml;

            cmd2.Script = @"
                var map = parent.parent.GetMapFrame();
                var url = ""../mapagent/mapagent.fcgi?OPERATION=GETMAPKML&VERSION=1.0.0&FORMAT=KML&DISPLAYDPI=96&MAPDEFINITION=" + wl.Map.ResourceId + @""";
                url += ""&SESSION="" + map.GetSessionId();
                window.open(url);"; //NOXLATE

            cmd2.TargetViewer = TargetViewerType.Ajax;
            cmd2.ImageURL = "../stdicons/icon_invokescript.gif"; //NOXLATE

            wl.CommandSet.AddCommand(cmd2);

            var cmd3 = wl.CreateInvokeScriptCommand();
            cmd3.Name = "GetExtents"; //NOXLATE
            cmd3.Label = Strings.Label_GetExtents;
            cmd3.Description = Strings.Desc_GetExtents;
            cmd3.Tooltip = Strings.Desc_GetExtents;

            cmd3.Script = @"
                var map = parent.parent.GetMapFrame();
                alert('Map Extents\n\nLower Left: ' + map.extX1 + ', ' + map.extY2 + '\nUpper Right: ' + map.extX2 + ', ' + map.extY1);
                "; //NOXLATE

            cmd3.TargetViewer = TargetViewerType.Ajax;
            cmd3.ImageURL = "../stdicons/icon_invokescript.gif"; //NOXLATE

            wl.CommandSet.AddCommand(cmd3);

            var zoomScale = wl.CreateCommandItem(cmd.Name);
            var menu = wl.CreateFlyout(Strings.Label_Tools, Strings.Label_Tools, Strings.Label_ExtraTools, string.Empty, string.Empty,
                wl.CreateCommandItem("ZoomScale"), //NOXLATE
                wl.CreateCommandItem("GetMapKml"), //NOXLATE
                wl.CreateCommandItem("GetExtents") //NOXLATE
            );
            wl.ToolBar.AddItem(menu);
        }

        private string GenerateWatermarkPreviewUrl(IWatermarkDefinition wmd, string locale)
        {
            //We demand a 2.3.0 Map Definition or higher
            if (wmd.CurrentConnection.SiteVersion < new Version(2, 3))
                throw new InvalidOperationException(Strings.SiteVersionDoesntSupportWatermarks);

            IMapDefinition2 map = CreateWatermarkPreviewMapDefinition(wmd);
            return GenerateMapPreviewUrl(map, locale);
        }

        internal static IMapDefinition2 CreateWatermarkPreviewMapDefinition(IWatermarkDefinition wmd)
        {
            IMapDefinition2 map = (IMapDefinition2)ObjectFactory.CreateMapDefinition(wmd.CurrentConnection, wmd.SupportedMapDefinitionVersion, "Watermark Definition Preview"); //NOXLATE
            map.CoordinateSystem = @"LOCAL_CS[""*XY-M*"", LOCAL_DATUM[""*X-Y*"", 10000], UNIT[""Meter"", 1], AXIS[""X"", EAST], AXIS[""Y"", NORTH]]"; //NOXLATE
            map.Extents = ObjectFactory.CreateEnvelope(-1000000, -1000000, 1000000, 1000000);
            map.AddWatermark(wmd);
            return map;
        }

        private string GenerateMapPreviewUrl(IResource res, string locale)
        {
            string url = _rootUrl;
            if (!url.EndsWith("/")) //NOXLATE
                url += "/"; //NOXLATE

            var sessionId = _edSvc.SessionID;
            var mdfId = "Session:" + sessionId + "//" + Guid.NewGuid() + ".MapDefinition"; //NOXLATE
            var mdf = (IMapDefinition)res;

            var conn = mdf.CurrentConnection;
            conn.ResourceService.SaveResourceAs(mdf, mdfId);

            //if (PropertyService.Get(ConfigProperties.PreviewViewerType, "AJAX").Equals("AJAX")) //NOXLATE
            if (PreviewSettings.UseAjaxViewer)
            {
                //Create temp web layout to house this map
                var wl = ObjectFactory.CreateWebLayout(_edSvc.GetEditedResource().CurrentConnection, new Version(1, 0, 0), mdfId);

                //Add a custom zoom command (to assist previews of layers that aren't [0, infinity] scale)
                AttachPreviewCommands(wl);

                var resId = "Session:" + sessionId + "//" + Guid.NewGuid() + ".WebLayout"; //NOXLATE

                conn.ResourceService.SaveResourceAs(wl, resId);
                url += "mapviewerajax/?WEBLAYOUT=" + resId + "&SESSION=" + sessionId + "&LOCALE=" + GetLocale(locale); //NOXLATE
            }
            else
            {
                throw new NotImplementedException();
                ////Create temp flex layout
                //var appDef = ObjectFactory.CreateFlexibleLayout(conn);
                //var resId = "Session:" + sessionId + "//" + Guid.NewGuid() + ".ApplicationDefinition"; //NOXLATE
                //appDef.AddMapGroup("previewmap", true, mdfId); //NOXLATE

                //conn.ResourceService.SaveResourceAs(appDef, resId);
                //url += "fusion/templates/mapguide/preview/index.html?Session=" + sessionId + "&ApplicationDefinition=" + resId; //NOXLATE
            }

            return url;
        }

        private string GenerateWebLayoutPreviewUrl(IResource res, string locale)
        {
            string url = _rootUrl;
            if (!url.EndsWith("/")) //NOXLATE
                url += "/"; //NOXLATE

            var sessionId = _edSvc.SessionID;
            var resId = "Session:" + sessionId + "//" + Guid.NewGuid() + ".WebLayout"; //NOXLATE
            var conn = res.CurrentConnection;

            conn.ResourceService.SaveResourceAs(res, resId);
            url += "mapviewerajax/?WEBLAYOUT=" + resId + "&SESSION=" + sessionId + "&LOCALE=" + GetLocale(locale); //NOXLATE

            return url;
        }

        private string GenerateFlexLayoutPreviewUrl(IResource res, string locale)
        {
            string url = _rootUrl;
            if (!url.EndsWith("/")) //NOXLATE
                url += "/"; //NOXLATE

            //Create temp flex layout
            var sessionId = _edSvc.SessionID;
            var appDef = (IApplicationDefinition)res;
            var conn = appDef.CurrentConnection;
            var resId = "Session:" + sessionId + "//" + Guid.NewGuid() + ".ApplicationDefinition"; //NOXLATE

            conn.ResourceService.SaveResourceAs(appDef, resId);
            url += appDef.TemplateUrl + "?Session=" + sessionId + "&ApplicationDefinition=" + resId + "&locale=" + GetLocale(locale); //NOXLATE
            return url;
        }

        public string GeneratePreviewUrl(IResource res, string locale)
        {
            switch (res.ResourceType)
            {
                case ResourceTypes.FeatureSource:
                    return GenerateFeatureSourcePreviewUrl(res, locale);
                case ResourceTypes.ApplicationDefinition:
                    return GenerateFlexLayoutPreviewUrl(res, locale);
                case ResourceTypes.LayerDefinition:
                    return GenerateLayerPreviewUrl(res, locale);
                case ResourceTypes.MapDefinition:
                    return GenerateMapPreviewUrl(res, locale);
                case ResourceTypes.WebLayout:
                    return GenerateWebLayoutPreviewUrl(res, locale);
                case ResourceTypes.WatermarkDefinition:
                    return GenerateWatermarkPreviewUrl((IWatermarkDefinition)res, locale);
                default:
                    throw new InvalidOperationException(Strings.UnpreviewableResourceType);
            }
        }

        private static ResourceTypes[] PREVIEWABLE_RESOURCE_TYPES = new ResourceTypes[] 
        {
            ResourceTypes.FeatureSource,
            ResourceTypes.ApplicationDefinition,
            ResourceTypes.LayerDefinition,
            ResourceTypes.MapDefinition,
            ResourceTypes.WebLayout,
            ResourceTypes.WatermarkDefinition
        };

        internal static bool IsPreviewableType(OSGeo.MapGuide.MaestroAPI.ResourceTypes rt)
        {
            return Array.IndexOf(PREVIEWABLE_RESOURCE_TYPES, rt) >= 0;
        }
    }
}