LBP.Scroller = new Class({
    Implements: [Events, Options],
    options: {
        containerId: '',
        contentId: '',
        cssPrefix: 'scroll',
        pkg: 'pkg',
        mode: 'auto',  // horizontal | vertical | auto
        position: 'after',   //  before | after (content)
        wheel: true,    //enable mouse wheel scrolling
        wheelSteps: 40,
        btnSteps: 10,
        backForward: true,    //true: scroll content backward/forward
        prevNext: false, //true: scroll content a page forward/backward
        knobMinSize: { x: 22, y: 22}, //minimum width/height for the knob
        btnSize: { x:22, y:22},
        bar: { top: 9, btm: 12},
        btnMargin: 1,
        cntrMargin: 3,
        noOverflow: false,  // true: when there's no overflow, the background is still showing
        idx: 200,
        fade: true,
        opacity: 0.6,
        period: 50,
        cntrSize: { x: 365, y:429},
        onScroll: $empty,
        onPagePrev: $empty,
        onPageNext: $empty,
        onUpdate: $empty
    },
    initialize: function(options) {
        this.setOptions(options);
        if(!$(this.options.containerId) || !$(this.options.contentId)) return;
        this.container = $(this.options.containerId);
        this.content = $(this.options.contentId);
        if(this.container.retrieve('hasScrollBar')) return;
        this.container.store('hasScrollBar', true);
        this.bound = {
            'wheel' : this.wheel.bind(this),
            'update': this.update.bind(this),
            'resize': this.reposition.bind(this),
            'start': this.start.bind(this),
            'end': this.end.bind(this),
            'drag': this.drag.bind(this)
        };
        this.knobPos = { start: 0, end: 0, current: 0};
        
        this.generate();        
    },
    //call every time there's content change
    update: function() {
        this.cntrSize = this.container.getSize();
        this.cntrScrollSize = this.container.getScrollSize();
        //this.content.setStyle('height', this.cntrSize.y);
        if(this.direction) {
            this.resizeKnob('x');
            this.knob.set('styles', { width: this.knobSize+'px'});
        } else {
            this.resizeKnob('y');
            this.knob.set('styles', { height: this.knobSize+'px'});
            this.knobPad1.set('styles', { height: this.knobSize-this.options.bar.btm+'px'});
        } 
        
    },
    resizeKnob: function(dir) {
        this.cntrRatio = this.cntrSize[dir]/this.cntrScrollSize[dir];
        this.knobSize = (this.trackSize[dir]*this.cntrRatio).limit(this.options.knobMinSize[dir],this.trackSize[dir]);
        if(this.options.noOverflow && (this.knobSize == this.trackSize[dir]) ) {
            this.deactivate();
        } else {
            this.activate();
        }
        this.scrollRatio = this.cntrScrollSize[dir]/this.trackSize[dir];      
    },
    scroll: function(steps) {
        if(this.direction) {
            this.container.scrollLeft += steps;
            this.position.current = (this.container.scrollLeft/this.scrollRatio).limit(0, (this.trackSize.x - this.knobSize));
            this.knob.set('styles', { 'left': this.position.current});
        } else {
            this.container.scrollTop += steps;
            this.position.current = (this.container.scrollTop/this.scrollRatio).limit(0, (this.trackSize.y - this.knobSize));
            this.knob.set('styles', { 'top': this.position.current});
        }
        this.content.fireEvent('onScroll', steps);
    },
    clickTrack: function(event) {
        var axis = (this.direction)? 'x' : 'y';
        var position = this.track.getPosition();
        event.stop();
    },
    reposition: function() {
        var cntrPos = this.container.getPosition();
        dir1 = (this.direction)? 'y': 'x';
        if(this.options.position == 'after') {
            cntrPos[dir1] += (this.cntrSize[dir1]-this.options.btnSize[dir1]-this.options.cntrMargin);
        }
        this.scroller.set('styles', { top: cntrPos.y + 'px', left: cntrPos.x+'px'});
    },
    generate: function(){
        this.position = {};
        this.mouse = {};
        var mode = this.options.mode;
        this.cntSize = this.content.getSize();
        this.cntrSize = this.container.getSize();
        if(this.cntrSize.x == 0) {
            this.cntrSize = this.options.cntrSize;
        }
        this.content.store('this.cntSize', this.cntSize);
        this.content.store('this.cntrSize', this.cntrSize);
        
        this.container.set('styles', { overflow: 'hidden'});
        if(this.options.mode == 'auto') {
            if( this.cntrSize.y < this.cntSize.y) {
                this.direction = false;
            } else if( this.cntrSize.x < this.cntSize.x) {
                this.direction = true;
            }
        } else {
            this.direction = (this.options.mode == 'horizontal')? true: false;
        }
        this.build(); 
        this.container.addEvents({
            'mouseenter': function(event) {
                event.stopPropagation();
                this.toggleWheel(true);
            }.bind(this),
            'mouseleave': function(event) {
                event.stopPropagation();
                this.toggleWheel(false);
            }.bind(this)
        });
        this.content.addEvent('onReLoad', this.bound.update);
        window.addEvent('resize', this.bound.resize);
        this.update();
    },
    forwardScroll: function (steps) {
        steps = steps || this.options.btnSteps;
        this.scroll(steps);
    },
    backwardScroll: function (steps) {
        steps = steps || this.options.btnSteps;
        this.scroll(-steps);
    },
    prevScroll: function() {
        this.page();
    },
    nextScroll: function() {
        this.page();
    },
    page: function() {
        /*var axis = this.direction?'x':'y';
        var forward = (event.page[axis] > this.knob.getPosition()[axis]);
        this.scroll((forward?1:-1)*this.content['offset'+(this.direction?'Width':'Height')]);
        //this.updateThumbFromContentScroll();
        event.stop();*/
    },
    start: function(event){
        var axis = this.direction?'x':'y';
        this.mouse.start = event.page[axis];
        this.position.start = this.knob.getStyle(this.direction?'left':'top').toInt();
        this.toggleDocument('addEvent');
        event.stop();
    },
    toggleDocument: function(state) {
        document[state]('mousemove', this.bound.drag);
        document[state]('mouseup', this.bound.end);
        this.knob[state]('mouseup', this.bound.end);
    },
    end: function(event){
        this.toggleDocument('removeEvent');
        event.stop();
    },
    drag: function(event){
        var axis = this.direction?'y':'x';
        this.mouse.now = event.page[axis];
        this.position.current = (this.position.start + (this.mouse.now - this.mouse.start)).limit(0, (this.trackSize[axis] - this.knobSize));
        this.updateContent();
        this.updateKnob();
        event.stop();
    },
    updateContent: function() {
    },
    updateKnob:function () {
    },
    wheel: function(event) {
        if(this.hidden) return;
        this.scroll(- (event.wheel * this.options.wheelSteps));
        event.stop();
    },
    toggleWheel: function(state) {
        var fn = (state) ? 'addEvent' : 'removeEvent';
        this.content[fn]('mousewheel', this.bound.wheel);
    },
    build: function() {
        var key = (this.direction)? 'hor' : 'ver';
        this.scroller = new Element("div", { 'id': this.options.containerId+''+this.options.cssPrefix+'-'+key, 'class': this.options.cssPrefix+'-cntr-'+key});
        this.track = new Element("div", { 'class': this.options.cssPrefix+'-track' });
        this.knob = new Element("div", { 'class': this.options.cssPrefix+'-knob'});
        this.knobPad1 = new Element("div",{ 'class': this.options.cssPrefix+'-knob-pad1'});
        this.knobPad1.adopt(new Element("div",{ 'class': this.options.cssPrefix+'-knob-pad2'}));
        this.knob.adopt(this.knobPad1);
        this.track.adopt(this.knob);
        this.scroller.adopt(this.track);
        var clearScroll = function() {  $clear(this.scrolling); }.bind(this);
        this.scroller.addEvent('mouseleave', function(event){ $clear(this.scrolling); this.scroller.getElements('div.focus').each(function( obj){ obj.removeClass('focus'); });}.bind(this));
        var btnPerSide = 0;
        if(this.options.backForward) {
            this.backward = new Element("div", { 'class': this.options.cssPrefix+'-btn-backward'});
            this.forward = new Element("div", { 'class': this.options.cssPrefix+'-btn-forward'});
            this.scroller.adopt(this.forward);
            this.backward.inject(this.scroller,'top');
            btnPerSide = 1;
            this.addBtn(this.backward, 'backwardScroll', clearScroll);
            this.addBtn(this.forward, 'forwardScroll', clearScroll);
        }
        if(this.options.prevNext) {
            this.prev = new Element("div", { 'class': this.options.cssPrefix+'-btn-prev'});
            this.next = new Element("div", { 'class': this.options.cssPrefix+'-btn-next'});
            this.scroller.adopt(this.next);
            this.prev.inject(this.scroller,'top');
            btnPerSide += 1;
            this.addPreNextEvent(this.prev, 'prevScroll');
            this.addPreNextEvent(this.next, 'nextScroll');
        }
        this.track.addEvent('click', function(event){ this.clickTrack(event); }.bind(this));
        this.knob.addEvents({
            'mouseenter': function() { this.knob.addClass('hover'); }.bind( this ),
            'mouseleave': function() { if(this.knob.hasClass('hover')){ this.knob.removeClass('hover'); } }.bind(this),
            'mousedown': function(event){ this.knob.addClass('focus'); }.bind(this),
            'mouseup': function(event){ if(this.knob.hasClass('focus')) this.knob.removeClass('focus');}.bind(this),
            'mousemove': function(event){ this.drag(event);}.bind(this)
        });
        var scrollerStyle = { 'display': 'block', 'width': this.cntrSize.x+'px', 'height': this.cntrSize.y+'px', position: 'absolute', zIndex: this.options.idx };
        if(this.direction) {
            var pos = this.compute('y', 'x', btnPerSide);
            scrollerStyle.height = this.options.btnSize.y;
            
        } else {
            var pos = this.compute('x', 'y',btnPerSide);
            scrollerStyle.width = this.options.btnSize.x;
        }
        scrollerStyle = $merge(scrollerStyle, { top: pos.y+'px', left: pos.x+'px', zIndex: this.options.idx });
        this.track.set('styles', { width: this.trackSize.x+'px', height: this.trackSize.y+'px'});
        this.knob.set('styles', {position: 'relative', zIndex: this.options.idx+2});
        this.scroller.set('styles', scrollerStyle);
        if(this.options.fade) {
            this.scroller.addEvents ({
                'mouseenter': function(event) { this.scroller.tween('opacity', [this.options.opacity,1]); }.bind(this),
                'mouseleave': function(event) { this.scroller.tween('opacity', [1, this.options.opacity]); }.bind(this)
            });
        }
    },
    addBtn: function(btn, fn,clearScroll) {
        btn.addEvents({
            'mouseup' : function(event){ clearScroll.bind(this); if(btn.hasClass('focus')) {btn.removeClass('focus');} },
            'click': clearScroll.bind(this),
            'mousedown': function(event) {  btn.addClass('focus'); this.scrolling = this[fn].periodical(this.options.period, this); }.bind(this),
            'mouseenter': function(event) { btn.addClass('hover'); }.bind( this ),
            'mouseleave': function(event) { 
                if(btn.hasClass('hover')){ btn.removeClass('hover'); } 
                if(btn.hasClass('focus')){ btn.removeClass('focus'); } 
                $clear(this.scrolling); 
                }.bind( this )
        });
    },
    addPreNextEvent: function(btn, fn) {
        btn.addEvents({
            'mouseup' : function(){ if(btn.hasClass('focus')) { btn.removeClass('focus'); } },
            'mousedown': function() { btn.addClass('focus'); this[fn].call();}.bind(this),
            'mouseenter': function() { btn.addClass('hover'); }.bind( this ),
            'mouseleave': function() { if(btn.hasClass('hover')){ btn.removeClass('hover'); } if(btn.hasClass('focus')){ btn.removeClass('focus'); }  $clear(this.scrolling); }.bind( this )
        });
    },
    compute: function(dir1, dir2, btnPerSide) {
        this.trackSize = { x: 0, y: 0};
        this.trackSize[dir1] = this.options.btnSize[dir1]
        this.trackSize[dir2] = this.cntrSize[dir2] - 2*btnPerSide*(this.options.btnSize[dir2]+this.options.btnMargin);
        var cntrPos = this.container.getPosition();
        this.container.adopt(this.scroller);
        if(this.options.position == 'after') {
            cntrPos[dir1] += (this.cntrSize[dir1]-this.options.btnSize[dir1]-this.options.cntrMargin);
        } else {
            //this.scroller.inject(this.container,'top');
        } 
        
        return cntrPos;
    },
    deactivate: function() {
        this.scroller.addClass('inactive');
        var deactivate = { display: 'none', opacity: 0, visibility: 'hidden'};
        this.toggleActivate(deactivate);
        this.hidden = true;
    },
    activate: function() {
        if(this.scroller.hasClass('inactive')) this.scroller.removeClass('inactive');
        var deactivate = { display: 'block', opacity: 1, visibility: 'visible'};
        this.toggleActivate(deactivate);
        this.hidden = false;
    },
    toggleActivate: function(deactivate) {
        this.knob.set('styles', deactivate);
        this.track.set('styles', deactivate);
        if(this.options.prevNext) {
            this.prev.set('styles', deactivate);
            this.next.set('styles', deactivate);
        }
        if(this.options.backForward) {
            this.backward.set('styles', deactivate);
            this.forward.set('styles', deactivate);
        }
    },
    hideScroller: function() {
        this.scroller.set('styles', { display:'none'});
    }
});