/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
TagCloud = Class.create();

Object.extend( TagCloud.prototype, {
    initialize: function(target, args) {
        this.target = target;
        this.args = args;
        this.tag_type = args.tag_type || 'span';

        // extract the font size and it's units
        this.args.size.match(/^(\d+)\s*(\S+)\s*$/);
        this.font_size = RegExp.$1;
        this.font_unit = RegExp.$2;

        // do the rendering
        this.render();
    },
    render : function() {
        var data = this.args.data;
        var target = $(this.target);
        var tags = {};

        // get the total and average value
        var total = 0;
        var count = 0;
        for(var key in data) {
            total += (data[key] - 0);
            count++;
        }
        var avg = count ? total / count : 0;
        this.avg_font_size = avg;

        // now create a <span> for each word
        for(var key in data) {
            // calculate the font size
            var size  = this._get_font_size(data[key]);
            //this.tag_type refers to tag type in arguments
            var tag = Builder.node("a", { style: "font-weight:normal;color:#1073a5;text-decoration:none"}, ' ' + key);
            tag.style.fontSize = size + this.font_unit;
            tag.style.opacity = this._get_opacity(data[key]);
            tag.href="Search.do?reset=true&searchString="+key;
            target.appendChild(tag);
            tags[key] = { tag: tag, current_size: size };
        }

        // save the tags for possible later uses
        this.tags = tags;
    },
    _get_font_size : function(val) {
        return Math.round((this.font_size * (val / this.avg_font_size)) * 1000) / 1000;
    },
    _get_opacity : function(val) {
        var opacity = Math.round( (val/this.avg_font_size) * 100) / 100;
        if( opacity > 1 ) opacity = 1;
        return opacity;
    },
    animate : function(args) {
        // create a new queued effect so we can call animate()
        // multiple times and they will be executed in order
        new TagCloud.AnimateEffect( 
            this, 
            {
                data     : args.data,
                duration : args.duration
            }
        );
    }
} );

TagCloud.AnimateEffect = Class.create();
Object.extend(Object.extend(TagCloud.AnimateEffect.prototype, Effect.Base.prototype), {
    initialize: function(tagcloud, args) {
        var duration = args.duration || 5;
        var new_data = args.data;

        // start the effect
        this.start({
            transition : Effect.Transitions.linear,
            duration   : duration,
            queue      : {
                position : 'end',
                scope    : 'TagCloud-' + tagcloud.target
            },
            afterSetup : function() {
                this.tagcloud = tagcloud;

                // get the final value for each and update the resulting avg_font_size
                this.final_values = {};
                this.start_values = {};
                for(var key in new_data) {
                    // TODO - create a tag if it doesn't already exist
                    this.final_values[key] = tagcloud._get_font_size(new_data[key]);
                    this.start_values[key] = tagcloud.tags[key].current_size;
                }
            }.bind(this),
            afterFinish : function() {
                // update each tag to have the right current_size
                var tags = this.tagcloud.tags;
                var final_values = this.final_values;
                for(var key in tags) {
                    tags[key].current_size = final_values[key];
                }
            }.bind(this)
        });
    },
    // called the Scriptaculous effect's engine
    update: function(pos) {
        // loop through each final_value and update that span
        var cloud           = this.tagcloud;
        var tags            = cloud.tags;
        var unit            = cloud.font_unit;
        var avg             = cloud.avg_font_size;
        var start_values    = this.start_values;

        for(var key in this.final_values) {
            var start = start_values[key];
            var new_size = (((this.final_values[key] - start) * pos) + start);
            new_size = Math.round((new_size * 1000) / 1000);
            var tag = tags[key].tag;
            tag.style.fontSize = new_size + unit;
            var opacity = Math.round( (new_size/avg) * 100) / 100;
            if( opacity > 1 ) opacity = 1;
            tag.style.opacity = opacity;
        }
    }
});

