# -*- coding: utf-8 -*-

"""
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License,
    or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, see <http://www.gnu.org/licenses/>.
    
    @author: RaNaN
    @author: jeix
    @interface-version: 0.2
"""

from select import select
import socket
import sys
from threading import Thread
import time
from time import sleep
from traceback import print_exc

from module.plugins.Hook import Hook

class IRCInterface(Thread, Hook):
    __name__ = "IRCInterface"
    __version__ = "0.1"
    __description__ = """connect to irc and let owner perform different tasks"""
    __config__ = [("activated", "bool", "Activated", "False"),
        ("host", "str", "IRC-Server Address", "Enter your server here!"),
        ("port", "int", "IRC-Server Port", "6667"),
        ("ident", "str", "Clients ident", "pyload-irc"),
        ("realname", "str", "Realname", "pyload-irc"),
        ("nick", "str", "Nickname the Client will take", "pyLoad-IRC"),
        ("owner", "str", "Nickname the Client will accept commands from", "Enter your nick here!"),
        ("info_file", "bool", "Inform about every file finished", "False"),
        ("info_pack", "bool", "Inform about every package finished", "True")]
    __author_name__ = ("Jeix")
    __author_mail__ = ("Jeix@hasnomail.com")
    
    def __init__(self, core):
        Thread.__init__(self)
        Hook.__init__(self, core)
        self.setDaemon(True)
        
    def coreReady(self):
        self.new_package = {}
        
        self.abort = False
        
        self.links_added = 0

        self.start()
        
        
    def packageFinished(self, pypack):
        try:
            if self.getConfig("info_pack"):
                self.response(_("Package finished: %s") % pypack.name)
        except:
            pass
        
    def downloadFinished(self, pyfile):
        try:
            if self.getConfig("info_file"):
                self.response(_("Download finished: %s @ %s") % (pyfile.name, pyfile.pluginname) )
        except:
            pass
             
    def run(self):
        # connect to IRC etc.
        self.sock = socket.socket()
        host = self.getConfig("host")
        self.sock.connect((host, self.getConfig("port")))
        nick = self.getConfig("nick")
        self.sock.send("NICK %s\r\n" % nick)
        self.sock.send("USER %s %s bla :%s\r\n" % (nick, host, nick))
        for t in self.getConfig("owner").split():
            if t.strip().startswith("#"):
                self.sock.send("JOIN %s\r\n" % t.strip())
        self.log.info("pyLoadIRC: Connected to %s!" % host)
        self.log.info("pyLoadIRC: Switching to listening mode!")
        try:        
            self.main_loop()
            
        except IRCError, ex:
            self.sock.send("QUIT :byebye\r\n")
            print_exc()
            self.sock.close()

            
    def main_loop(self):
        readbuffer = ""
        while True:
            sleep(1)
            fdset = select([self.sock], [], [], 0)
            if self.sock not in fdset[0]:
                continue
            
            if self.abort:
                raise IRCError("quit")
            
            readbuffer += self.sock.recv(1024)
            temp = readbuffer.split("\n")
            readbuffer = temp.pop()

            for line in temp:
                line  = line.rstrip()
                first = line.split()

                if(first[0] == "PING"):
                    self.sock.send("PING %s\r\n" % first[1])
                    
                if first[0] == "ERROR":
                    raise IRCError(line)
                    
                msg = line.split(None, 3)
                if len(msg) < 4:
                    continue
                    
                msg = {
                    "origin":msg[0][1:],
                    "action":msg[1],
                    "target":msg[2],
                    "text":msg[3][1:]
                }
                
                self.handle_events(msg)
        
        
    def handle_events(self, msg):
        if not msg["origin"].split("!", 1)[0] in self.getConfig("owner").split():
            return
            
        if msg["target"].split("!", 1)[0] != self.getConfig("nick"):
            return
            
        if msg["action"] != "PRIVMSG":
            return
            
        # HANDLE CTCP ANTI FLOOD/BOT PROTECTION
        if msg["text"] == "\x01VERSION\x01":
            self.log.debug("Sending CTCP VERSION.")
            self.sock.send("NOTICE %s :%s\r\n" % (msg['origin'], "pyLoad! IRC Interface"))
            return
        elif msg["text"] == "\x01TIME\x01":
            self.log.debug("Sending CTCP TIME.")
            self.sock.send("NOTICE %s :%d\r\n" % (msg['origin'], time.time()))
            return
        elif msg["text"] == "\x01LAG\x01":
            self.log.debug("Received CTCP LAG.") # don't know how to answer
            return
         
        trigger = "pass"
        args = None
        
        temp = msg["text"].split()
        trigger = temp[0]
        if len(temp) > 1:
            args = temp[1:]
        
        handler = getattr(self, "event_%s" % trigger, self.event_pass)
        try:
            res = handler(args)
            for line in res:
                self.response(line, msg["origin"])
        except Exception, e:
            self.log.error("pyLoadIRC: "+ repr(e))
        
        
    def response(self, msg, origin=""):
        if origin == "":
            for t in self.getConfig("owner").split():
                self.sock.send("PRIVMSG %s :%s\r\n" % (t.strip(), msg))
        else:
            self.sock.send("PRIVMSG %s :%s\r\n" % (origin.split("!", 1)[0], msg))
        
        
