Hw_Comments = function(data, dataorder) {
    this.updateCurTime();
    this.topTemplate = new EJS({text: $('#tpl_top').html()});
    this.innerTemplate = new EJS({text: $('#tpl_inner').html()});
    this.data = data;
    this.dataorder = dataorder;
};

Hw_Comments.prototype = {
    data : null,
    dataorder: null, // для сраного хрома %)
    topTemplate : '',
    lastReadTime : null,
    userSide : null,
    curTime : null,
    loadNewComments : function(){
        var ts = parseInt(this.lastReadTime.getTime()/1000);
        var instance = this;
        $.getJSON(BASEURL+"/comments/get-data", {war_id: WAR_ID, after: ts}, function(data){ 
            instance.updateData(data.data, data.dataorder);
            instance.renderPartial(data.data, data.dataorder);
            instance.setLastReadTime(data.ts);
        });
    },
    postAndLoad : function(parent_id, text, callback){
        var ts = parseInt(this.lastReadTime.getTime()/1000);
        var instance = this;
        $.postJSON(BASEURL+"/comments/add", {war_id: WAR_ID, after: ts, parent_id: parent_id, text: text}, function(data){ 
            if (data.success===true) { 
                instance.updateData(data.data, data.dataorder);
                instance.renderPartial(data.data, data.dataorder);
                instance.setLastReadTime(data.ts);
            } else { 
                if (data.error) {
                    alert(data.error);
                } else {
                    alert('Ошибка!');
                }
            }
            if (callback) {
                callback(data);
            }
        });
    },
    reRender : function(){
        var crl = $('#comments_row_left');
        var crr = $('#comments_row_right');
        crl.html('');
        crr.html('');
        for (var x in this.dataorder) { 
            var argumentId = this.dataorder[x];
            var row = this.data[argumentId];
            if (row.side == 1) {
                crl.append(this.renderTopRow(row));
            }
            if (row.side == 2) {
                crr.append(this.renderTopRow(row));
            }
        }
    },
    updateData : function(data, dataorder){
        for (var x in dataorder) { 
            var argumentId = dataorder[x];
            if (!this.data[argumentId]) {
                this.data[argumentId] = data[argumentId];
            } else {
                for (var commentId in data[argumentId].childNodesOrder) {
                    this.data[argumentId].childNodes[commentId] = data[argumentId].childNodes[commentId];
                    this.data[argumentId].childNodesOrder = this.data[argumentId].childNodesOrder.concat(data[argumentId].childNodesOrder);
                }
            }
        }
        this.dataorder = this.dataorder.concat(dataorder);
    },
    renderPartial : function(data, dataorder){
        //removeNewState();
        this.updateCurTime();
        for (var x in dataorder) {
            var argumentId = dataorder[x];
            var row = data[argumentId];
            if (row.id) {
                if ($('#comment-cnt_'+row.id).length > 0) continue;
                if (row.side == 1) {
                    $('#comments_row_left').append(this.renderTopRow(row));
                }
                if (row.side == 2) {
                    $('#comments_row_right').append(this.renderTopRow(row));
                }
            } else {
                var numChilds = 0;
                for (var xx in row.childNodes) {
                    var innerRow = row.childNodes[xx];
                    $('#comment_'+argumentId+'_childs').append(this.renderInnerRow(innerRow));
                    numChilds++;
                }
                if (numChilds > 0) {
                    this.updateChildCount(argumentId, numChilds);
                    $('#comm_open_'+argumentId).hide();
                    $('#comm_close_'+argumentId).show();
                }
            }
        }
        updateRPanelCounter();
    },
    updateChildCount : function(id, plus){
        var el1 = $('#comm_edit_place_'+id).parent().find('.comm_close a.dashed_link').eq(0);
        var el2 = $('#comm_edit_place_'+id).parent().find('.comm_open a.dashed_link').eq(0);
        var num = parseInt(el1.html().replace(/\D+/, ''));
        num += parseInt(plus);
        var text = num+' '+declension(num, 'комментарий комментария комментариев');
        el1.html(text);
        el2.html(text);
    },
    renderTop : function(){ 
        this.updateCurTime();
        var crl = $('#comments_row_left');
        var crr = $('#comments_row_right');
        var leftHtml = '';
        var rightHtml = '';
        for (var x in this.dataorder) { 
            var argumentId = this.dataorder[x];
            var row = this.data[argumentId];
            if (row.side == 1) { 
                leftHtml += this.renderTopRow(row);
                //crl.append(this.renderTopRow(row));
            }
            if (row.side == 2) {
                rightHtml += this.renderTopRow(row);
                //crr.append(this.renderTopRow(row));
            }
        }
        crl.append(leftHtml);
        crr.append(rightHtml);
        updateRPanelCounter();
    },
    renderInnerRow : function(row){
        switch (row.side) {
        case 1:
            var cssClass = 'green_comment';
            break;
        case 2:
            var cssClass = 'blue_comment';
            break;
        case 0:
            var cssClass = 'neitral_comment';
            break;
        default:
            console.log(row);
            return '';
            break;
        }
        row.tableClass = (this.isNew(row.time)?'new_comm '+cssClass:cssClass);
        row = this.getExtendedRow(row);
        return this.innerTemplate.render({row:row});
    },
    renderTopRow : function(row){ 
        switch (row.side) {
        case 1:
            var cssClass = 'green_argument';
            var imagePart = 'green';
            break;
        case 2:
            var cssClass = 'blue_argument';
            var imagePart = 'blue';
            break;
        case 0:
        default:
            console.log(row);
            return '';
            break;
        }
        row.tableClass = (this.isNew(row.time)?'new_view '+cssClass:cssClass);
        row.imagePart = imagePart;
        row = this.getExtendedRow(row);
        return this.topTemplate.render({row:row});
    },
    renderInner : function(row){
        
    },
    renderAnchor : function(id){
        return '<a name="comment_'+id+'"></a>';
    },
    getExtendedRow : function(row){
        if (row.votes == 0) {
            row.vnumClass = 'zero';
        } else if (row.votes > 0) { 
            row.votes = '+'+row.votes;
            row.vnumClass = 'plus';
        } else if (row.votes < 0) {
            row.vnumClass = 'minus';
        }
        if (row.bigavatar) {
            row.avatarSrc = '/upload/avatars/'+row.bigavatar;
        } else if (row.openid) {
            row.avatarSrc = '/images/openid.gif';
        } else {
            row.avatarSrc = '/images/ava.gif';
        }
        row.isNewPic = this.isNewImage(row.time);
        row.timeFormatted = this.date(row.time);
        if (row.childNodes) {
            row.childNodesNum = count(row.childNodes);
            row.childNodesTitle = declension(row.childNodesNum, 'комментарий комментария комментариев');
        }
        row.isAuthor = false;
        row.canEditComment = false;
        row.canDeleteComment = false;
        row.openComments = false;
        row.canVoteMinus = false;
        row.canVotePlus = false;
        row.voteType = null;
        
        if (USER_ID !== null) {
            if (USER.role == 'admin') {
                row.canEditComment = true;
                row.canDeleteComment = true;
            }
            if (row.author_id == USER_ID) {
                row.isAuthor = true;
                if (this.curTime.getTime() - row.time*1000 < EDIT_COMMENT_LIMIT*1000) {
                    row.canEditComment = true;
                }
            }
            if (this.userSide !== false) {
                if (row.isAuthor === false) {
                    var voters = unserialize(row.voters);
                    if (voters[USER_ID]) {
                        row.voteType = voters[USER_ID];
                    } else {
                        if (this.userSide === row.side || row.side===0) {
                            row.canVoteMinus = true;
                            row.canVotePlus = true;
                        } else {
                            row.canVotePlus = true;
                        }
                    }
                }
            }
        }
        
        if (row.parent_id == 0) {
            if (row.side===0){}
            if (row.childNodesNum > 0) {
                for(var x in row.childNodes) {
                    var chNode = row.childNodes[x];
                    // если есть свои ответы
                    if (chNode.author_id == USER_ID) {
                        row.openComments = true;
                    }
                    // если есть новые комменты
                    if (chNode.time*1000 > this.lastReadTime) {
                        row.openComments = true;
                    }
                    // если есть "золотые" комменты
                    if (chNode.votes >= GOLD_COMMENT_VOTE) {
                        row.openComments = true;
                    }
                }
            }
        } else {
            if (row.argument_id == row.parent_id) { 
                var replyRow = this.data[row.argument_id];
            } else {
                var replyRow = this.data[row.argument_id]['childNodes'][row.parent_id];
            }
            row.text = '<a href="#comment_'+replyRow.id+'" onclick="$(\'#comment-cnt_'+replyRow.id+' div.text\').effect(\'pulsate\', { times:1 }, 800);">'+replyRow.username+'</a>: '+row.text;
        }
        
        return row;
    },
    updateCurTime : function(){
        this.curTime = new Date();
    },
    setLastReadTime : function(ts){
        if (!ts) {
            this.lastReadTime = new Date();
        } else {
            this.lastReadTime = new Date(ts*1000);
        }
        return parseInt(this.lastReadTime.getTime()/1000);
    },
    setUserSide : function(side){
        if (!side && side !== 0) side = false;
        this.userSide = side;
    },
    isNew : function(time){
        time = time*1000;
        return time > this.lastReadTime.getTime();
    },
    isNewImage : function(time){
        if (this.isNew(time)) {
            return '&nbsp; <img align="absmiddle" src="'+BASEPATH+'/public/images/new.png" />';
        } else {
            return '';
        }
    },
    date : function(ts) {
        //var d = new Date(ts*1000);
        //return d.toLocaleString();
        return date('d-m-Y H:i', ts);
    }
};

