#!/usr/bin/env python
# -*- coding: utf-8 -*-

from time import time
from traceback import print_exc

from pyload.Api import LinkStatus
from pyload.utils.packagetools import parseNames
from pyload.utils import has_method, accumulate

from BaseThread import BaseThread

class InfoThread(BaseThread):
    def __init__(self, manager, data, pid=-1, rid=-1):
        """Constructor"""
        BaseThread.__init__(self, manager)

        self.data = data
        self.pid = pid # package id
        # [ .. (name, plugin) .. ]

        self.rid = rid #result id

        self.cache = [] #accumulated data

        self.start()

    def run(self):
        """run method"""

        plugins = accumulate(self.data)
        crypter = {}

        # filter out crypter plugins
        for name in self.m.core.pluginManager.getPlugins("crypter"):
            if name in plugins:
                crypter[name] = plugins[name]
                del plugins[name]

        #directly write to database
        if self.pid > -1:
            for pluginname, urls in plugins.iteritems():
                plugin = self.m.core.pluginManager.getPluginModule(pluginname)
                klass = self.m.core.pluginManager.getPluginClass(pluginname)
                if has_method(klass, "getInfo"):
                    self.fetchForPlugin(pluginname, klass, urls, self.updateDB)
                    self.m.core.files.save()
                elif has_method(plugin, "getInfo"):
                    self.log.debug("Deprecated .getInfo() method on module level, use classmethod instead")
                    self.fetchForPlugin(pluginname, plugin, urls, self.updateDB)
                    self.m.core.files.save()

        else: #post the results
            for name, urls in crypter:
                #attach container content
                try:
                    data = self.decrypt(name, urls)
                except:
                    print_exc()
                    self.m.log.error("Could not decrypt container.")
                    data = []

                accumulate(data, plugins)

            self.m.infoResults[self.rid] = {}

            for pluginname, urls in plugins.iteritems():
                plugin = self.m.core.pluginManager.getPlugin(pluginname, True)
                klass = getattr(plugin, pluginname)
                if has_method(klass, "getInfo"):
                    self.fetchForPlugin(pluginname, plugin, urls, self.updateResult, True)
                    #force to process cache
                    if self.cache:
                        self.updateResult(pluginname, [], True)
                elif has_method(plugin, "getInfo"):
                    self.log.debug("Deprecated .getInfo() method on module level, use staticmethod instead")
                    self.fetchForPlugin(pluginname, plugin, urls, self.updateResult, True)
                    #force to process cache
                    if self.cache:
                        self.updateResult(pluginname, [], True)
                else:
                    #generate default result
                    result = [(url, 0, 3, url) for url in urls]

                    self.updateResult(pluginname, result, True)

            self.m.infoResults[self.rid]["ALL_INFO_FETCHED"] = {}

        self.m.timestamp = time() + 5 * 60


    def updateDB(self, plugin, result):
        self.m.core.files.updateFileInfo(result, self.pid)

    def updateResult(self, plugin, result, force=False):
        #parse package name and generate result
        #accumulate results

        self.cache.extend(result)

        if len(self.cache) >= 20 or force:
            #used for package generating
            tmp = [(name, (url, LinkStatus(name, plugin, "unknown", status, int(size))))
            for name, size, status, url in self.cache]

            data = parseNames(tmp)
            result = {}
            for k, v in data.iteritems():
                for url, status in v:
                    status.packagename = k
                    result[url] = status

            self.m.setInfoResults(self.rid, result)

            self.cache = []

    def updateCache(self, plugin, result):
        self.cache.extend(result)

    def fetchForPlugin(self, pluginname, plugin, urls, cb, err=None):
        try:
            result = [] #result loaded from cache
            process = [] #urls to process
            for url in urls:
                if url in self.m.infoCache:
                    result.append(self.m.infoCache[url])
                else:
                    process.append(url)

            if result:
                self.m.log.debug("Fetched %d values from cache for %s" % (len(result), pluginname))
                cb(pluginname, result)

            if process:
                self.m.log.debug("Run Info Fetching for %s" % pluginname)
                for result in plugin.getInfo(process):
                    #result = [ .. (name, size, status, url) .. ]
                    if not type(result) == list: result = [result]

                    for res in result:
                        self.m.infoCache[res[3]] = res

                    cb(pluginname, result)

            self.m.log.debug("Finished Info Fetching for %s" % pluginname)
        except Exception, e:
            self.m.log.warning(_("Info Fetching for %(name)s failed | %(err)s") %
                               {"name": pluginname, "err": str(e)})
            if self.m.core.debug:
                print_exc()

            # generate default results
            if err:
                result = [(url, 0, 3, url) for url in urls]
                cb(pluginname, result)


    def decrypt(self, plugin, urls):
        self.m.log.debug("Pre decrypting %s" % plugin)
        klass = self.m.core.pluginManager.loadClass("crypter", plugin)

        # only decrypt files
        if has_method(klass, "decryptFile"):
            urls = klass.decrypt(urls)
            data, crypter = self.m.core.pluginManager.parseUrls(urls)
            return data

        return []