/*
 * LINGUIST.LLMAP.Map
 * Written by Benjamin J. Cool <ben@linguistlist.org>
 *            Joshua M. Thompson <joshua@linguistlist.org>
 *
 * This class provides a map as an Ext.BoxComponent
 */

LINGUIST.LLMAP.Map = Ext.extend(Ext.BoxComponent, {
    basemaps: [
        new LINGUIST.LLMAP.SatelliteBaseMap,
        new LINGUIST.LLMAP.VectorBaseMap
    ],

    enableDD: false,

    projection: 'EPSG:4326',

    defaultZoom: 4,
    defaultLonLat: { lat: 0, lon: 0 },

    basemap: null,      // The LINGUIST.LLMAP.Service object of the current base layer
    services: [],       // Service objects for any additional services being displayed

    project_mode: false,

    constructor: function(config) {
        LINGUIST.LLMAP.Map.superclass.constructor.call(this, config);

        this.addEvents('addservice', 'olclick', 'olrightclick', 'removeservice', 'setbasemap');
    },

    addService: function(service) {
        this.services.push(service);

        service.updateLayers();

        this.map.addLayer(service.map_layer);

        var len = this.services.length;
        var top = len;

        for (var i = len - 1 ; i >= 0 ; i--) {
            var s = this.services[i];

            if (s.alwaysOnTop) {
                this.map.setLayerIndex(s.map_layer, len);
            }
        }

        this.fireEvent('addservice', this, service);
    },

    clearMap: function() {
        var i = this.services.length;

        while (--i >= 0) {
            var service = this.services[i];

            if (!this.services[i].isPermanent) {
                this.services.splice(i, 1);

                this.map.removeLayer(service.map_layer);

                this.fireEvent('removeservice', this, service);
            }
        }
    },

    loadService: function(config, callback, scope) {
        var transid;

        Ext.MessageBox.show({
            title: 'Please Wait',
            msg: 'Loading service information',
            wait: true,
            waitConfig: { interval: 200 },
            buttons: Ext.MessageBox.CANCEL,
            icon: Ext.MessageBox.INFO,
            fn: function() { Ext.Ajax.abort(transid); }
        });

        if (config.id) {
            transid = Ext.Ajax.request({
                method: 'GET',
                url: '/services/' + config.id + '/details.json',
                client_scope: scope,
                client_callback: callback,
                scope: this,
                failure: function() {
                    Ext.MessageBox.hide();
                },
                success: function (response, options) {
                    Ext.MessageBox.hide();

                    this.onLoadService(response, options);
                }
            });
        }
        else {
            transid = Ext.Ajax.request({
                method: 'GET',
                url: '/service.json',
                params: {
                    type: config.type,
                    server: config.url,
                    service: config.service
                },
                client_scope: scope,
                client_callback: callback,
                scope: this,
                failure: function() {
                    Ext.MessageBox.hide();
                },
                success: function (response, options) {
                    Ext.MessageBox.hide();

                    this.onLoadService(response, options);
                }
            });
        }
    },

    notifyDrop: function(source, e, data) {
        var service = source.dragData.node.attributes.service;

        if (e.getTarget('.olMapViewport') && service && (service.type != 'folder')) {
            this.loadService(service, this.addService, this);
        }
    },

    onLoadService: function(response, options) {
        var result = Ext.decode(response.responseText);

        if (result.success) {
            var data = result.data;
            var type = data.type;
            var service;

            for (var i = 0 ; i < data.layers.length ; i++) {
                var layer = data.layers[i];

                layer.visible     = layer.visible == 1? true : false;
                layer.show_legend = layer.show_legend == 1? true : false;
            }

            if (type == 'ims') {
                service = new LINGUIST.LLMAP.IMSService(data);
            }
            else if (type == 'project') {
                service = new LINGUIST.LLMAP.ProjectService(data);
            }
            else if (type == 'wms') {
                service = new LINGUIST.LLMAP.WMSService(data);
            }

            options.client_callback.call(options.client_scope, service);
        }
        else {
            Ext.MessageBox.show({
                title: 'Error',
                msg: 'Unable to retrieve service information: ' + result.error,
                buttons: Ext.MessageBox.OK,
                icon: Ext.MessageBox.ERROR,
                width: 400
            });
        }
    },

    onRender: function(container, position) {
        var fragment = {
            tag: 'div',
            style: {
                'position': 'absolute',
                'background-color': '#000014',
                'top': 0,
                'left': 0,
                'bottom': 0,
                'right': 0
            }
        };

        if (position) {
            this.el = Ext.DomHelper.insertBefore(postion, fragment, true);
        } else {
            this.el = Ext.DomHelper.append(container, fragment, true);
        }

        if (this.id) {
            this.el.dom.id = this.id;
        }

        var map = new OpenLayers.Map(this.el.dom.id, {
            controls: [],
            displayProjection: new OpenLayers.Projection(this.projection),
            units: 'degrees'
        });

        var slider = new OpenLayers.Control.PanZoomBar();		
        var nav    = new OpenLayers.Control.Navigation({ handleRightClicks: true });

        map.addControl(slider);
        map.addControl(nav);
        map.addControl(new OpenLayers.Control.ZoomBox());
/*
        map.addControl(new OpenLayers.Control.OverviewMap({
            layers: [
                new OpenLayers.Layer.WMS(
                    'Overview Map',
                    'http://maggie.linguistlist.org/geoserver/wms',
                    {
                        layers: [ 'llmap:CONTINENTS' ],
                        styles: 'countries',
                        bgcolor: '#3C64FF'
                    },
                    {
                        singleTile: true,
                        ratio: 1
                    }
                )
            ]
        }));
*/
        //map.addControl(new OpenLayers.Control.Permalink());

        nav.handlers.click.callbacks.click = this.handleClick.createDelegate(this);
        nav.handlers.click.callbacks.rightclick = this.handleRightClick.createDelegate(this);

        this.map    = map;
        this.slider = slider;

        if (!this.project_mode) {
            this.bmc = new LINGUIST.LLMAP.BaseMapControl({ basemaps: this.basemaps });
            this.bmc.render(this.el);
            this.bmc.addListener({
                scope: this,
                change: function(bmc, service) { this.setBaseMap(service); }
            });

            this.setBaseMap(this.basemaps[0]);
            this.map.setCenter(new OpenLayers.LonLat(this.defaultLonLat.lon, this.defaultLonLat.lat), this.defaultZoom);
        }

        this.setControlOffsets(0, 0, 0, 0);

        if (this.enableDD) {
            var dt_config = {
                ddGroup: 'MapServiceDD',

                getTargetFromEvent: function(e) { return ; },

                notifyDrop: this.notifyDrop.createDelegate(this), 

                notifyEnter: function(source, e, data) {
                    var service = source.dragData.node.attributes.service;

                    if (e.getTarget('.olMapViewport') && service && (service.type != 'folder')) {
                        if (this.overClass) {
                            this.el.addClass(this.overClass);
                        }

                        return this.dropAllowed;
                    }
                    else {
                        return this.dropNotAllowed;
                    }
                },

                notifyOver: function(source, e, data) {
                    var service = source.dragData.node.attributes.service;

                    if (e.getTarget('.olMapViewport') && service && (service.type != 'folder')) {
                        return this.dropAllowed;
                    }
                    else {
                        return this.dropNotAllowed;
                    }
                }
            }

            if (this.dropConfig) {
                Ext.apply(dt_config, this.dropConfig);
            }

            this.dt = new Ext.dd.DropTarget(this.map.viewPortDiv, dt_config);
        }
    },

    removeService: function(service) {
        for (var i = 0 ; i < this.services.length ; i++) {
            if (this.services[i] === service) {

                this.services.splice(i, 1);

                this.map.removeLayer(service.map_layer);

                this.fireEvent('removeservice', this, service);

                break;
            }
        }
    },

    handleClick: function(evt) {
        this.fireEvent('olclick', evt);
    },

    handleRightClick: function(evt) {
        this.fireEvent('olrightclick', evt);
    },

    setBaseMap: function(service){
        if (this.basemap) {
            this.map.removeLayer(this.basemap.map_layer);
        }

        this.basemap = service;

        this.map.addLayer(service.map_layer);
        this.map.setBaseLayer(service.map_layer);

        this.fireEvent('setbasemap', this, service);
    },

    setControlOffsets: function(off_left, off_right, off_top, off_bottom) {
        if (this.bmc) {
            this.bmc.setPosition(this.getBox().width - this.bmc.getBox().width - off_right, off_top);
        }

        if (this.slider) {
            this.slider.moveTo(new OpenLayers.Pixel(off_left, off_top));
        }
    },

    zoomToBounds: function(minx, miny, maxx, maxy) {
        this.map.zoomToExtent(new OpenLayers.Bounds(minx, miny, maxx, maxy));
    }
});

