﻿if (!Poseidon.Controls.Print.Module) {
    Poseidon.Controls.Print.Module = {};
}

Poseidon.Controls.Print.Module.DefaultScaleLine = Ext.extend(Poseidon.Controls.Print.Module.DraggableModule, {
    style: 'background-color: transparent;',
    bodyStyle: 'background-color: transparent; border: 0px solid transparent;',

    autoHeight: true,
    stretch: this.stretch || false,

    moduleType: 'default_scaleline',

    imageSrc: '/images/print/defaultscaleline.png',

    map: this.map,

    layout: 'fit',

    initComponent: function () {
        this.divScaleLine = new Ext.BoxComponent({
            id: 'poseidon-control-print-defaultscaleline',
            autoEl:
            {
                tag: 'div'
            },
            listeners:
            {
                afterrender: function (box) {
                    this.scalelineControl = new OpenLayers.Control.ScaleLine(
                    {
                        maxWidth: this.width,
                        topInUnits: 'm',
                        topOutUnits: 'km',
                        bottomInUnits: '',
                        bottomOutUnits: '',
                        div: this.divScaleLine.el.dom
                    });

                    this.map.addControl(this.scalelineControl);
                },
                scope: this
            }
        });

        var config = {
            items:
            [
                this.divScaleLine
            ]
        };

        Ext.apply(this, Ext.apply(this.initialConfig, config));

        for (var i = 0; i < this.map.controls.length; i++) {
            if (this.map.controls[i].CLASS_NAME === 'OpenLayers.Control.ScaleLine') {
                this.map.removeControl(this.map.controls[i]);
            }
        }

        Poseidon.Controls.Print.Module.DefaultScaleLine.superclass.initComponent.apply(this, arguments);
    },

    getObject: function () {
        this.maxWidth = this.width;

        for (var i = 0; i < map.controls.length; i++) {
            if (map.controls[i].CLASS_NAME === 'OpenLayers.Control.ScaleLine') {
                this.topInUnits = map.controls[i].topInUnits;
                this.topOutUnits = map.controls[i].topOutUnits;
                this.bottomInUnits = map.controls[i].bottomInUnits;
                this.bottomOutUnits = map.controls[i].bottomOutUnits;
            }
        }

        this.update();

        return {
            "module_id": this.module_id,
            "module_name": this.module_name,
            "width": this.topWidth,
            "height": this.getSize().height * 0.5,
            "x": this.x,
            "y": this.y,
            "align_x": this.align_x || this.x || 0,
            "align_y": this.align_y || this.y || 0,
            "align": this.align,
            "top_width": this.topWidth || this.width || 0,
            "bottom_width": this.bottomWidth || this.width || 0,
            "top_scale": this.topScale || 0,
            "bottom_scale": this.bottomScale || 0
        };
    },

    z_index: this.z_index || 100,

    topInUnits: 'm',
    topOutUnits: 'km',
    scaleTop: 0,
    topWidth: this.width,
    bottomInUnits: 'm',
    bottomOutUnits: 'km',
    scaleBottom: 0,
    bottomWidth: this.width,

    /** The following methods are taken from OpenLayers.Control.ScaleLine (OpenLayers/Control/ScaleLine.js).
    * The methods has been altered to use references in this module instead of the initial implementation.
    *
    **/

    /**
    * Method: update
    * Update the size of the bars, and the labels they contain.
    */
    update: function () {
        var res = this.map.getResolution();
        if (!res) {
            return;
        }

        var curMapUnits = this.map.getUnits();
        var inches = OpenLayers.INCHES_PER_UNIT;

        // convert maxWidth to map units
        var maxSizeData = this.maxWidth * res * inches[curMapUnits];
        var geodesicRatio = 1;
        if (this.geodesic === true) {
            var maxSizeGeodesic = this.getGeodesicLength(this.maxWidth);
            var maxSizeKilometers = maxSizeData / inches["km"];
            geodesicRatio = maxSizeGeodesic / maxSizeKilometers;
            maxSizeData *= geodesicRatio;
        }

        // decide whether to use large or small scale units     
        var topUnits;
        var bottomUnits;
        if (maxSizeData > 100000) {
            topUnits = this.topOutUnits;
            bottomUnits = this.bottomOutUnits;
        } else {
            topUnits = this.topInUnits;
            bottomUnits = this.bottomInUnits;
        }

        // and to map units units
        var topMax = maxSizeData / inches[topUnits];
        var bottomMax = maxSizeData / inches[bottomUnits];

        // now trim this down to useful block length
        var topRounded = this.getBarLen(topMax);
        var bottomRounded = this.getBarLen(bottomMax);

        // and back to display units
        topMax = topRounded / inches[curMapUnits] * inches[topUnits];
        bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits];

        // and to pixel units
        var topPx = topMax / res / geodesicRatio;
        var bottomPx = bottomMax / res / geodesicRatio;

        // now set the pixel widths
        // and the values inside them

        this.bottomWidth = Math.round(bottomPx);
        this.bottomScale = bottomRounded + " " + bottomUnits;

        this.topWidth = Math.round(topPx);
        this.topScale = topRounded + " " + topUnits;
    },

    /**
    * Method: getGeodesicLength
    * 
    * Parameters:
    * pixels - {Number} the pixels to get the geodesic length in meters for.
    */
    getGeodesicLength: function (pixels) {
        var map = this.map;
        var centerPx = map.getPixelFromLonLat(map.getCenter());
        var bottom = map.getLonLatFromPixel(centerPx.add(0, -pixels / 2));
        var top = map.getLonLatFromPixel(centerPx.add(0, pixels / 2));
        var source = map.getProjectionObject();
        var dest = new OpenLayers.Projection("EPSG:4326");
        if (!source.equals(dest)) {
            bottom.transform(source, dest);
            top.transform(source, dest);
        }
        return OpenLayers.Util.distVincenty(bottom, top);
    },

    /** 
    * Method: getBarLen
    * Given a number, round it down to the nearest 1,2,5 times a power of 10.
    * That seems a fairly useful set of number groups to use.
    * 
    * Parameters:
    * maxLen - {float}  the number we're rounding down from
    * 
    * Returns:
    * {Float} the rounded number (less than or equal to maxLen)
    */
    getBarLen: function (maxLen) {
        // nearest power of 10 lower than maxLen
        var digits = parseInt(Math.log(maxLen) / Math.log(10));
        var pow10 = Math.pow(10, digits);

        // ok, find first character
        var firstChar = parseInt(maxLen / pow10);

        // right, put it into the correct bracket
        var barLen;
        if (firstChar > 5) {
            barLen = 5;
        } else if (firstChar > 2) {
            barLen = 2;
        } else {
            barLen = 1;
        }

        // scale it up the correct power of 10
        return barLen * pow10;
    }
});
