#!/usr/bin/python

z = "\r\n"
DEFAULT_HOST = "irc.freenode.net"
DEFAULT_PORT = 6667
DEFAULT_NICK = "Myrkur"
DEfAULT_USER = "Myrkur"

import re
import socket
import sys
import os

class Types:
    pass
    
types = Types()

types.MSG     = 1
types.NOTICE  = 2
types.JOIN    = 3
types.PART    = 4
types.KICK    = 5
types.CQUIT   = 6
types.MODE    = 7
types.TOPIC   = 8

# unknown modes get 1000 added, that is: a message like :yab.ath.cx 001 Myrkur :Welcome to the Internet Relay Network Myrkur gets mode 1001


class IRCdata:
    def __init__(self):
        self.host = ""
        self.port = 6667
        self.nick = ""
        self.user = ""
        self.real = ""
        self.server_pass   = ""
        self.nickserv_pass = ""
        self.autoexec = []
        
    def check(self):
        if (self.host == ""):
            self.host = DEFAULT_HOST
        if (self.port == 0):
            self.port = DEFAULT_PORT
        if (self.nick == ""):
            self.nick = DEFAULT_NICK
        if (self.user == ""):
            self.user = DEFAULT_USER
        if (self.server_pass == ""):
            self.use_server_pass = False
        else:
            self.use_server_pass = True
        if (self.nickserv_pass == ""):
            self.use_nickserv_pass = False
        else:
            self.use_nickserv_pass = True
            
    def next_nick(self):
        self.nick = self.nick + "_"
        return self.nick


def normalize(stringle):
    return stringle.strip().lower()

def userathost(a, b):
    "Concatenates a and b to a '(user@host) ' combination, adds a blank at the end."
    if a == None or b == None:
        return ""
    else:
        return "("+str(a)+"@"+str(b)+") "

class IRC:
    def __init__(self, data):
        data.check()
        self.user = data
        self.connected = False
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.queue = []
        self.parsedqueue = []
        self.channels = {}
        self.moddir = "." # we assume . till somebody tells us something different
        # TODO: Log file please
        #####################################
        # MODULES                           #
        #####################################
        self.modlist = {}

    def moddir(self, moddir):
        if os.path.exists(moddir):
            self.moddir = moddir
        
        
    def send(self, data, selfconnected = False):
        if (not selfconnected and not self.connected):
            return False
        data = data + z
        self.socket.send (data)
        return True
        
    def raw_recv(self, bufsiz = 1024):
        #x[-1]
        data = self.socket.recv(bufsiz)
        if (len(data) == 0):
            self.connected = False
            self.do_quit("poof.")
        c = 1
        while (data[-1] != '\n'):
            data = data + self.socket.recv(bufsiz)
            print data
            c += 1
        return data
                
    def recv(self, bufsiz = 1024):
        if (not self.connected):
            return None
        data = self.raw_recv (bufsiz)
        data2 = data.splitlines()
        for i in data2:
            if (i.find("PING") == 0):
                pong = i.split()[1]           # if the server sends PING :12345, we return PONG :12345, if it sends PING 12345, we return PONG 12345.
                self.socket.send("PONG "+pong+z)
            else:
                self.queue.append(i.strip())  # We don't enqueue pings, because they're vital. - strip() is trim(), btw.
    
    def parse_queue (self):
        while len(self.queue) > 0:
            i = self.queue.pop(0)
            if (i[0] == ':'):
                print "                                      "+i
                # see what it is
                xsplices = re.findall(':(.*?) .*', i)
                ysplices = ()
                if len(xsplices) > 0:
                    ysplices = xsplices[0]
                else:
                    continue
                                
                zsplices = re.findall('(.*?)!(.*?)@(.*)', ysplices)
                
                if (len(zsplices) > 0):
                    splices = zsplices[0]
                else:
                    splices = (ysplices, None, None)
                
                if (len(splices) > 2):
                    nick = splices[0]
                    user = splices[1]
                    host = splices[2]
                elif (len(splices) < 1):
                    nick = ysplices[0]
                    user = None
                    host = None
                else:
                    nick = splices[0]
                    user = None
                    host = None
                    
                
                
                sender = i.split()[0]
                rest = i[len(sender):].strip() # now we've got something like "MODE #yab +o Xjs"
                
                what = rest.split()[0].lower() # I convert all the args to lower case, it's easier later.
                rest = rest[len(what):].strip()
                
                if (what == "quit"):
                    mesg = rest[1:]
#                    print nick+" "+userathost(user,host)+"quit (Reason: "+mesg+")!"
                    continue
                
                chan = rest.split()[0].lower()
                mesg = rest[len(chan):].strip()
                if (len(mesg) > 0 and mesg[0] == ':'):
                    mesg = mesg[1:]                    
                
#                print nick+" "+userathost(user,host)+"did a "+what+" on "+chan,
                if (mesg != ""):
#                    print "with message "+mesg
                    pass
                else:
