var smsPublication = function() {
    var Config = {
        view : {
            messages : {
                date_format   : "%d-%m-%Y %H:%M:%S",
                scroll_delay  : 30,
                scroll_amount : 1,
                refresh_interval : 60000,
                classes : {
	                container : "sms-messages-container",
	                message   : "sms-message",
	                channel   : "sms-message-channel-",
	                type      : "sms-message-type-",
	                time      : "sms-message-time",
	                text      : "sms-message-text"
                }
            },
            messages_horizontal : {
                date_format   : "%d-%m-%Y %H:%M:%S",
                scroll_delay  : 30,
                scroll_amount : 1,
                refresh_interval : 60000,
                classes : {
	                container : "sms-messages-container",
	                message   : "sms-message",
	                channel   : "sms-message-channel-",
	                type      : "sms-message-type-",
	                time      : "sms-message-time",
	                text      : "sms-message-text",
                    separator : "sms-messages-separator"
                },
                separator     : '::'
            },
            poll : {
                refresh_interval : 60000,
                classes : {
                    title           : "sms-poll-title",
                    option          : "sms-poll-option",
	                option_num      : "sms-poll-option-num",
	                text            : "sms-poll-option-text",
	                voices          : "sms-poll-option-voices",
	                voices_percents : "sms-poll-option-voices-percents",
	                bar             : "sms-poll-option-bar",
	                bar_stripe      : "sms-poll-option-bar-stripe"
                }
            }
        },
        request : {
            url     : 'http://iawp.portalsoft.ru/export.wp?k=',
            timeout : 120000 
        },
        init: {
            id_prefix                : 'sms_',
	        publish_class            : 'sms-publish',
	        view_model_class_prefix  : 'sms-view-',
	        view_model_object_prefix : 'view',
	        request_id_regexp        : '[A-z0-9_-]+',
	        view_model_regexp        : '[A-z]+'
        }
    };

    /* class extends framework */

    Function.prototype.$extends = function ($super) { 
        this.$super = $super;
        return this;
    }
    Function.prototype.$class = function ($class) {
        if ($class == null) {
            $class = function () {};
        }
        this.$class = $class;
        if (this.$super != null) this.$class.prototype = this.$super.prototype;
        this.prototype = new $class(this.$super!=null ? this.$super.prototype : null);
        return this;
    }

    /* abstractData */

    var abstractData = function(){}.$class(function() {
        this.p_type = null;
        this.version = null;
        this.p_data = null;
        
        this.checkVersion = function(data) {
        	return data.version > this.version;
        }
        
        this.checkData = function(data) {
        	return  !((typeof (data) !== "object") || !('p_type' in data) || !('version' in data) || !('p_data' in data)); 
        }
        
        this.setData = function(data) {
        	if (!this.checkData(data) || !this.checkVersion(data)) return false;
            this.p_type = data.p_type;
        	this.version = data.version;
        	this.p_data = data.p_data;
        	return true;	
        }  
    });

    /* messages data */

    var messagesData  = function(){}.$extends(abstractData).$class(function($super) {
	    this.checkData = function(data) {
		    if (!$super.checkData.call(this, data) || (data.p_type != 'messages') || !(data.p_data instanceof Array)) return false;
		    var msg;
		    for (var i=0; i<data.p_data.length; i++) {
			    msg = data.p_data[i];
			    if ((typeof (msg) !== "object") || !('message' in msg) || !('message_time' in msg)) return false;
		    }
		    return true;
	    }
    });

    /* poll data */

    var pollData  = function(){}.$extends(abstractData).$class(function($super) {
	    this.checkData = function(data) {
		    if (!$super.checkData.call(this, data) || (data.p_type != 'polls') || (typeof(data.p_data) !== "object")) return false;
		    if (!('title' in data.p_data) || !('options' in data.p_data)) return false;
		
		    data.p_data.total_voices = 0;
		    data.p_data.max_voices = 0;
		    var option;
		    for (var i=0; i<data.p_data.options.length; i++) {
			    option = data.p_data.options[i];
                data.p_data.options[i].title = data.p_data.options[i].title || '';
                data.p_data.options[i].voices = data.p_data.options[i].voices || 0;
			    if ((typeof (option) !== "object") || !('option' in option)) return false;
			    data.p_data.max_voices = Math.max(data.p_data.max_voices, option.voices);
			    data.p_data.total_voices += parseInt(option.voices);
		    }
		    for (var i=0; i<data.p_data.options.length; i++) {
			    data.p_data.options[i].percents = data.p_data.options[i].voices * 100 / data.p_data.total_voices;
			    data.p_data.options[i].percents_from_max = data.p_data.options[i].voices * 100 / data.p_data.max_voices;
		    }
		
		    return true;
	    }
    });

    /* view abstract */ 

    var viewAbstract = function(request_id, dom, params) {
	    this.dom = {self: dom};
	    this.request_id = request_id;
        this.params = params;
    }.$class(function() {
        
        this.renderInit = function() {};
        this.renderData = function() {};
        
        this.requestSuccess = function(received_data) {
        	if (this.data.setData(received_data)) {
        		this.renderData();
        		return true;
        	}
        	return false;
        }
        
        this.requestData = function() {
        	requestData(this.request_id, createMethodReference(this,'requestSuccess'));
        }
        
        this.init = function() {
        	this.renderInit(); 
        	this.requestData();
        	this.refresh = window.setInterval(createMethodReference(this,'requestData'), this.params.refresh_interval);
        }
    });

    /*  sms view messages */

    this.viewMessages = function(request_id, dom){
	    arguments.callee.$super.call(this, request_id, dom, Config.view.messages);
	    this.data = new messagesData();
    }.$extends(viewAbstract).$class( function ($super) {
        this.need_render = null;
	    this.prev_scroll = 0;
        this.scroll_timer = null;
        
	    this.renderInit = function(){
            this.dom.self.innerHTML = '<div class="' + this.params.classes.container + '"></div>';
		    this.dom.view = this.dom.self.firstChild;
	    }

        this.renderMessages = function() {
        	var height = getElementHeight(this.dom.view.parentNode);
		    var view = '<div style="height:' + height + 'px">&nbsp;</div>';
		    for (var i=0; i < this.data.p_data.length; i++) {
			    var msg = this.data.p_data[i];
			    var channel_class = 'channel' in msg ? " " + this.params.classes.channel + msg.channel.toString() : "";
			    var type_class = 'type' in msg ? " " + this.params.classes.type + msg.type.toString() : "";
			    view += '<div class="' + this.params.classes.message + channel_class + type_class + '">' + 
			        '<div class="' + this.params.classes.time + '">' +	formatDate(msg.message_time + "000", this.params.date_format) + '</div>' + 
			        '<div class="' + this.params.classes.text + '">' + msg.message + '</div>' +
			        '</div>';
		    }
            view += '<div style="height:' + height + 'px">&nbsp;</div>';

		    emptyNode(this.dom.view);
		    this.dom.view.innerHTML = view;
            this.dom.view.scrollTop = 0;
            this.need_render = false;
            if (this.scroll_timer) window.clearInterval(this.scroll_timer);
            this.scroll_timer = window.setInterval(createMethodReference(this, 'scroll'), this.params.scroll_delay);
        }
        
	    this.renderData = function(){
            if (this.need_render == null) {
                this.need_render = true;
                this.renderMessages();
            } else {
                this.need_render = true;
            }
	    }

        this.scroll = function(){
            this.dom.view.scrollTop += this.params.scroll_amount;
            if (this.dom.view.scrollTop == this.prev_scroll) {
                if (this.need_render) this.renderMessages();
                this.dom.view.scrollTop = 0;
            } else {
                this.prev_scroll = this.dom.view.scrollTop;
            }
        }
    });
    
    /* view messages horizontal */
    
    this.viewMessagesHorizontal = function(request_id, dom){
	    arguments.callee.$super.call(this, request_id, dom);
        this.params = Config.view.messages_horizontal;
    }.$extends(this.viewMessages).$class( function ($super) {
        this.scroller_width = 0;
        
        this.renderInit = function(){
		    this.dom.view = this.dom.self;
	    }

        this.renderMessages = function() {
            this.scroller_width = this.dom.view.offsetWidth;
		    var view = '<div class="' + this.params.classes.container + '" style="left:' + this.scroller_width + 'px;">&nbsp;';
		    for (var i=0; i < this.data.p_data.length; i++) {
			    var msg = this.data.p_data[i];
			    var channel_class = 'channel' in msg ? " " + this.params.classes.channel + msg.channel.toString() : "";
			    var type_class = 'type' in msg ? " " + this.params.classes.type + msg.type.toString() : "";
			    view += '<div class="' + this.params.classes.message + channel_class + type_class + '">' + 
			        '<span class="' + this.params.classes.time + '">' +	formatDate(msg.message_time + "000", this.params.date_format) + '</span>' + 
			        '<span class="' + this.params.classes.text + '">' + msg.message + '</span>' +
			        '</div>';
                if (this.params.separator.length && ((i+1) < this.data.p_data.length))
                    view += '<div class="' + this.params.classes.separator + '">' + this.params.separator + '</div>';
		    }
            view += "</div>";

		    emptyNode(this.dom.view);
		    this.dom.view.innerHTML = view;
            this.dom.view.scrollLeft = 0;
            this.need_render = false;
            if (this.scroll_timer) window.clearInterval(this.scroll_timer);
            this.scroll_timer = window.setInterval(createMethodReference(this, 'scroll'), this.params.scroll_delay);
        }

        this.scroll = function(){
            var scroller = this.dom.view.firstChild;
            scroller.style.left = (parseInt(scroller.style.left) - this.params.scroll_amount) + 'px';
            if (parseInt(scroller.style.left) < -scroller.offsetWidth) {
                if (this.need_render) {
                    this.renderMessages();
                } else {
                    scroller.style.left = this.scroller_width + 'px';
                }
            }
        }
    });

    /* view poll */

    this.viewPoll = function(request_id, dom){
	    arguments.callee.$super.call(this, request_id, dom, Config.view.poll);
	    this.data = new pollData();
    }.$extends(viewAbstract).$class( function ($super) {
	    this.renderData = function(){
		    var view = '<div class="' + this.params.classes.title + '">' + this.data.p_data.title + '</div>';
		    var options = this.data.p_data.options; 
		    for (var i=0; i < options.length; i++) {
			    var option = options[i];
			    view += '<div class="' + this.params.classes.option + '">' + 
				    '<div style="float:left">' + 
					    '<span class="' + this.params.classes.option_num + '">'+ option.option + '. </span>' + 
					    '<span class="' + this.params.classes.text + '">' + option.title + '</span>' + 
				    '</div>' + 
				    '<div style="float:right">' + 
			        	'<span class="' + this.params.classes.voices + '">' + option.voices + '</span> ' +
			        	'<span class="' + this.params.classes.voices_percents + '">(' + option.percents.toFixed(2) + '%)</span>' + 
			        '</div>' +
			        '&nbsp;</div>' +
			        '<div class="' + this.params.classes.bar + '">' + 
			        	'<div style="width:' + option.percents.toFixed(2) + '%" class="' + this.params.classes.bar_stripe + '">&nbsp;</div>' +
			        '</div>';
		    }
		    emptyNode(this.dom.self);
		    this.dom.self.innerHTML = view;
	    }
    });

    /* JSONP request wrapper */

    var active_requests = {};

    function requestData(request_id, callback)
    {
        if (active_requests[request_id]) return;

        var cbId, timer;
	    do cbId = 'sms_request_' + request_id + '_' + Math.floor(Math.random() * 99999); 
	       while (window[cbId]);
	
	    var head = document.getElementsByTagName("head")[0];
	    var script = document.createElement("script");
        script.src = Config.request.url + request_id + '&cb=' + cbId;
	    script.type = 'text/javascript';
        
        var timeoutCleanup = function()
        {
            window[cbId] = undefined;
            active_requests[request_id] = undefined;
            try {
                delete window[cbId];
                delete active_requests[request_id];
            } catch(e){};
            if (head && script)
                head.removeChild(script);
        }

	    window[cbId] = function(obj)
        {
            callback(obj);
            delete obj;
            window.clearTimeout(timer);
		    window[cbId] = undefined;
            active_requests[request_id] = undefined;
		    try {
                delete window[cbId];
                delete active_requests[request_id];
            } catch(e){};
		    head.removeChild(script);
	    };
        
        active_requests[request_id] = true;
        timer = window.setTimeout(function() {timeoutCleanup()}, Config.request.timeout);
	    head.appendChild(script);
    }

    /* initializator */

    this.init = function()
    {
        var publication_elements;
        var params = Config.init;

        if (document.getElementsByClassName) {
            publication_elements = document.getElementsByClassName(params.publish_class);
        } else { //implement getElementsByClassName function
            var hasClassName = new RegExp("(?:^|\\s)" + params.publish_class + "(?:|\\s)");
		    var allElements = document.getElementsByTagName("*");
		
		    var element;
            publication_elements = new Array();
		    for (var i = 0; (element = allElements[i]) != null; i++) {
			    var elementClass = element.className;
			    if (elementClass && (elementClass.indexOf(params.publish_class) != -1) && hasClassName.test(elementClass))
				    publication_elements.push(element);
		    }
        }

	    for (var i=0; i < publication_elements.length; i++) {
		    var element = publication_elements[i];
		    var id = element.id;
		    if (id) {
			    var match = id.match("^" + params.id_prefix + '(' + params.request_id_regexp + ')');
			    if (match && match[1]) {
				    var request_id = match[1];
                    var dom = element;
				    match = element.className.match("(?:^|\\s)" + params.view_model_class_prefix + "(" + params.view_model_regexp + ")" + "(?:|\\s)");
				    if (match && match[1]) {
					    var view_model = params.view_model_object_prefix + match[1].slice(0,1).toUpperCase() + match[1].slice(1);
					    if (this[view_model] && (typeof(this[view_model]) == 'function')) {
						    var view;
						    try {
							    view = new this[view_model](request_id, dom);
							    view.init();
						    } catch(e) {}
					    }
				    }
			    }
		    }
	    }
    }
    
    /* misc */

    function formatDate(date, format)
    {
	    if (!(date instanceof Date)) {
		    var d = new Date();
		    d.setTime(date);
		    date = d;
	    }
	
	    var add_zero = function(str) {
		    str = str.toString();
		    return (str.length == 1) ? "0" + str : str;
	    }
	
	    var parts = {
		    '%Y' : date.getFullYear(),
		    '%y' : date.getFullYear().toString().slice(2,4),
		    '%m' : add_zero(date.getMonth() + 1),
		    '%d' : add_zero(date.getDate()),
		    '%H' : add_zero(date.getHours()),
		    '%M' : add_zero(date.getMinutes()),
		    '%S' : add_zero(date.getSeconds())
	    };
	
	    var out = format;
	    for (part in parts)
		    out = out.replace(new RegExp(part, 'g'), parts[part]);
		
	    return out;
    }
    
    function extend(primary, secondary)
    {
        var ex = primary || {};
        for (param in secondary)
            if (!(param in ex))
                ex[param] = secondary[param];
        return ex;
    }

    function emptyNode(node)
    {
        if (!node) return;
        while (node.hasChildNodes()) {
            emptyNode(node.firstChild);
            node.removeChild(node.firstChild);
        }
    }

    function getElementHeight(e)
    {
        var height =  e.offsetHeight || e.style.pixelHeight || null;
        if (height != null) return height;
        var res = 0;
        while ((res == 0) && e.parentNode) {
            e = e.parentNode;
            res = e.offsetHeight;
        }
        return res;
    }

    function createMethodReference(object, methodName)
    {
        return function () {
            return object[methodName].apply(object, arguments);
        };
    }

    function attachOnload(func) 
    {
        function init() {
		    if (arguments.callee.done) return;
		    arguments.callee.done = true;
		    if (_timer) {
			    clearInterval(_timer);
			    _timer = null;
		    }
		
		    func();
	    };
	
	    /* for Mozilla */
	    if (document.addEventListener) {
		    document.addEventListener("DOMContentLoaded", init, false);
	    }
	
	    /* for Internet Explorer */
	    /*@cc_on @*/
	    /*@if (@_win32)
		    document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
		    var script = document.getElementById("__ie_onload");
		    script.onreadystatechange = function() {
			    if (this.readyState == "complete") {
				    init(); // call the onload handler
			    }
		    };
	    /*@end @*/
	
	    /* for Safari */
	    if (/WebKit/i.test(navigator.userAgent)) { // sniff
		    var _timer = setInterval(function() {
			    if (/loaded|complete/.test(document.readyState)) {
				    init(); // call the onload handler
			    }
		    }, 10);
	    }
	
	    /* for other browsers */
	    window.onload = init;
    }

    attachOnload(createMethodReference(this, 'init'));
}

var sms_publication = new smsPublication();