Ext.ComponentMgr.registerType('map', LINGUIST.LLMAP.Map);

LINGUIST.LLMAP.BaseMapControl = Ext.extend(Ext.BoxComponent, {
    basemaps: [],

    selectedIndex: 0,

    constructor: function(config) {
        LINGUIST.LLMAP.BaseMapControl.superclass.constructor.call(this, config);

        this.addEvents('change');
    },

    getBaseMap: function(index) {
        return this.basemaps[index];
    },

    onRender: function(container, position) {
        LINGUIST.LLMAP.BaseMapControl.superclass.onRender.call(this, container, position);

        var maps = [];

        var dom = {
            tag: 'div',
            cls: 'llmap-bmc',
            children: [
                {
                    tag: 'table',
                    children: [
                        {
                            tag: 'tr',
                            children: maps
                        }
                    ]
                }
            ]
        };

        for (var i = 0 ; i < this.basemaps.length ; i++) {
            var bm = this.basemaps[i];

            if (i == this.selectedIndex) {
                maps.push({ tag: 'td', cls: 'llmap-bmc-selected', html: bm.map_layer.name });
            }
            else {
                maps.push({ tag: 'td', html: bm.map_layer.name });
            }
        }

        if (position) {
            this.el = Ext.DomHelper.insertBefore(postion, dom, true);
        } else {
            this.el = Ext.DomHelper.append(container, dom, true);
        }

        if (this.id) {
            this.el.dom.id = this.id;
        }

        this.cells = this.el.select('td');

        this.cells.addClassOnOver('llmap-bmc-hover');

        this.cells.on('click', function(e) {
            var index = e.target.cellIndex;

            if (index != this.selectedIndex) {
                this.cells.removeClass('llmap-bmc-selected');

                Ext.get(e.target).addClass('llmap-bmc-selected');

                this.selectedIndex = index;

                this.fireEvent('change', this, this.basemaps[index]);
            }
        }, this);
    }
});

