var Graph = new Class({
    'mapScale': 0.05,
    'isPanning': false,
    'field': null,
    'options': null,
    'throwMultiplier': 2,
    
    'initialize': function(options) {
        this.id = options.id;
        this.options = options;
        this.mask = $(this.id);
        this.map = $(this.id + '-field').setOpacity(0);
        this.field = $(this.id + '-field').setOpacity(0);

        if (!this.map) {
            return false;
        }

        if (!options.mapimg) {
            return false;
        }

        this.img = new Element('img');
        this.img.injectInside(this.field)
                .addEvent('load', this.loadComplete.bind(this))
                .setStyle('opacity', 0)
                .setProperty('src', options.lgimg);
    },
    
    'loadComplete': function() {
        this.map.setStyles({width: this.img.width + 'px', 
                            height: this.img.height+ 'px',
                            backgroundImage: 'url(' + this.img.src + ')'});
        this.img.remove();
        
        var map_w = this.map.getCoordinates().width;
        var map_h = this.map.getCoordinates().height;
        
        var mask_w = this.mask.getCoordinates().width;
        var mask_h = this.mask.getCoordinates().height;

        this.xScale = (map_w > mask_w) ? mask_w / map_w : 1;
        this.yScale = (map_h > mask_h) ? mask_h / map_h : 1;

        var drag = new Drag.Move(this.id + '-field', { 'limit': {x: [mask_w - map_w,0], y: [mask_h - map_h,0]}, 'handle': this.id });
        drag.addEvent('onStart', this.cursorHandClose.bind(this));
        drag.addEvent('onDrag', this.dragHandler.bind(this));
        drag.addEvent('onComplete', this.cursorHandOpen.bind(this));
        drag.addEvent('onComplete', this.throwStart.bind(this));
        this.panDrag = drag;
        document.addEvent('mousemove', this.throwObserver.bind(this));

        this.gotoFx = new Fx.Styles(this.field, {
            'duration': 1000,
            'wait': false,
            'transition': Fx.Transitions.Quart.easeOut
        });

        this.throwFx = new Fx.Styles($(this.field), {
            'duration': 500,
            'wait': false,
            'transition': Fx.Transitions.Circ.easeOut
        });

        this.cursorHandOpen();
        this.fixLinks();
        this.mapInitialize();
        this.gotoFx.set({
            'left': 0,
            'top': 0
        });
        this.map.effect('opacity').start(1);
    },

    'dragHandler': function() {
        $(this.id).addClass('dragging');
        this.isPanning = true;
    },

    'fixLinks': function() {
        $$('a').each(function(link) {
            link.addEvent('mouseup', this.checkIfPanning.bind(this));
        }.bind(this));
    },

    'checkIfPanning': function(e) {
        var event = new Event(e);
        var target = event.target;
        if (this.isPanning) {
            var clickHandler = function(event) {
                var event = new Event(event);
                event.preventDefault();
                target.removeEvent('click', clickHandler);
            };
            target.addEvent('click', clickHandler);
        }
    },

    'goto': function(x, y) {
        this.gotoFx.start({
            'left': x,
            'top': y
        });
    },
    
    'gotoMap': function(x, y) {
        this.gotoMapFx.start({
            'left': Math.floor(x / this.mapRatio.xScale),
            'top': Math.floor(y / this.mapRatio.yScale)
        });
    },
    
    'cursorHandOpen': function() {
        $(this.id).removeClass('dragging');
        if (window.gecko) {
            $(this.id).setStyle('cursor', '-moz-grab');
        } else if (window.webkit) {
            $(this.id).setStyle('cursor', 'auto');
        } else {
            $(this.id).setStyle('cursor', 'url(images/hand-open.cur)');
        }
        this.isPanning = false;
    },
    
    'cursorHandClose': function() {
        if (window.gecko) {
            $(this.id).setStyle('cursor', '-moz-grabbing');
        } else if (window.webkit) {
            $(this.id).setStyle('cursor', 'move');
        } else {
            $(this.id).setStyle('cursor', 'url(images/hand-closed.cur)');
        }
    },
    
    'throwObserver': function(e) {
        var event = new Event(e);
        if (this.isThrown) {
            var dx = this.mask.getLeft() - this.field.getLeft();
            var dy = this.mask.getTop() - this.field.getTop();
            var pos = $(this.id + '-field').getPosition();
            var throwPos = {
                'x': pos.x + dx * this.throwMultiplier,
                'y': pos.y + dy * this.throwMultiplier
            };
            this.gotoMap(dx, dy);
            this.isThrown = false;
        } else {
            this.throwOrigin = {
                'x': event.page.x,
                'y': event.page.y
            };
        }
    },
    
    'throwStart': function() {
        this.isThrown = true;
    },
    
    'mapInitialize': function() {

        var map = $(this.id + '-map');
        var field = $(this.id + '-field');
        
        map.setStyle('background-image', 'url(' + this.options.mapimg + ')');
        map.setStyle('height', (((map.getCoordinates().width / field.getCoordinates().width) * field.getCoordinates().height) + 'px'));
        
        var xScale = field.getCoordinates().width / map.getCoordinates().width;
        var yScale = field.getCoordinates().height / map.getCoordinates().height;
        
        this.mapRatio = {
            'xScale': xScale,
            'yScale': yScale,
            'width': Math.ceil(map.getCoordinates().width * this.xScale),
            'height': Math.ceil(map.getCoordinates().height * this.yScale)
        };

        var mapView = new Element('div', {
            'class': 'graph-map-view',
            'styles': {
                'position': 'absolute',
                'width': this.mapRatio.width,
                'height': this.mapRatio.height,
                'left': 0,
                'top': 0
            }
        });

        map.setOpacity(1);
        mapView.setOpacity(0.5);
        mapView.injectInside(map);

        var drag = new Drag.Move(mapView, { 'handle': map, container: map });

        drag.addEvent('onComplete', function() {
            var x = mapView.getLeft() - map.getLeft();
            var y = mapView.getTop() - map.getTop();
            this.goto(Math.floor(x * -xScale), Math.floor(y * -yScale));
        }.bind(this));

        this.gotoMapFx = new Fx.Styles(mapView, {
            'duration': 1000,
            'wait': false,
            'transition': Fx.Transitions.Quart.easeOut
        });

    }
});
