#!/usr/bin/python
__requires__ = 'TurboGears[future]'
import pkg_resources
pkg_resources.require("TurboGears")

import datetime as dt
import errno
from inspect import isclass
from itertools import izip
import logging
import optparse
import os
import sys
import time
import warnings
with warnings.catch_warnings():
    warnings.filterwarnings("ignore", category=DeprecationWarning) 
    import turbogears
import turbogears.util as tg_util
from turbogears import config
from turbogears.view import engines
from turbogears.database import PackageHub
sys.path.append("/usr/share/mirrormanager/server")
from mirrormanager.model import Product, publiclist_arches, publiclist_hosts
from mirrormanager.lib import project_template

hub = __connection__ = None
logger = None

def setup_logger():
    fmt = "%(asctime)s %(message)s"
    formatter = logging.Formatter(fmt=fmt)
    formatter.converter = time.gmtime
    logger = logging.getLogger('generate-publiclist')
    handler = logging.handlers.WatchedFileHandler(options.logfile, "a+b")
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    logger.setLevel(logging.DEBUG)
    return logger

def _process_output(output, template, format, engine):
    """Produces final output form from the data returned from a
    controller method.

    @param format: format of desired output (html or json)
    @param output: the output returned by the controller
    @param template: HTML template to use
    @param engine: output engine to use e.g. kid

    Written for Smolt, used by permission.
    """

    if isinstance(output, dict):
        from turbogears.widgets import js_location

        css = tg_util.setlike()
        js = dict(izip(js_location, iter(tg_util.setlike, None)))
        include_widgets = {}
        include_widgets_lst = config.get("tg.include_widgets", [])

        for i in include_widgets_lst:
            widget = tg_util.load_class(i)
            if isclass(widget):
                widget = widget()
            include_widgets["tg_%s" % i.split(".")[-1]] = widget
            for script in widget.retrieve_javascript():
                if hasattr(script, "location"):
                    js[script.location].add(script)
                else:
                    js[js_location.head].add(script)
            css.add_all(widget.retrieve_css())

        for value in output.itervalues():
            if hasattr(value, "retrieve_css"):
                retrieve = getattr(value, "retrieve_css")
                if callable(retrieve):
                    css.add_all(value.retrieve_css())
            if hasattr(value, "retrieve_javascript"):
                retrieve = getattr(value, "retrieve_javascript")
                if callable(retrieve):
                    for script in value.retrieve_javascript():
                        if hasattr(script, "location"):
                            js[script.location].add(script)
                        else:
                            js[js_location.head].add(script)
        output.update(include_widgets)
        output["tg_css"] = css

        for l in iter(js_location):
            output["tg_js_%s" % str(l)] = js[l]

        output["tg_flash"] = output.get("tg_flash")

        return engine.render(output, format=format, template=template)

class ProductVersionArchMatrix:
    """ {product: {version: {arch: [hosts] }}} """

    def __init__(self):
        self.data = {}

    def set_pva(self, product, version, arch, value):
        if product is None or version is None or arch is None:
            return
        if product not in self.data:
            self.data[product] = {}
        if version not in self.data[product]:
            self.data[product][version] = {}
        self.data[product][version][arch] = value


    def get_pva(self, product, version, arch):
        if product is None or version is None or arch is None:
            raise KeyError
        try:
            result = self.data[product][version][arch]
        except KeyError:
            result = []
        return result

    def fill(self):
        arches = publiclist_arches()
        self.data = {}
        for p in list(Product.select()):
            for v in list(p.versions):
                if not v.display:
                    continue
                for a in arches:
                    hosts = publiclist_hosts(productname=p.name, vername=v.name, archname=a.name)
                    self.set_pva(p.name, v.name, a.name, hosts)
                    msg = "filled %s/%s/%s with %s hosts" % (p.name, v.name, a.name, len(hosts))
                    logger.info(msg)



def make_pva(productname=None, vername=None, archname=None):
    if productname is None and vername is None and archname is None:
        return ''
    elif vername is None and archname is None:
        return productname
    elif archname is None:
        return "%s/%s" % (productname, vername)
    return "%s/%s/%s" % (productname, vername, archname)

def generate_all():
    turbogears.view.load_engines()
    engine = engines.get('kid', None)
    pvaMatrix = ProductVersionArchMatrix()
    pvaMatrix.fill()
    products = list(Product.select().orderBy('name'))
    arches = publiclist_arches()
    email  = config.get('mirrormanager.report_problems_to_email', 'webmaster')
    utcnow = dt.datetime.utcnow()
    projectname = config.get('mirrormanager.projectname','Fedora')
    template = engine.load_template(project_template('publiclist'))
    generate_one_html(engine, template, pvaMatrix, projectname, products,
                      arches, email, utcnow)
    for p in products:
        generate_one_html(engine, template, pvaMatrix, projectname, products,
                          arches, email, utcnow, productname=p.name)
        
        for v in p.versions:
            if not v.display:
                continue
            generate_one_html(engine, template, pvaMatrix, projectname, products,
                              arches, email, utcnow, productname=p.name, vername=v.name)


            for a in arches:
                generate_one_html(engine, template, pvaMatrix, projectname, products,
                                  arches, email, utcnow,
                                  productname=p.name, vername=v.name, archname=a.name)


def generate_one_html(engine, template, pvaMatrix, projectname, products, arches,
                      email, utcnow, productname=None, vername=None, archname=None):
    try:
        hosts = pvaMatrix.get_pva(productname, vername, archname)
    except KeyError:
        hosts = publiclist_hosts(productname=productname, vername=vername, archname=archname)

    if len(hosts) == 0:
        return

    pva = make_pva(productname, vername, archname)
    title = "%s %s Public Active Mirrors" % (projectname, pva)
    msg = "making %s, %d hosts" % (pva, len(hosts))
    logger.info(msg)

    output_dir = os.path.join(options.output, pva)
    try:
        os.makedirs(output_dir)
    except OSError, e:
        if e.errno != errno.EEXIST:
            raise

    bandwidth = 0
    for h in hosts:
        bandwidth += h.bandwidth_int
    bandwidth = bandwidth / 1024 # convert to Gb

    template_data = dict(page_title = title,
                         hosts = hosts, numhosts = len(hosts),
                         bandwidth = bandwidth,
                         pvaMatrix = pvaMatrix,
                         pva = pva,
                         products = products,
                         arches = arches,
                         report_problems_to_email = email,
                         utcnow = utcnow)

    out_html = _process_output(template_data, template = template,
                               format = 'html', engine = engine)

    fname = "index.html"
    f = open(os.path.join(output_dir, fname), "w")
    f.write(out_html)
    f.close()

options=None

def main():
    global options
    parser = optparse.OptionParser(usage=sys.argv[0] + " [options]")
    parser.add_option("-c", "--config",
                      dest="config", default='dev.cfg',
                      help="TurboGears config file to use")
    parser.add_option("--logfile", type="string", metavar="FILE",
                      dest="logfile",
                      default='/var/log/mirrormanager/generate-publiclist.log',
                      help="write logfile to FILE")
    parser.add_option("-o", "--output",
                      metavar="DIR", dest="output", action="store", type="string",
                      help="write output to DIR")

    (options, args) = parser.parse_args()
    if options.output is None:
        parser.print_help()
        sys.exit(1)

    turbogears.update_config(configfile=options.config,
                             modulename="mirrormanager.config")
    global hub
    global __connection__
    hub = PackageHub("mirrormanager")
    __connection__ = hub

    global logger
    logger = setup_logger()
    
    generate_all()


if __name__ == "__main__":
    sys.exit(main())
        