function time(){
    var d = new Date();
    return parseInt(d.getTime()/1000);
}

function count( mixed_var, mode ) {
    var key, cnt = 0;
    if( mode == 'COUNT_RECURSIVE' ) mode = 1;
    if( mode != 1 ) mode = 0;
    for (key in mixed_var){
        cnt++;
        if( mode==1 && mixed_var[key] && (mixed_var[key].constructor === Array || mixed_var[key].constructor === Object) ){
            cnt += count(mixed_var[key], 1);
        }
    }
    return cnt;
}

function date ( format, timestamp ) {
    var a, tal=[], jsdate=(
        (typeof(timestamp) == 'undefined') ? new Date() : // Not provided
        (typeof(timestamp) == 'number') ? new Date(timestamp*1000) : // UNIX timestamp
        new Date(timestamp) // Javascript Date()
    );
    var pad = function(n, c){
        if( (n = n + "").length < c ) {
            return new Array(++c - n.length).join("0") + n;
        } else {
            return n;
        }
    };
    var ret = '';
    var txt_weekdays = ["Sunday","Monday","Tuesday","Wednesday",
        "Thursday","Friday","Saturday"];
    var txt_ordin = {1:"st",2:"nd",3:"rd",21:"st",22:"nd",23:"rd",31:"st"};
    var txt_months =  ["", "January", "February", "March", "April",
        "May", "June", "July", "August", "September", "October", "November",
        "December"];

    var f = {
        // Day
            d: function(){
                return pad(f.j(), 2);
            },
            D: function(){
                var t = f.l();
                return t.substr(0,3);
            },
            j: function(){
                return jsdate.getDate();
            },
            l: function(){
                return txt_weekdays[f.w()];
            },
            N: function(){
                return f.w() + 1;
            },
            S: function(){
                return txt_ordin[f.j()] ? txt_ordin[f.j()] : 'th';
            },
            w: function(){
                return jsdate.getDay();
            },
            z: function(){
                return (jsdate - new Date(jsdate.getFullYear() + "/1/1")) / 864e5 >> 0;
            },

        // Week
            W: function(){
                var a = f.z(), b = 364 + f.L() - a;
                var nd2, nd = (new Date(jsdate.getFullYear() + "/1/1").getDay() || 7) - 1;

                if(b <= 2 && ((jsdate.getDay() || 7) - 1) <= 2 - b){
                    return 1;
                } else{

                    if(a <= 2 && nd >= 4 && a >= (6 - nd)){
                        nd2 = new Date(jsdate.getFullYear() - 1 + "/12/31");
                        return date("W", Math.round(nd2.getTime()/1000));
                    } else{
                        return (1 + (nd <= 3 ? ((a + nd) / 7) : (a - (7 - nd)) / 7) >> 0);
                    }
                }
            },

        // Month
            F: function(){
                return txt_months[f.n()];
            },
            m: function(){
                return pad(f.n(), 2);
            },
            M: function(){
                var t;
                t = f.F(); return t.substr(0,3);
            },
            n: function(){
                return jsdate.getMonth() + 1;
            },
            t: function(){
                var n;
                if( (n = jsdate.getMonth() + 1) == 2 ){
                    return 28 + f.L();
                } else{
                    if( n & 1 && n < 8 || !(n & 1) && n > 7 ){
                        return 31;
                    } else{
                        return 30;
                    }
                }
            },

        // Year
            L: function(){
                var y = f.Y();
                return (!(y & 3) && (y % 1e2 || !(y % 4e2))) ? 1 : 0;
            },
            o: function(){
                if (f.n() === 12 && f.W() === 1) {
                    return jsdate.getFullYear()+1;
                }
                if (f.n() === 1 && f.W() >= 52) {
                    return jsdate.getFullYear()-1;
                }
                return jsdate.getFullYear();
            },
            Y: function(){
                return jsdate.getFullYear();
            },
            y: function(){
                return (jsdate.getFullYear() + "").slice(2);
            },

        // Time
            a: function(){
                return jsdate.getHours() > 11 ? "pm" : "am";
            },
            A: function(){
                return f.a().toUpperCase();
            },
            B: function(){
                // peter paul koch:
                var off = (jsdate.getTimezoneOffset() + 60)*60;
                var theSeconds = (jsdate.getHours() * 3600) +
                                 (jsdate.getMinutes() * 60) +
                                  jsdate.getSeconds() + off;
                var beat = Math.floor(theSeconds/86.4);
                if (beat > 1000) beat -= 1000;
                if (beat < 0) beat += 1000;
                if ((String(beat)).length == 1) beat = "00"+beat;
                if ((String(beat)).length == 2) beat = "0"+beat;
                return beat;
            },
            g: function(){
                return jsdate.getHours() % 12 || 12;
            },
            G: function(){
                return jsdate.getHours();
            },
            h: function(){
                return pad(f.g(), 2);
            },
            H: function(){
                return pad(jsdate.getHours(), 2);
            },
            i: function(){
                return pad(jsdate.getMinutes(), 2);
            },
            s: function(){
                return pad(jsdate.getSeconds(), 2);
            },
            u: function(){
                return pad(jsdate.getMilliseconds()*1000, 6);
            },

            e: function () {
                return 'UTC';
            },
            I: function(){
                var DST = (new Date(jsdate.getFullYear(),6,1,0,0,0));
                DST = DST.getHours()-DST.getUTCHours();
                var ref = jsdate.getHours()-jsdate.getUTCHours();
                return ref != DST ? 1 : 0;
            },
            O: function(){
               var t = pad(Math.abs(jsdate.getTimezoneOffset()/60*100), 4);
               if (jsdate.getTimezoneOffset() > 0) t = "-" + t; else t = "+" + t;
               return t;
            },
            P: function(){
                var O = f.O();
                return (O.substr(0, 3) + ":" + O.substr(3, 2));
            },
            T: function () {
                return 'UTC';
            },
            Z: function(){
               var t = -jsdate.getTimezoneOffset()*60;
               return t;
            },
        // Full Date/Time
            c: function(){
                return f.Y() + "-" + f.m() + "-" + f.d() + "T" + f.h() + ":" + f.i() + ":" + f.s() + f.P();
            },
            r: function(){
                return f.D()+', '+f.d()+' '+f.M()+' '+f.Y()+' '+f.H()+':'+f.i()+':'+f.s()+' '+f.O();
            },
            U: function(){
                return Math.round(jsdate.getTime()/1000);
            }
    };

    return format.replace(/[\\]?([a-zA-Z])/g, function(t, s){
        if( t!=s ){
            // escaped
            ret = s;
        } else if( f[s] ){
            // a date function exists
            ret = f[s]();
        } else{
            // nothing special
            ret = s;
        }

        return ret;
    });
}

