﻿Poseidon.Controls.Print.ModuleManager = OpenLayers.Class({
    /**
    * All the items in the print window.
    */
    items: null,

    /**
    * The page element which is not in the items list.
    */
    pageModule: null,

    /**
    * The map element which is not in the items list.
    */
    map: null,

    /** 
    * The map module which is not in the items list.
    */
    mapModule: null,

    /**
    * The initial configuration of the template.
    */
    initialConfig: null,

    /**
    * A scales lookup structure for the map.
    */
    scalesCorrected: [],
    scalesReal: [],

    /**
    * Constructor.
    * Create a new module loader instance.
    *
    * Parameters:
    * config - {struct} A template from a webservice.
    *
    * Returns:
    * An instance of the module loader.
    */
    initialize: function (config) {
        // Store the initial config.
        this.initialConfig = config;

        // Initialize the page element.
        if (config.page) {
            this.pageModule = new Poseidon.Controls.Print.Module.DefaultPage(config.page);
        }
    },

    /**
    * Load template. Do this AFTER map zoom scales and the paper sizes has been loaded.
    */
    loadTemplate: function (paperWidth, paperHeight, scales) {
        // Update the page size.
        if (this.pageModule.width / this.pageModule.height > 1) {
            this.pageModule.page_width = Math.max(paperWidth, paperHeight);
            this.pageModule.page_height = Math.min(paperWidth, paperHeight);
        } else {
            this.pageModule.page_width = Math.min(paperWidth, paperHeight);
            this.pageModule.page_height = Math.max(paperWidth, paperHeight);
        }

        // Calculate the OpenLayers scales based on the scales, the page size and the map size.
        this.setOpenLayersScales(scales);

        // Create the map.
        this.resetMap();

        // Load the modules into GUI elements.
        this.loadModules();
    },

    /**
    * Reset the scales. Call this when ever the paper size or the screen size of the page changes.
    */
    resetScales: function (paperWidth, paperHeight, scales) {
        // Update the page size.
        if (this.pageModule.width / this.pageModule.height > 1) {
            this.pageModule.page_width = Math.max(paperWidth, paperHeight);
            this.pageModule.page_height = Math.min(paperWidth, paperHeight);
        } else {
            this.pageModule.page_width = Math.min(paperWidth, paperHeight);
            this.pageModule.page_height = Math.max(paperWidth, paperHeight);
        }

        // Calculate the OpenLayers scales based on the scales, the page size and the map size.
        this.setOpenLayersScales(scales);

        // Create the map.
        this.resetMap();
    },

    // Private - Load the modules.
    loadModules: function () {

        this.items = [];

        // Create the map module.
        this.initialConfig.map.map = this.map;
        this.initialConfig.map.extent = this.map.getExtent();
        this.mapModule = new Poseidon.Controls.Print.Module.DefaultMap(this.initialConfig.map);

        // Create all the items.
        for (var index = 0; index < this.initialConfig.modules.length; index++) {
            var module = this.initialConfig.modules[index];
            module.dpi = this.pageModule.width / (this.pageModule.page_width / 10) * 2.54;

            var item = null;
            switch (module.module_name) {
                case 'default-page':
                    // Do nothing...
                    break;
                case 'default-map':
                    module.map = this.map;
                    module.extent = this.map.getExtent();
                    this.mapModule = new Poseidon.Controls.Print.Module.DefaultMap(module);
                    item = null;
                    break;
                case 'compass-simple':
                    item = new Poseidon.Controls.Print.Module.CompassSimple(module);
                    break;
                case 'compass-rose':
                    item = new Poseidon.Controls.Print.Module.CompassRose(module);
                    break;
                case 'default-legend':
                    module.map = this.map;
                    item = new Poseidon.Controls.Print.Module.DefaultLegend(module);
                    break;
                case 'default-map-header':
                    module.map = this.map;
                    item = new Poseidon.Controls.Print.Module.DefaultMapHeader(module);
                    break;
                case 'le34-map-header':
                    module.map = this.map;
                    item = new Poseidon.Controls.Print.Module.LE34MapHeader(module);
                    break;
                case 'default-scaleline':
                    module.map = this.map;
                    item = new Poseidon.Controls.Print.Module.DefaultScaleLine(module);
                    break;
                case 'default-overviewmap':
                    module.map = this.map;
                    // Locate the overview map - if there is one.
                    var control = null;
                    for (var controlID = 0; controlID < map.controls.length; controlID++) {
                        if (map.controls[controlID].CLASS_NAME === 'OpenLayers.Control.OverviewMap') {
                            control = map.controls[controlID];
                            break;
                        }
                    }

                    if (control) {
                        module.maxExtent = control.maxExtent;
                        module.minRatio = control.minRatio;
                        module.maxRatio = control.maxRatio;
                    } else {
                        module.maxExtent = map.maxExtent;
                        module.minRatio = 32;
                        module.maxRatio = 64;
                    }

                    item = new Poseidon.Controls.Print.Module.DefaultOverviewMap(module);
                    break;
                default:
                    alert('Module not supportet yet.');
                    break;
            }
            if (item) {
                item.visible = true;
                this.items.push(item);
            }
        }
    },

    /**
    * Get the visible/real scale by supplying the corresponding corrected OpenLayers scale.
    */
    getRealScale: function (correctedScale) {
        for (var index = 0; index < this.scalesCorrected.length; index++) {
            if (this.scalesCorrected[index] === correctedScale) {
                return this.scalesReal[index];
            }
        }
    },

    /**
    * Get the corrected scale by specifying the corresponding real scale.
    */
    getCorrectedScale: function (realScale) {
        for (var index = 0; index < this.scalesReal.length; index++) {
            if (this.scalesReal[index] === realScale) {
                return this.scalesCorrected[index];
            }
        }
    },

    /**
    * Recalculate the OpenLayers scale from the page size.
    */
    setOpenLayersScales: function (scales) {
        // To compute these scales the following are necessary.
        if (this.pageModule && scales) {

            // Reset the scales.
            this.scalesCorrected = [];
            this.scalesReal = [];

            // Calculate the page-screen DPI.
            var dpi = this.pageModule.width / (this.pageModule.page_width / 10) * 2.54;

            // Go through the scales and calculate the correct OpenLayers scales.
            for (var index = 0; index < scales.length; index++) {
                var scale = scales[index];
                this.scalesReal.push(scale);

                // Multiply the scale with the DPI difference.                
                this.scalesCorrected.push(Math.round(scale * OpenLayers.DOTS_PER_INCH / dpi));
            }
        }
    },

    resetMap: function () {
        //        if (this.map) {
        //            this.map.destroy();
        //        }
        if (!this.map) {
            // Create a new map object.
            this.map = new OpenLayers.Map('', {
                projection: map.projection,
                units: map.units,
                maxExtent: map.maxExtent,
                //minResolution: map.minResolution,
                //maxResolution: map.maxResolution,
                //minScale: scales[scales.length - 1],
                //maxScale: scales[0],
                //resolution: map.resolution,
                //resolution: resolutions,
                //scales: [500000, 250000, 100000, 75000, 50000, 25000, 10000, 5000, 2000, 1000, 500],
                scales: this.scalesCorrected,
                //numZoomLevels: scales.length,
                restrictedExtent: map.restrictedExtent,
                //zoom: map.zoom,
                controls: [
                new OpenLayers.Control.Navigation()
            ]
            });

            // Add the visible layers to the new map.
            for (var layerIndex = 0; layerIndex < map.layers.length; layerIndex++) {
                if (map.layers[layerIndex].visibility) {
                    this.map.addLayer(map.layers[layerIndex].clone());
                }
            }
        } else {
            // The map just needs to be updated.

            // Set the scales.
            this.map.scales = this.scalesCorrected;

            // Do the same on all the layers and force a redraw with the new scalings.
            for (var index = 0; index < this.map.layers.length; index++) {
                this.map.layers[index].initResolutions();
                this.map.layers[index].redraw();
            }
        }
    },

    /** 
    * Destroy the manager.
    */
    destroy: function () {
        // GUI items. They are not destroyed - so far we are relying on the print tool to do this.        
        this.pageModule = null;
        this.mapModule = null;
        this.items = null;    
        this.map = null;

        this.initialConfig = null;
        this.scalesCorrected = null;
        this.scalesReal = null;
    }
});