#### Events
    def event_pass(self, args):
        return []
        
    def event_status(self, args):
        downloads = self.core.status_downloads()
        if len(downloads) < 1:
            return ["INFO: There are no active downloads currently."]
            
        lines = []
        lines.append("ID - Name - Status - Speed - ETA - Progress")
        for data in downloads:
            lines.append("#%d - %s - %s - %s - %s - %s" %
                     (
                     data['id'],
                     data['name'],
                     data['status'],
                     "%d kb/s" % int(data['speed']),
                     "%d min" % int(data['eta'] / 60),
                     "%d/%d MB (%d%%)" % ((data['size']-data['kbleft']) / 1024, data['size'] / 1024, data['percent'])
                     )
                     )
        return lines
            
    def event_queue(self, args):
        # just forward for now
        return self.event_status(args)
        
    def event_collector(self, args):
        ps = self.core.get_collector()
        if len(ps) == 0:
            return ["INFO: No packages in collector!"]
        
        lines = []
        for packdata in ps:
            lines.append('PACKAGE: Package "%s" with id #%d' % (packdata['package_name'], packdata['id']))
            for fileid in self.core.get_package_files(packdata['id']):
                fileinfo = self.core.get_file_info(fileid)
                lines.append('#%d FILE: %s (#%d)' % (packdata['id'], fileinfo["filename"], fileinfo["id"]))
                
        return lines
        
    def event_links(self, args):
        fids = self.core.get_files()
        if len(fids) == 0:
            return ["INFO: No links."]
            
        lines = []
        for fid in fids:
            info = self.core.get_file_info(fid)
            lines.append('LINK #%d: %s [%s]' % (fid, info["filename"], info["status_type"]))
            
        return lines
        
    def event_packages(self, args):
        pids = self.core.get_packages()
        if len(pids) == 0:
            return ["INFO: No packages."]
            
        lines = []
        for pid in pids:
            data = self.core.get_package_data(pid)
            lines.append('PACKAGE #%d: %s (%d links)' % (pid, data["package_name"], len(self.core.get_package_files(pid))))
            
        return lines
        
    def event_info(self, args):
        if not args:
            return ['ERROR: Use info like this: info <id>']
            
        info = self.core.get_file_info(int(args[0]))
        return ['LINK #%d: %s (%d) [%s bytes]' % (info['id'], info['filename'], info['size'], info['status_type'])]
        
    def event_packinfo(self, args):
        if not args:
            return ['ERROR: Use packinfo like this: packinfo <id>']
            
        lines = []
        packdata = self.core.get_package_data(int(args[0]))
        lines.append('PACKAGE: Package "%s" with id #%d' % (packdata['package_name'], packdata['id']))
        for fileid in self.core.get_package_files(packdata['id']):
            fileinfo = self.core.get_file_info(fileid)
            lines.append('#%d LINK: %s (#%d)' % (packdata['id'], fileinfo["filename"], fileinfo["id"]))
            
        return lines
    
    def event_start(self, args):
        if not args:
            count = 0
            for packdata in self.core.get_collector_packages():
                self.core.push_package_2_queue(packdata['id'])
                count += 1

            return ["INFO: %d downloads started." % count]
            
        lines = []
        for val in args:
            id = int(val.strip())
            self.core.push_package_2_queue(id)
            lines.append("INFO: Starting download #%d" % id)
        
        return lines
        
    def event_stop(self, args):
        if not args:
            self.core.stop_downloads()
            return ["INFO: All downloads stopped."]
            
        lines = []
        for val in args:
            id = int(val.strip())
            self.core.stop_download("", id)
            lines.append("INFO: Download #%d stopped." % id)
            
        return lines
        
    def event_add(self, args):
        if len(args) != 2:
            return ['ERROR: Add links like this: "add <package|id> <link>". '\
                     'This will add the link <link> to to the package <package> / the package with id <id>!']
            
        def get_pack_id(pack):
            if pack.isdigit():
                pack = int(pack)
                for packdata in self.core.get_collector_packages():
                    if packdata['id'] == pack:
                        return pack
                return -1
                    
            for packdata in self.core.get_collector_packages():
                if packdata['package_name'] == pack:
                    return packdata['id']
            return -1
            
            
        pack = args[0].strip()
        link = args[1].strip()
        count_added = 0
        count_failed = 0
                         
        # verify that we have a valid link
        if not self.core.is_valid_link(link):
            return ["ERROR: Your specified link is not supported by pyLoad."]
                
        # get a valid package id (create new package if it doesn't exist)
        pack_id = get_pack_id(pack)
        if pack_id == -1:
            pack_id = self.core.new_package(pack)

        # move link into package
        fid = self.core.add_links_to_package(pack_id, [link])        
        return ["INFO: Added %s to Package %s [#%d]" % (link, pack, pack_id)]
        
    def event_del(self, args):
        if len(args) < 2:
            return ["ERROR: Use del command like this: del -p|-l <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"]
            
        if args[0] == "-p":
            ret = self.core.del_packages(map(int, args[1:]))
            return ["INFO: Deleted %d packages!" % ret]
            
        elif args[0] == "-l":
            ret = self.core.del_links(map(int, args[1:]))
            return ["INFO: Deleted %d links!" % ret]

        else:
            return ["ERROR: Use del command like this: del <-p|-l> <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"]
            
    def event_help(self, args):
        lines = []
        lines.append("The following commands are available:")
        lines.append("add <package|packid> <link> Adds link to package. (creates new package if it does not exist)")
        lines.append("collector                   Shows all packages in collector")
        lines.append("del -p|-l <id> [...]        Deletes all packages|links with the ids specified")
        lines.append("info <id>                   Shows info of the link with id <id>")
        lines.append("help                        Shows this help file")
        lines.append("links                       Shows all links in pyload")
        lines.append("packages                    Shows all packages in pyload")
        lines.append("packinfo <id>               Shows info of the package with id <id>")
        lines.append("queue                       Shows info about the queue")
        lines.append("start  [<id>...]            Starts the package with id <id> or all packages if no id is given")
        lines.append("status                      Show general download status")
        lines.append("stop [<id>...]              Stops the package with id <id> or all packages if no id is given")
        return lines
        
        
class IRCError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)