general.namespace('SW.Regions');


(function() {

    ////////// CLASS Region
    /*        
    */
    var Region = SW.Regions.Region = function(map, bounds, centerLat, centerLng, name, regionId, zoom, isNavRegion) {
        this.map = map;
        this.bounds = bounds;
        this.center = new GLatLng(centerLat, centerLng);
        this.zoom = zoom; // the zoom level at which the region takes up the full screen
        this.hidden = false;

        this.borderColor = 'red';
        this.borderEnabled = false;
        this.name = name;
        this.regionId = regionId;
        this.zoom = zoom;
        this.isNavRegion = isNavRegion;
        this.inZoom = false;

        this.SW = this.bounds.getSouthWest();
        this.NE = this.bounds.getNorthEast();

        // setup visibility control
        this.setupDivs();

        var currentZoom = this.map.getZoom();

        // initialize this.visible before calling this.updatePosition() for the first time
        if (this.inZoom === true && this.hidden === false) {
            this.initVisibility(true);
        } else {
            this.initVisibility(false);
        }

        this.handlersEnabled = false;

        this.setupHandlers();
    };
    Region.prototype.constructor = Region;


    /* */
    Region.prototype.toString = function() {
        return 'ClickableRegionDiv: ' + this.regionId + '; ' + this.name;
    };


    /* */
    Region.prototype.toString = function() {
        return 'RegionDiv: ' + this.bounds.toString();
    };


    /* Remove the rectangle div(s) from the map pane */
    Region.prototype.remove = function() {
        var parent = this.div.parentNode;

        parent.removeChild(this.div);
    };


    /* Update the size and position of the divs. Called upon moveend and movezoom, but not move. */
    Region.prototype.updatePosition = function() {

        // Calculate the DIV coordinates of two opposite corners of our bounds to
        // get the size and position of our rectangle

        var SWPoint = this.map.fromLatLngToDivPixel(this.SW);
        var NEPoint = this.map.fromLatLngToDivPixel(this.NE);

        // Now position our div based on the pixel coordinates of our bounds

        var width = NEPoint.x - SWPoint.x;

        // it may happen that the W and E are so close together that they have no pixel width at this zoom level;
        // in this case, need to look at actual W E to see which is on which side of the other
        if (width === 0) {
            var E = this.NE.lng();
            var W = this.SW.lng();
            if ((E > W) && ((E - W) < 350)) { // make sure width is not zero because the region is ~360 degrees wide
                width = 1; // region would be invisible, otherwise
            }
        }

        // when NEPoint.x - SWPoint.x is positive
        if (width <= 0) {
            var zoomFactor = Math.pow(2, this.map.getZoom() - 1); // every zoom lvl, the world doubles in each dimension in terms of pixels
            width = (SWMap.info.mapWidth * zoomFactor) - Math.abs(width); // when not overlayed, the left edge and right edge coords of the region are wrapped around the world and so width needs different derivation
        }

        var height = SWPoint.y - NEPoint.y;

        // if there's a border, need to shrink the div size in both dimensions by twice the border width
        if (this.borderEnabled) {
            width -= 2; // border width is 1
            height -= 2;
        }

        // make sure width and height are set to minimum of 1px wide and tall
        if (width <= 0) {
            width = 1;
        }
        if (height <= 0) {
            height = 1;
        }

        // set styles
        var top = NEPoint.y + "px";
        width = width + 'px';
        height = height + 'px';

        var divStyle = this.div.style;
        divStyle.width = width;
        divStyle.height = height;
        divStyle.left = SWPoint.x + "px";
        divStyle.top = top;

        var blankImgStyle = this.blankImg.style;
        blankImgStyle.width = width;
        blankImgStyle.height = height;
    };


    /* Hide the overlay. (Does nothing if overlay is already hidden.) */
    Region.prototype.hide = function() {
        if (this.hidden === false) {
            this.hidden = true;
            this.updateVisibility();
        }
    };


    /* Show the overlay. (Does nothing if overlay is already not hidden.) */
    Region.prototype.show = function() {
        if (this.hidden === true) {
            this.hidden = false; // do this before updateVisibility() so that the redraw makes the image appear
            this.updateVisibility();
        }
    };


    /* If hidden, show the overlay. If shown, hide the overlay. */
    Region.prototype.toggleHidden = function() {
        if (this.hidden === true) {
            this.show();
        } else {
            this.hide();
        }
    };


    /* When hidden status of overlay is changed, need to update the actual visibility of the 
    overlay */
    Region.prototype.updateVisibility = function() {

        var newVisibility = (this.inZoom === true && this.hidden === false);

        if (newVisibility === this.visible) { // nothing to change
            return;
        } else if (newVisibility === true) { // make visible
            this.div.style.display = 'block';
            this.visible = true;
        } else {         // make invisible
            this.div.style.display = 'none';
            this.visible = false;
        }
    };


    /* Force visibility or invisibility without regard to previous visibility status or inZoom, hidden, or inView */
    Region.prototype.initVisibility = function(newVisibility /* bool */) {

        if (newVisibility === true) {   // make visible
            this.div.style.display = 'block';
            this.updatePosition();
            this.visible = true;
        } else {           // make invisible
            this.div.style.display = 'none';
            this.visible = false;
        }
    };


    /* Creates the div that is the visible region. */
    Region.prototype.setupDivs = function() {

        this.div = SW.Regions.DIV.cloneNode(true);
        this.div.regionId = this.regionId; // so handlers can find the regionId having found the div
        this.blankImg = this.div.firstChild;

        // Our div is to be flat against the map, so we add it to the
        // MAP_PANE pane, which is at the same z-index as the map itself (i.e.,
        // below the marker shadows)

        SW.Regions.REGIONS_DOC_FRAG.appendChild(this.div);
    };

    /**/
    Region.prototype.setCursor = function(cursorStyle) {
        this.div.style.cursor = cursorStyle;
    };


    /* 
    setup handlers for border highlighting on mouseover and centerAndZoom on clicking (but not dragging!) 
    */
    Region.prototype.setupHandlers = function() {

        var me = this;
        var handler;
        var mouseX;
        var mouseY;

        function mouseClickDown(e) {
            // make note of position on down to see how much user drags before releasing mouse button

            mouseX = e.clientX;
            mouseY = e.clientY;
        }

        function mouseClick(e) {
            if (handler) {
                window.clearTimeout(handler);
                handler = null;
            } else {
                var upMouseX = e.clientX;
                var upMouseY = e.clientY;

                handler = setTimeout(

                        function() {
                            if (me.handlersEnabled) {
                                // only center and zoom on region if user doesn't move the mouse too much before releasing the button
                                if ((mouseX < upMouseX + 5 && mouseX > upMouseX - 5) && (mouseY < upMouseY + 5 && mouseY > upMouseY - 5)) {
                                    me.centerAndZoomOnMe();
                                }
                            }

                            handler = null;
                        },
                        0
                    );
            }
        }

        var addListener = general.addListener;
        addListener(this.div, 'mousedown', mouseClickDown);
        addListener(this.div, 'click', mouseClick);
        addListener(this.div, 'mouseover', function() { me.showBorder(); });
        addListener(this.div, 'mouseout', function() { me.hideBorder(); });

        this.handlersEnabled = true;
    };


    /* called on mouseout */
    Region.prototype.hideBorder = function() {
        if (this.handlersEnabled && SW.Regions.hoveredRegionChangeable) {
            this.div.style.borderWidth = '0px';
            SWMap.tooltip.hide();
        }
    };


    /* called on mouseover*/
    Region.prototype.showBorder = function() {
        if (this.handlersEnabled && SW.Regions.hoveredRegionChangeable) {

            // make sure last highlighted region gets cleared (in a few edge cases, its mouseout doesn't get called, such as when you have it highlighted as drag you drag cursor off map)
            if (SW.Regions.lastHighlightedRegion) {
                SW.Regions.lastHighlightedRegion.hideBorder();
            }
            SW.Regions.lastHighlightedRegion = this;

            this.div.style.borderWidth = '1px';

            SWMap.tooltip.setMessage(this.name);
            SWMap.tooltip.show();
        }
    };


    /* for when region is on the map, visible at current zoom, but shouldn't be highlighteable (this is when map is at same zoom lvl as the region) */
    Region.prototype.disableBorder = function() {
        this.div.style.borderWidth = '0px'; // hide border        
        this.handlersEnabled = false; // prevents mouseover from highlighting the region (this is a simpler solution than adding and removing a bunch of handlers)
        this.borderEnabled = false;
    };


    /* */
    Region.prototype.enableBorder = function() {
        this.handlersEnabled = true; // allows mouseover to highlight the region
        this.borderEnabled = true;
    };


    /* get array of sibling regions (does not include this region); returns empty array if no siblings */
    Region.prototype.getSiblings = function() {
        if (this.parent) {
            var children = this.parent.children.slice(0); //make local copy

            for (var i = 0; i < children.length; i++) {
                if (this === children[i]) {
                    children.splice(i, 1);
                    break;
                }
            }

            return children;
        } else {
            return [];
        }
    };


    Region.prototype.centerAndZoomOnMe = function() {
        mapGeneral.centerAndZoomOnRegionAnimation(this);
    };

    ////////// END CLASS Region

})();
