/**
 * @author Ryan Johnson <http://syntacticx.com/>
 * @copyright 2008 PersonalGrid Corporation <http://personalgrid.com/>
 * @package LivePipe UI
 * @license MIT
 * @url http://livepipe.net/control/rating
 * @require prototype.js, livepipe.js
 */

/*global document, Prototype, Ajax, Class, Event, $, $A, $F, $R, $break, Control */

if(typeof(Prototype) == "undefined") {
    throw "Control.Rating requires Prototype to be loaded."; }
if(typeof(Object.Event) == "undefined") {
    throw "Control.Rating requires Object.Event to be loaded."; }

Control.Rating = Class.create({
    initialize: function(container,options){
        Control.Rating.instances.push(this);
        this.value = false;
        this.links = [];
        this.container = $(container);
        this.container.update('');
        this.options = {
            min: 1,
            max: 5,
            rated: false,
            input: false,
            reverse: false,
            capture: true,
            multiple: false,
            classNames: {
                off: 'rating_off',
                half: 'rating_half',
                on: 'rating_on',
                selected: 'rating_selected'
            },
            updateUrl: false,
            updateParameterName: 'value',
            updateOptions : {},
            afterChange: Prototype.emptyFunction
        };
        Object.extend(this.options,options || {});
        if(this.options.value){
            this.value = this.options.value;
            delete this.options.value;
        }
        if(this.options.input){
            this.options.input = $(this.options.input);
            this.options.input.observe('change',function(input){
                this.setValueFromInput(input);
            }.bind(this,this.options.input));
            this.setValueFromInput(this.options.input,true);
        }
        var range = $R(this.options.min,this.options.max);
        (this.options.reverse ? $A(range).reverse() : range).each(function(i){
            var link = this.buildLink(i);
            this.container.appendChild(link);
            this.links.push(link);
        }.bind(this));
        this.setValue(this.value || this.options.min - 1,false,true);
    },
    buildLink: function(rating){
        var link = $(document.createElement('a'));
        link.value = rating;
        if(this.options.multiple || (!this.options.rated && !this.options.multiple)){
            link.href = '';
            link.onmouseover = this.mouseOver.bind(this,link);
            link.onmouseout = this.mouseOut.bind(this,link);
            link.onclick = this.click.bindAsEventListener(this,link);
        }else{
            link.style.cursor = 'default';
            link.observe('click',function(event){
                Event.stop(event);
                return false;
            }.bindAsEventListener(this));
        }
        link.addClassName(this.options.classNames.off);
        return link;
    },
    disable: function(){
        this.links.each(function(link){
            link.onmouseover = Prototype.emptyFunction;
            link.onmouseout = Prototype.emptyFunction;
            link.onclick = Prototype.emptyFunction;
            link.observe('click',function(event){
                Event.stop(event);
                return false;
            }.bindAsEventListener(this));
            link.style.cursor = 'default';
        }.bind(this));
    },
    setValueFromInput: function(input,prevent_callbacks){
        this.setValue($F(input),true,prevent_callbacks);
    },
    setValue: function(value,force_selected,prevent_callbacks){
        this.value = value;
        if(this.options.input){
            if(this.options.input.options){
                $A(this.options.input.options).each(function(option,i){
                    if(option.value == this.value){
                        this.options.input.options.selectedIndex = i;
                        throw $break;
                    }
                }.bind(this));
            }else {
                this.options.input.value = this.value; }
        }
        this.render(this.value,force_selected);
        if(!prevent_callbacks){
            if(this.options.updateUrl){
                var params = {}, a;
                params[this.options.updateParameterName] = this.value;
                a = new Ajax.Request(this.options.updateUrl, Object.extend(
                    this.options.updateOptions, { parameters : params }
                ));
            }
            this.notify('afterChange',this.value);
        }
    },
    render: function(rating,force_selected){
        (this.options.reverse ? this.links.reverse() : this.links).each(function(link,i){
            if(link.value <= Math.ceil(rating)){
                link.className = this.options.classNames[link.value <= rating ? 'on' : 'half'];
                if(this.options.rated || force_selected) {
                    link.addClassName(this.options.classNames.selected); }
            }else {
                link.className = this.options.classNames.off; }
        }.bind(this));
    },
    mouseOver: function(link){
        this.render(link.value,true);
    },
    mouseOut: function(link){
        this.render(this.value);
    },
    click: function(event,link){
        this.options.rated = true;
        this.setValue((link.value ? link.value : link),true);
        if(!this.options.multiple) {
            this.disable(); }
        if(this.options.capture){
            Event.stop(event);
            return false;
        }
    }
});
Object.extend(Control.Rating,{
    instances: [],
    findByElementId: function(id){
        return Control.Rating.instances.find(function(instance){
            return (instance.container.id && instance.container.id == id);
        });
    }
});
Object.Event.extend(Control.Rating);
