ZoomBox = function (opts, callbacks) {
    this.opts = opts;
    this.opts.offset = {
        left:opts.offset.left || 0,
        top:opts.offset.top || 0
    }
    this.opts.size = this.opts.size || {};
    callbacks = callbacks || {};
    this.events = {};
    this.events.onstartzoom = callbacks.onstartzoom || this.defaultEvent;
    this.events.ondrag = callbacks.ondrag || this.defaultEvent;
    this.events.onendzoom = callbacks.onendzoom || this.defaultEvent;
    this.events.ontoggle = callbacks.ontoggle || this.defaultEvent;
};
ZoomBox.prototype = new GControl();

ZoomBox.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(this.opts.offset.left, this.opts.offset.top));
}

ZoomBox.prototype.defaultEvent = function () {return false}
ZoomBox.prototype.status = 'off';
ZoomBox.prototype.dragging = false;
ZoomBox.prototype.mousedown = null;
ZoomBox.prototype.mousemove = null;
ZoomBox.prototype.mouseup = null;
ZoomBox.prototype.buffer = { mousedown:null };
ZoomBox.prototype.overlay = document.createElement('div');
ZoomBox.prototype.box = document.createElement('div');
ZoomBox.prototype.borderBox = document.createElement('div');

ZoomBox.prototype.initialize = function (map) {
    this.map = map;
    this.renderParent = this.opts.renderParent || this.map.getContainer();

    var overlay = this.overlay;
    this.overlay.offset = {
        box:overlay,
        top:0,
        left:0,
        get:function () {
            this.top = 0;
            this.left = 0;
            var parent = this.box.offsetParent;
            while (parent != null) {
                this.top += parent.offsetTop;
                this.left += parent.offsetLeft;
                parent = parent.offsetParent;
            }
        }
    };
    this.overlay.styles = {
        position: 'absolute',
        width:this.opts.size.width || this.map.getSize().width-this.opts.offset.left+'px',
        height:this.opts.size.height || this.map.getSize().height-this.opts.offset.top+'px',
        background:'white',
        filter:'Alpha(opacity:1)',
        opacity:.01,
        zIndex:this.opts.zIndex || 150,
        cursor: this.opts.cursor || 'crosshair'
    };
    this.box.styles = {
        position:'absolute',
        width:'0px',
        height:'0px',
        fontSize:'0px',
        zIndex:this.opts.zIndex+1 || 151,
        background:this.opts.background || 'white',
        filter:'Alpha(opacity:30)',
        opacity:.3,
        cursor: this.opts.cursor || 'crosshair'
    }
    this.borderBox.styles = {
        position:'absolute',
        border: this.opts.border || '2px solid cyan',
        width:'0px',
        height:'0px',
        fontSize:'0px',
        zIndex:this.opts.zIndex+2 || 152,
        cursor: this.opts.cursor || 'crosshair'
    }
    this.borderBox.offset = {
        left:parseInt(this.borderBox.styles.border),
        top:parseInt(this.borderBox.styles.border)
    }
    this.overlay.id = this.opts.overlayId || 'zoomBoxOverlay';
    this.box.id = this.opts.boxId || 'zoomBox';
    this.borderBox.id = this.opts.zoomBorderBoxId || 'zoomBorderBox';
    this.attachStyles(this.overlay);
    this.attachStyles(this.box);
    this.attachStyles(this.borderBox);

    return this.overlay;
}

ZoomBox.prototype.toggle = function () {
    if (this.status == 'off') {
        this.renderParent.appendChild(this.overlay);
        this.overlay.offset.get();
        this.overlay.onselectstart = function () {return false;};

        this.mousedown = GEvent.bindDom(this.overlay, 'mousedown', this, function (e) {
            this.attachBox(e);
        });
        this.mousemove = GEvent.bindDom(document.body, 'mousemove', this, function (e) {
            this.dragBox(e);
        });
        this.mouseup = GEvent.bindDom(document.body, 'mouseup', this, function (e) {
            this.detach(e)
        });
        this.status = 'on';
    } else if (this.status == 'on') {
        this.status = 'off';
        GEvent.removeListener(this.mousedown);
        GEvent.removeListener(this.mousemove);
        GEvent.removeListener(this.mouseup);
        this.renderParent.removeChild(this.overlay);
    }
    this.events.ontoggle({on:this.status=='on'?true:false});
}

ZoomBox.prototype.getStatus = function () {
    return this.status;
}