function declension(int, expressions) {
    if (typeof expressions === 'string') {
        expressions = expressions.split(' ');
    }
    if (expressions.length == 2) {
        expressions[2] = expressions[1]; 
    }
    int = parseInt(int);
    var count = int % 100;
    if (count >= 5 && count <= 20) {
        var result = expressions[2];
    } else {
        count = count % 10;
        if (count == 1) {
            var result = expressions[0];
        } else if (count >= 2 && count <= 4) {
            var result = expressions[1];
        } else {
            var result = expressions[2];
        }
    }
    return result;
}

function unserialize(data){
    // Takes a string representation of variable and recreates it  
    // 
    // version: 903.3016
    // discuss at: http://phpjs.org/functions/unserialize
    // +     original by: Arpad Ray (mailto:arpad@php.net)
    // +     improved by: Pedro Tainha (http://www.pedrotainha.com)
    // +     bugfixed by: dptr1988
    // +      revised by: d3x
    // +     improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: Brett Zamir (http://brettz9.blogspot.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // %            note: We feel the main purpose of this function should be to ease the transport of data between php & js
    // %            note: Aiming for PHP-compatibility, we have to translate objects to arrays 
    // *       example 1: unserialize('a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}');
    // *       returns 1: ['Kevin', 'van', 'Zonneveld']
    // *       example 2: unserialize('a:3:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";s:7:"surName";s:9:"Zonneveld";}');
    // *       returns 2: {firstName: 'Kevin', midName: 'van', surName: 'Zonneveld'}
    
    var error = function (type, msg, filename, line){throw new window[type](msg, filename, line);};
    var read_until = function (data, offset, stopchr){
        var buf = [];
        var chr = data.slice(offset, offset + 1);
        var i = 2;
        while (chr != stopchr) {
            if ((i+offset) > data.length) {
                error('Error', 'Invalid');
            }
            buf.push(chr);
            chr = data.slice(offset + (i - 1),offset + i);
            i += 1;
        }
        return [buf.length, buf.join('')];
    };
    var read_chrs = function (data, offset, length){
        var buf;
        
        buf = [];
        for(var i = 0;i < length;i++){
            var chr = data.slice(offset + (i - 1),offset + i);
            buf.push(chr);
        }
        return [buf.length, buf.join('')];
    };
    var _unserialize = function (data, offset){
        var readdata;
        var readData;
        var chrs = 0;
        var ccount;
        var stringlength;
        var keyandchrs;
        var keys;

        if(!offset) offset = 0;
        var dtype = (data.slice(offset, offset + 1)).toLowerCase();
        
        var dataoffset = offset + 2;
        var typeconvert = new Function('x', 'return x');
        
        switch(dtype){
            case "i":
                typeconvert = new Function('x', 'return parseInt(x)');
                readData = read_until(data, dataoffset, ';');
                chrs = readData[0];
                readdata = readData[1];
                dataoffset += chrs + 1;
            break;
            case "b":
                typeconvert = new Function('x', 'return (parseInt(x) == 1)');
                readData = read_until(data, dataoffset, ';');
                chrs = readData[0];
                readdata = readData[1];
                dataoffset += chrs + 1;
            break;
            case "d":
                typeconvert = new Function('x', 'return parseFloat(x)');
                readData = read_until(data, dataoffset, ';');
                chrs = readData[0];
                readdata = readData[1];
                dataoffset += chrs + 1;
            break;
            case "n":
                readdata = null;
            break;
            case "s":
                ccount = read_until(data, dataoffset, ':');
                chrs = ccount[0];
                stringlength = ccount[1];
                dataoffset += chrs + 2;
                
                readData = read_chrs(data, dataoffset+1, parseInt(stringlength));
                chrs = readData[0];
                readdata = readData[1];
                dataoffset += chrs + 2;
                if(chrs != parseInt(stringlength) && chrs != readdata.length){
                    error('SyntaxError', 'String length mismatch');
                }
            break;
            case "a":
                readdata = {};
                
                keyandchrs = read_until(data, dataoffset, ':');
                chrs = keyandchrs[0];
                keys = keyandchrs[1];
                dataoffset += chrs + 2;
                
                for(var i = 0;i < parseInt(keys);i++){
                    var kprops = _unserialize(data, dataoffset);
                    var kchrs = kprops[1];
                    var key = kprops[2];
                    dataoffset += kchrs;
                    
                    var vprops = _unserialize(data, dataoffset);
                    var vchrs = vprops[1];
                    var value = vprops[2];
                    dataoffset += vchrs;
                    
                    readdata[key] = value;
                }
                
                dataoffset += 1;
            break;
            default:
                error('SyntaxError', 'Unknown / Unhandled data type(s): ' + dtype);
            break;
        }
        return [dtype, dataoffset - offset, typeconvert(readdata)];
    };
    return _unserialize(data, 0)[2];
}