Ext.ComponentMgr.registerType('basemapcontrol', LINGUIST.LLMAP.BaseMapControl);

/*
 * LINGUIST.LLMAP.Legend
 * Written by Joshua M. Thompson <joshua@linguistlist.org>
 *
 * This class implements a map legend as a BoxComponent.
 * It monitors the map to which it is attached for service or
 * layer modifications and updates itself accordingly.
 */

LINGUIST.LLMAP.Legend = Ext.extend(Ext.BoxComponent, {
    map: null,
    legends: [],

    attach: function(map) {
        this.map = map;

        map.addListener({
            scope: this,
            addservice: this.handleAddService,
            removeservice: this.handleRemoveService
        });
    },

    findServiceEl: function(service) {
        var el;

        for (var i = 0 ; i < this.legends.length ; i++) {
            if (this.legends[i].service === service) {
                return this.legends[i].el;
            }
        }

        return null;
    },

    handleAddService: function(map, service) {
        service.addListener('setvisible', this.handleSetVisible, this);
        service.addListener('update',     this.handleUpdate,     this);

        var fragment = {
            tag: 'div',
            cls: 'legend',
            children: [
                {
                    tag: 'h1',
                    html: service.canonical_name
                }
            ]
        };

        var el = Ext.DomHelper.insertFirst(this.el, fragment, true);
        el.setVisibilityMode(Ext.Element.DISPLAY);

        this.legends.push({
            service: service,
            el: el
        });

        this.handleUpdate(service);
    },

    handleRemoveService: function(map, service) {
        service.removeListener('setvisible', this.handleSetVisible, this);
        service.removeListener('update',     this.handleUpdate,     this);

        for (var i = 0 ; i < this.legends.length ; i++) {
            var l = this.legends[i];

            if (l.service === service) {
                l.el.remove();

                this.legends.splice(i, 1);

                break;
            }
        }
    },

    handleSetVisible: function(service, old_visible, visible) {
        var el = this.findServiceEl(service);

        if (el) {
            el.setVisible(visible);
        }
    },

    handleUpdate: function(service) {
        var el    = this.findServiceEl(service);
        var scale = this.map.map.getScale();

        if (el) {
            el.select('img').remove();
        }

        for (var i = 0 ; i < service.layers.length ; i++) {
            var l = service.layers[i];

            if (l.visible && l.show_legend) {
                var url = service.getLegendGraphic(l, scale);

                if (url != '') {
                    Ext.DomHelper.append(el, {
                        tag: 'img',
                        src: url
                    });
                }
            }
        }
    },

    handleZoomEnd: function() {
        for (var i = 0 ; i < this.legends.length ; i++) {
            this.handleUpdate(this.legends[i].service);
        }
    },

    onRender: function(container, position) {
        var fragment = {
            tag: 'div',
            style: {
                background: 'white',
                border: 'none',
                margin: 0,
                padding: 0
            }
        };

        if (position) {
            this.el = Ext.DomHelper.insertBefore(postion, fragment, true);
        } else {
            this.el = Ext.DomHelper.append(container, fragment, true);
        }

        if (this.id) {
            this.el.dom.id = this.id;
        }
    }
});

Ext.ComponentMgr.registerType('legend', LINGUIST.LLMAP.Legend);
