//Copyright (c) 2004 Jan-Klaas Kollhof

Module("irc", "0.0.3", function(thisMod){
    var svgnet = importModule("svgnet");    
    
    var StateChanged = Class("StateChanged", function(thisClass, Super){
        thisClass.prototype.init = function(){
            this.type = "stateChanged";
        }
    })
        
    var EventDispatcher=Class("EventDispatcher", function(thisClass, Super){
        thisClass.prototype.init = function(){
            this.eventListeners = {};
        }
        
        thisClass.prototype.addEventListener=function(type, handler){
            if(this.eventListeners[type]){
                this.eventListeners[type].push(handler);
            }else{
                this.eventListeners[type] = [handler];
            }
        }
        
        thisClass.prototype.dispatchEvent=function(evt){
            var handlers = this.eventListeners[evt.type];
            if(handlers){
                for(var i=0;i<handlers.length;i++){
                    handlers[i].handleEvent(evt);
                }
            }
        }
    })
    
    var IRCMessage=Class("IRCMessage", function(thisClass, Super){
        thisClass.prototype.init=function(msg){
            this.prefix="";
            this.cmd="";
            this.params=[];
            this.msg = "";
            if(msg.charAt(0) == ":"){
                var pos = msg.indexOf(" ");
                this.prefix = msg.slice(1, pos);
                msg = msg.slice(pos+1, msg.length);    
            }
            msg = msg.split(":");
            this.params = msg.shift().split(" ");
            this.cmd = this.params.shift().toUpperCase();
            this.msg = msg.join(":");
        }
        
        thisClass.prototype.toString=function(){
            var s = "";
            if(this.prefix){
                s+=":" + this.prefix;
            }
            s+="%s %s".format(this.cmd, this.params.join(" "));
            if(this.msg){
                s+=" :" + this.msg;
            }
            return s;
        }
    })
    
    
    thisMod.IRCClient = Class("IRCClient", EventDispatcher, function(thisClass, Super){
        thisClass.prototype.init = function(host, port){
            Super.prototype.init.call(this);
            this.connected = false;
            this.messages = [];        
            this.nick = "";
            this.user = "svgirc";
            this.channel = "";
            this.realName = "";
            this.host = host;
            this.port = port;
            this.buffer="";
       }
        
        thisClass.prototype.connect=function(){
            try{
                this._conn = createConnection();
                this._conn.addEventListener("connectionData",this, false);
                this._conn.connect(this.host + ":" + this.port);
                this.connected = true;
            }catch(e){
                this.connected = false;
                this.handleMsg("ERROR :failed connecting to %s:%d".format(host, port));
                this.dispatchEvent(new StateChanged());
            }
        }
    
        thisClass.prototype.sendCommand = function(cmd, params, msg){
            if(msg){
                this.send("%s %s :%s\r\n".format(cmd, params.join(" "), msg));
            }else{
                this.send("%s %s\r\n".format(cmd, params.join(" "), msg));
            }
        }
        
        thisClass.prototype.send = function(s){
            if(this.connected){
                this._conn.send(s);
            }
        }
        
        thisClass.prototype.handleEvent = function(evt){
            if(this[evt.type]){
                this[evt.type](evt);
            }
        }
        
        thisClass.prototype.connectionData = function(evt){
            this.buffer += evt.data;
            var lines = this.buffer.split("\n");
            if(lines.length > 0){
                this.buffer = lines.pop();
            }
            
            for(var i=0;i<lines.length;i++){
                this.handleMsg(lines[i]);
            }
            this.dispatchEvent(new StateChanged());
        }
                
        thisClass.prototype.handleMsg = function(msg){
            msg = new IRCMessage(msg);
            this.messages.push(msg);
        }
        
        thisClass.prototype.userInput = function(msg){
            if(msg.slice(0,1) =="/"){
                msg = msg.slice(1, msg.length);
                msg = msg.split(":");
                var params = msg.shift().split(" ");
                var cmd = params.shift().toUpperCase();
                msg = msg.join(":");
            }else{
                var cmd="PRIVMSG";
                var params=[this.channel];
            }
            if(cmd == "SVGIRC"){
                this.channel = "#svg";
                this.nick = params.shift();
                this.realName = params.join(" ");
                this.connect();
                this.sendCommand("NICK", [this.nick],"");
                this.sendCommand("USER", ["svgirc", 8, "*"], this.realName);
                this.sendCommand("JOIN", [this.channel],"");
                this.handleMsg("%s %s :%s".format(cmd, params.join(" "), msg));
                this.dispatchEvent(new StateChanged());
            }else{
                switch(cmd){
                    case "QUIT":
                    case "AWAY":
                        if(msg){
                            msg = " :" + msg;
                        }
                        msg = params.join(" ") + msg
                        params = [];
                        break;
                    case "TOPIC":
                        var c = params.shift();
                        msg = params.join(" ");
                        params = [c];
                }
                this.sendCommand(cmd, params, msg);
                this.handleMsg("%s %s :%s".format(cmd, params.join(" "), msg));
                this.dispatchEvent(new StateChanged());
            }
        }
    })
    
    
    thisMod.IRCTextBox =Class("IRCTextBox", function(thisClass, Super){
        thisClass.prototype.init = function(chat, node){
            this.chat = chat;
            chat.addEventListener("stateChanged", this);
            this.node = node;
            this.spans = new Array();
            for(var i=0;i<22;i++){
                var tsp = document.createElementNS("http://www.w3.org/2000/svg","text");
                tsp.setAttribute("y", i * 14 + 15);
                tsp.setAttribute("x","5");
                tsp.setAttribute("font-family","monospace");
                tsp.appendChild(document.createTextNode(""));
                this.spans.push(tsp);
                node.appendChild(tsp);
            }
            this.text = "";
        }
        
        thisClass.prototype.handleEvent = function(evt){
            if(this[evt.type]){
                this[evt.type](evt);
            }
        }
        
        thisClass.prototype.stateChanged = function(evt){
            this.showMessages(this.chat.messages);
        }
        
        thisClass.prototype.getPrefixNick = function(msg){
            if(msg.prefix){
                var pos = msg.prefix.indexOf("!");
                if(pos){
                    var nick = msg.prefix.slice(0,pos);
                }else{
                    var nick = msg.prefix;
                }
            }else{
                var nick = this.chat.nick;
            }
            return nick;
        }
        
        thisClass.prototype.getMsgString = function(msg){
            var s="";
            switch(msg.cmd){
                case "NOTICE":
                    s = "   notice: %s".format(msg.msg);
                    break;
                case "ERROR":
                    s = "    error: %s".format(msg.msg);
                    break;
                case "PRIVMSG":
                    s = "<%s> %s".format(this.getPrefixNick(msg), msg.msg);
                    break;
                case "301":
                    s =  "    %s is away: %s".format(msg.params.join(" "), msg.msg);
                    break;
                case "305":
                case "306":
                    s= "    " + msg.msg;
                    break;
                case "311":
                    s = "    <%s> is %s".format(msg.params[0], msg.msg);
                    break;
                case "314":
                    s = "    <%s> was %s".format(msg.params[0], msg.msg);
                    break;
                case "318":
                    s = "    end of whois <%s>".format(msg.params[0]);
                    break;
                case "319":
                    s = "    <%s> is in <%s>".format(msg.params[0], msg.msg);
                    break;
                case "369":
                    s = "    end of whowas <%s>".format(msg.params[0]);
                    break;          
                case "372":
                    s = "    " + msg.msg;
                    break;
                case "332":
                    s=  "    the topic is: " + msg.msg;
                    break;
                default:
                    s = "/%s %s".format(msg.cmd, msg.params.join(" "));
                    if(msg.msg){
                        s+=" :" + msg.msg;
                    }
            }
            return s;
        }
        
        thisClass.prototype.showMessages = function(msgs){
            var ms = msgs.slice(- this.spans.length);
            for(var i=0;i<ms.length;i++){
                var indx=this.spans.length-ms.length + i;
                var d = this.getMsgString(ms[i]);
                this.spans[indx].firstChild.data = d;
                switch(ms[i].cmd){
                    case "NOTICE":
                        this.spans[indx].setAttribute("fill", "blue");
                        break;
                    case "ERROR":
                        this.spans[indx].setAttribute("fill", "red");
                        break;
                    default:
                        this.spans[indx].setAttribute("fill", "black");
                }
            }
        }
        
        thisClass.prototype.setText = function(s){
            var lines = s.split("\n");
            var start=0;
            if(lines.length >  this.spans.length){
                start = lines.length - this.spans.length ;
            }
            var a = [];
            for(var i=start; i < lines.length;i++){
                a.push(lines[i]);
            }  
            var t = a.join("\n");
            if(this.text != t){
                this.text = t;
                for(var i=0;i<a.length;i++){
                    this.spans[i].firstChild.data = a[i];
                }
            }
        }
    })
})


