/**
 * 
 * @return
 */
function Mosaic() {
    this.modules = {};
    this.maxContent = 12;
    this.content = [];
    this.initialize();
}

/**
 * 
 * @return
 */
Mosaic.prototype.initialize = function() {
	for (var i = 0; i < this.maxContent; i++) {
		this.content.push(null);
	}
};

/**
 * Show module
 * 
 * @param module
 * @return this
 */
Mosaic.prototype.addModule = function(module) {
	var rule = module.getColor();
	
	if (!this.modules[rule]) {
		this.modules[rule] = [];
	}
	
	this.modules[rule].push(module);

	return this;
};

/**
 * Show module
 * 
 * @param rule
 * @param i
 * @param last
 * @return void
 */
Mosaic.prototype._showModule = function(rule, i, last) {
    var rand = Math.floor(Math.random() * 12);
    var module = this.modules[rule][rand];

    if ($.inArray(module, this.content) < 0) {
        this.content[i] = module;
        var element = $('#home ul#mosaic li').eq(i);

        var photo = new Image();

        photo.onload = function() {
            element
                .removeClass('green')
                .removeClass('orange')
                .removeClass('red')
                .addClass(rule)
                .prepend('<span class="container rounded" style="background: url(' + this.src + ') no-repeat center"><a class="rounded" href="' + module.getContent().link + '" rev="' + rule + '" rel="' + module.getContent().title + '"></a><span class="rounded">&nbsp;</span></span>');

            element
                .find('span.container')
                .fadeOut(0)
                .fadeIn('slow');
        };

        photo.src = module.getContent().photo;

    } else {
        this._showModule(rule, i);
    }
};

/**
 * Show modules by rule
 * 
 * @param rule
 * @param count
 * @return this
 */
Mosaic.prototype.showModules = function(rule, count) {
    var length = this.content.length;
    var show = 0;
    
    for (var i = 0; i < length; i++) {

        if (this.content[i] === null && show < count) {
            this._showModule(rule, i, (show + 1 == count));
            show++;
        }

    }

    return this;
};

/**
 * Hide module
 * 
 * @param position
 * @return this
 */
Mosaic.prototype._hideModule = function(position) {
    this.content[position] = null;
    var element = $('#home ul#mosaic li').eq(position);
    var link = element.find('span.container');

    link.fadeOut('fast', function() {
        $(this).remove();
    })
    
    return this;
};


/**
 * Hide modules by position or color
 * 
 * @param rule
 * @param count
 * @return this
 */
Mosaic.prototype.hideModules = function(rule, count) {
    if (isNaN(rule)) {
        var length = this.content.length;
        var removed = 0;

        for(var i = length - 1; i >= 0; i--) {
            var module = this.content[i];

            if (module instanceof Module && module.getColor() == rule) {
                if (count > 0 && removed < count) {
                    this._hideModule(i);
                    removed++;
                }
            }
        }
    } else {
        if (rule > this.maxContent - 1) {
            return;
        }

        this._removeModule(rule);
    }
};

/**
 * Return count of modules by color
 * 
 * @param rule
 * @return this
 */
Mosaic.prototype.getModuleCount = function(rule) {
    var length = this.content.length;
    var count = 0;
    
    for(var i = length - 1; i >= 0; i--) {
        var module = this.content[i];

        if (module instanceof Module && module.getColor() == rule) {
                count++;
        }
    }

    return count;
};

/**
 * Update modules by color
 * 
 * @param iterator
 * @return this
 */
Mosaic.prototype.update = function(iterator) {
    var _this = this;
    var order = [];
    var rules = {};

    $.each(iterator, function(rule, i) {
        rules[rule] = {
            actual: _this.getModuleCount(rule),
            value: i
        };

        if (rules[rule].actual > rules[rule].value) {
            _this.hideModules(rule, rules[rule].actual - rules[rule].value);
        } else if (rules[rule].actual < rules[rule].value) {
            order.push(rule);
        }
    });

    var length = order.length;
    for (var i = 0; i < length; i++) {
        var rule = order[i];
        this.showModules(rule, rules[rule].value - rules[rule].actual);
    }

    return this;
};

/**
 * Hide all modules
 * 
 * @return this
 */
Mosaic.prototype.clear = function() {
    for(var i = length - 1; i >= 0; i--) {
        if (this.content[i] instanceof Module) {
            this._removeModule(i);
        }
    }

    return this;
};

/**
 * Start modules
 * 
 * @return this
 */
Mosaic.prototype.start = function() {
    var count = 4;
    var colors = [{rule:'green', count:0},
                  {rule:'orange', count:0},
                  {rule:'red', count:0}];

    for (var i = 0; i < 12; i++) {
        this.showModules((function() {
            do {
                var valid = false;
                var color = Math.floor(Math.random() * 3);
                
                if (colors[color].count < count) {
                    colors[color].count++;
                    valid = true;
                }
            } while(!valid);

            return colors[color].rule;
        })(), 1);
    }
};

/**
 * Create modules
 *
 * @param iterator
 * @param rule
 * @return this
 */
Mosaic.prototype.creatModules = function(iterator, rule) {
    var _this = this;
    $.each(iterator, function(index, element) {
        var _module = new Module(element, rule);
        _this.addModule(_module);
    });
}

/**
 * Class module
 * 
 * @return this
 */
function Module(content, color) {
	this.content = null;
	this.color = null;
	
	this.initialize(content, color);
}

/**
 * Initialize module
 * 
 * @param content
 * @param color
 * @return void
 */
Module.prototype.initialize = function(content, color) {
	this.content = content;
	
	if (color) 
		this.setColor(color);
};

/**
 * Define the color of module
 * 
 * @param color
 * @return this
 */
Module.prototype.setColor = function(color) {
	this.color = color;
	return this;
};

/**
 * Return the color of module
 * 
 * @return string
 */
Module.prototype.getColor = function() {
	return this.color;
};

/**
 * Return content
 * 
 * @return object
 */
Module.prototype.getContent = function() {
	return this.content;
};