ZoomBox.prototype.attachBox = function (e) {
    this.buffer.mousedown = {top:e.clientY-this.overlay.offset.top,left:e.clientX-this.overlay.offset.left};
    this.box.styles = {top : this.buffer.mousedown.top+'px',left : this.buffer.mousedown.left+'px',width : '0px',height : '0px'};
    this.attachStyles(this.box);

    this.borderBox.styles = {
        left : parseInt(this.box.styles.left)-this.borderBox.offset.left+'px',
        top : parseInt(this.box.styles.top)-this.borderBox.offset.top+'px',
        width : '0px', height : '0px'
    }
    this.attachStyles(this.borderBox);

    this.renderParent.appendChild(this.box);
    this.renderParent.appendChild(this.borderBox);
    this.dragging = true;
    this.events.onstartzoom(this.box.styles);
    return false;
}
ZoomBox.prototype.dragBox = function (e) {

    if (this.dragging) {
        var relMousePosLeft = e.clientX-this.overlay.offset.left;
        var relMousePosTop = e.clientY-this.overlay.offset.top;

        this.box.styles.left = (relMousePosLeft > this.buffer.mousedown.left ? this.buffer.mousedown.left : relMousePosLeft) + 'px';
        this.box.styles.top = (relMousePosTop > this.buffer.mousedown.top ? this.buffer.mousedown.top : relMousePosTop) + 'px';
        this.box.styles.width = this.borderBox.styles.width = (relMousePosLeft > this.buffer.mousedown.left ? relMousePosLeft-this.buffer.mousedown.left : this.buffer.mousedown.left-relMousePosLeft) + 'px';
        this.box.styles.height = this.borderBox.styles.height = (relMousePosTop > this.buffer.mousedown.top ? relMousePosTop-this.buffer.mousedown.top : this.buffer.mousedown.top-relMousePosTop) + 'px';

        this.borderBox.styles.left = parseInt(this.box.styles.left)-this.borderBox.offset.left+'px';
        this.borderBox.styles.top = parseInt(this.box.styles.top)-this.borderBox.offset.top+'px';

        if (parseInt(this.borderBox.styles.left)+parseInt(this.borderBox.styles.width)-this.opts.offset.left <
                parseInt(this.overlay.style.width) &&
            parseInt(this.borderBox.styles.top)+parseInt(this.borderBox.styles.height)-this.opts.offset.top <
                parseInt(this.overlay.style.height) &&
            parseInt(this.borderBox.styles.left) >= parseInt(this.overlay.style.left) &&
            parseInt(this.borderBox.styles.top) >= parseInt(this.overlay.style.top)) {

            this.attachStyles(this.borderBox);
            this.attachStyles(this.box);
        }
        this.events.ondrag(this.box.styles);
    }
    return false;
}
ZoomBox.prototype.detach = function (e) {
    var r = null;
    if (this.dragging == true){
        this.renderParent.removeChild(this.box);
        this.renderParent.removeChild(this.borderBox);
        var r = this.setZoom();
    }
    this.dragging = false;
    this.events.onendzoom(r);
}
ZoomBox.prototype.setZoom = function () {
    var p = {
        ne : new GPoint(parseInt(this.borderBox.styles.left),
                           parseInt(this.borderBox.styles.top)),
        nw : new GPoint(parseInt(this.borderBox.styles.left)+parseInt(this.borderBox.styles.width),
                       parseInt(this.borderBox.styles.top)),
        se : new GPoint(parseInt(this.borderBox.styles.left),
                           parseInt(this.borderBox.styles.top)+parseInt(this.borderBox.styles.height)),
        sw : new GPoint(parseInt(this.borderBox.styles.left)+parseInt(this.borderBox.styles.width),
                       parseInt(this.borderBox.styles.top)+parseInt(this.borderBox.styles.height))
    }
    var c = {
        ne : this.map.fromContainerPixelToLatLng(p.ne),
        nw : this.map.fromContainerPixelToLatLng(p.nw),
        se : this.map.fromContainerPixelToLatLng(p.se),
        sw : this.map.fromContainerPixelToLatLng(p.sw)
    };
    var z = new GPolyline([c.nw, c.ne, c.se, c.sw]);
    var n = z.getBounds();
    var zoomLevel = this.map.getBoundsZoomLevel(n);
    var center = n.getCenter();
    this.map.setCenter(center, zoomLevel);
    return {zoomLevel:zoomLevel,center:center,points:p,coordinates:c};
}
ZoomBox.prototype.attachStyles = function (element, styles) {
    styles = styles || element.styles;
    for (var i in styles) {
        element.style[i] = styles[i];
    }
}