#                    print
                    pass
                    
                if (what == "join" and nick == self.user.nick):
                    self.channels[chan] = True
                
                if (what == "part" and nick == self.user.nick):
                    self.channels[chan] = False
                
                
                t = 0
                pq = (0, None)
                
                # append to parsedqueue
                if what == "privmsg":
                    t = types.MSG
                    sender = (nick, user, host)
                    pq = (t, (sender, chan, mesg)) # the parser knows how to handle that
                elif what == "notice":
                    t = types.NOTICE
                    sender = (nick, user, host)
                    # destination is always "me"
                    pq = (t, (sender, mesg))
                elif what == "join":
                    t = types.JOIN
                    sender = (nick, user, host)
                    pq = (t, (sender, chan)) # there are no join messages
                elif what == "part":
                    t = types.PART
                    sender = (nick, user, host)
                    pq = (t, (sender, chan, mesg))
                elif what == "kick":
                    t = types.KICK
                    sender = (nick, user, host)
                    whom = mesg.split()[0]
                    mesg = mesg[len(whom):].strip()
                    mesg = mesg[1:]
                    pq = (t, (sender, whom, chan, mesg))
                elif what == "quit":
                    t = types.CQUIT
                    sender = (nick, user, host)
                    pq = (t, (sender, chan, mesg))
                elif what == "mode":
                    t = types.MODE
                    sender = (nick, user, host)
                    info = mesg.split()
                    mode = info.pop(0)
                    whom = info         # info[0] is now away
                    pq = (t, (sender, mode, chan, whom))
                elif what == "topic":
                    t = types.TOPIC
                    sender = (nick, user, host)
                    pq = (t, (sender, chan, mesg))
                else:
                    t = int(what)+1000 # to be parsed by an unknown authority
                    sender = i.split()[0]
                    raw_args = i[len(sender):].strip()
                    x = raw_args.find(":")
                    mesg = None
                    if (x > -1):
                        mesg = raw_args[x+1:] # +1 because of the :
                        raw_args2 = raw_args[:x].strip()
                    else:
                        raw_args2 = raw_args
                    
                    args = raw_args2.split()
                    args.pop(0)
                    args.pop(0) # it's always our nickname I think
                    if mesg != None:
                        args.append(mesg)
                    pq = (t, args)
                    # important: args is a list, not a tuple (because it's easier to handle)
                
                self.parsedqueue.append(pq)
                print pq
                
                #what = splices[3]
                #chan = splices[4]
                #mesg = splices[5]
                #reuse it...
            else:
                print i
        
    def connect(self):
        """Connect to the server and enter main loop"""
        if (not self.connected):
            self.socket.connect((self.user.host, self.user.port))
        #Auth.
        init = self.raw_recv() # reuse it if you like
        #nick first
        self.send ("NICK "+self.user.nick, True)
        self.send ("USER "+self.user.user+" 0 * :"+self.user.real, True)
        
        inuse = self.raw_recv()
        while (len(re.findall(':.* 433 AUTH '+self.user.nick+' :.*', inuse)) > 0):
            self.send ("NICK "+self.user.next_nick(), True)
            self.send ("USER "+self.user.user+" 0 * :"+self.user.real, True)
            inuse = self.raw_recv()
        
        # now inuse should contain a ping
        inuse2 = inuse.splitlines()
        # go through the lines
        
        for i in inuse2:
            if (i.find("PING") == 0):
                pong = i.split()[1]           # if the server sends PING :12345, we return PONG :12345, if it sends PING 12345, we return PONG 12345.
                self.socket.send("PONG "+pong+z)
            else:
                self.queue.append(i.strip())
                
        # okay. Now auth is over.
        self.connected = True
        
        # Server/Nickserv auth
        if (self.user.server_pass != ""):
            self.send("PASS "+self.user.server_pass)
            
        if (self.user.nickserv_pass != ""):
            self.send("PRIVMSG NickServ :IDENTIFY "+self.user.nickserv_pass)
        
        # Autoexec functions
        for i in self.user.autoexec:
            self.send(i) # We expect the user to enter serious values here. ;-)

        while True:
            try:
                self.recv()
                self.parse_queue()
            except KeyboardInterrupt:
                self.do_quit ("KeyboardInterrupt")
                
                
                
    # COMMANDS!
    def do_quit(self, s = "MyrkurBot quit because he was told to"):
        if (self.connected):
            self.send("QUIT :"+s)
            self.connected = False
        sys.exit()
        return None
        
    def msg (self, chan, message):
        self.send ("PRIVMSG "+chan.strip()+" :"+message)
    
    def is_joined (self, chan):
        chan = normalize(chan)
        return self.channels.has_key(chan) and self.channels[chan]
    
    def join (self, chan): #is_joined is set by the parse_queue() function
        chan = normalize(chan)
        if (not self.is_joined(chan)):
            self.send ("JOIN "+chan)

    def part (self, chan):
        chan = normalize(chan)
        if (self.is_joined(chan)):
            self.send("PART "+chan)
            
            
    # MODULES! Now it's getting funny.
    def addmod (self, name): # name is the modname, and it's saved in the database under this name.
        return None # this is not finished
        
        if self.modlist.has_key(name):
            themod = reload (self.modlist[name])
            #test if it's serious
            for i in dir(themod):
                if i == "parse": # gotta be a class.
                    if str(type(themod.parse)) == "<type 'classobj'>":
                        pass
                        
            self.modlist[name] = themod
            return True
        else:
            themod = None
            if self.moddir == ".":
                try:
                    themod = __import__(name) # we hope that works ;_)
                    
                except ImportError:
                    return False
    
    
