#!/usr/bin/env python
#encoding: utf8
"""
wmlunits -- tool to output information on all units in HTML

Run without arguments to see usage.
"""

# Makes things faster on 32-bit systems
try: import psyco; psyco.full()
except ImportError: pass
  
import sys, os, glob, shutil, urllib2, optparse, traceback
import subprocess, yaml

import wesnoth.wmlparser2 as wmlparser2
import unit_tree.helpers as helpers
import unit_tree.animations as animations
import unit_tree.html_output as html_output
import unit_tree.overview

def copy_images():
    print("Recolorizing pictures.")
    image_collector.copy_and_color_images(options.output)
    shutil.copy2(os.path.join(image_collector.datadir,
        "data/tools/unit_tree/style.css"), options.output)
    shutil.copy2(os.path.join(image_collector.datadir,
        "data/tools/unit_tree/menu.js"), options.output)
    for grab in [
        "http://www.wesnoth.org/mw/skins/glamdrol/headerbg.jpg",
        "http://www.wesnoth.org/mw/skins/glamdrol/wesnoth-logo.jpg",
        "http://www.wesnoth.org/mw/skins/glamdrol/navbg.png"]:
        local = os.path.join(options.output, grab[grab.rfind("/") + 1:])
        if not os.path.exists(local):
            print "Fetching", grab
            url = urllib2.urlopen(grab)
            file(local, "w").write(url.read())

def shell(com):
    #print(com)
    p = subprocess.Popen(com, stdout = subprocess.PIPE,
        stderr = subprocess.PIPE, shell = True)
    out, err = p.communicate()
    #if out: sys.stdout.write(out)
    #if err: sys.stdout.write(err)

    return p.returncode

def bash(name):
    return "'" + name.replace("'", "'\\''") + "'"

def move(f, t, name):

    if os.path.exists(f + "/" + name + ".cfg"):
        com = "mv " + f + "/" + bash(name + ".cfg") + " " + t + "/"
        shell(com)

    com = "mv " + f + "/" + bash(name) + " " + t + "/"
    return shell(com)

def list_contents():
    class Empty: pass
    local = Empty()

    mainline_eras = set()
    filename = options.list
    
    def append(info, id, define, c = None, name = None, domain = None):
        info.append({})
        info[-1]["id"]= id
        info[-1]["define"] = define
        if c:
            info[-1]["name"] = c.get_text_val("name")
        else:
            info[-1]["name"] = name
        info[-1]["units"] = "?"
        info[-1]["translations"] = {}
        
        info[-1]["translations"] = {}
        for isocode in languages:
            translation = html_output.Translation(options.transdir, isocode)
            def translate(string, domain):
                return translation.translate(string, domain)
            if c:
                info[-1]["translations"][isocode] = c.get_text_val("name",
                    translation = translate)
            else:
                info[-1]["translations"][isocode] = translate(name, domain)

    def list_eras(addon):
        eras = local.wesnoth.parser.get_all(tag = "era")
        if addon != "mainline":
            eras = [x for x in eras if not x.get_text_val("id") in mainline_eras]
        info = []
        for era in eras:
            eid = era.get_text_val("id")
            if addon == "mainline":
                mainline_eras.add(eid)
            append(info, eid, "MULTIPLAYER", c = era)
            

        return info
    
    def list_campaigns(addon):
        campaigns = local.wesnoth.parser.get_all(tag = "campaign")
        info = []

        for campaign in campaigns:
            cid = campaign.get_text_val("id")
            append(info, cid, campaign.get_text_val("define"), c = campaign)

        return info

    def parse(wml, defines):
        local.wesnoth = helpers.WesnothList(
            options.wesnoth,
            options.config_dir,
            options.data_dir,
            options.transdir)
        local.wesnoth.parser.parse_text(wml, defines)
    
    def get_version(addon):
        try:
            parser = wmlparser2.Parser(options.wesnoth, options.config_dir,
                options.data_dir, no_preprocess = False)
            parser.parse_file(options.config_dir + "/data/add-ons/" + addon + "/_info.cfg")
            for info in parser.get_all(tag = "info"):
                return info.get_text_val("version") + "*" + info.get_text_val("uploads")
        except wmlparser2.WMLError as e:
            print(e)

    try: os.makedirs(options.output + "/mainline")
    except OSError: pass

    try:
        batchlist = yaml.load(open(options.list))
    except IOError:
        batchlist = []
    
    def search(name):
        for info in batchlist:
            if info and info["name"] == name: return info
        batchlist.append({})
        batchlist[-1]["name"] = name
        return batchlist[-1]

    print("mainline")

    info = search("mainline")
    info["version"] = "mainline"
    info["parsed"] = "false"

    parse("{core}{multiplayer/eras.cfg}", "SKIP_CORE")
    info["eras"] = list_eras("mainline")

    # Fake mainline campaign to have an overview of the mainline units
    info["campaigns"] = []
    append(info["campaigns"], "mainline", "", name = "Units", domain = "wesnoth-help")
        
    if not options.addons_only:

        parse("{core}{campaigns}", "SKIP_CORE")
        info["campaigns"] += list_campaigns("mainline")

    addons = []
    if options.addons:
        addons = os.listdir(options.addons)
    for i, addon in enumerate(addons):
        if not os.path.isdir(options.addons + "/" + addon): continue
        sys.stdout.write("%4d/%4d " % (1 + i, len(addons)) + addon + " ... ")
        sys.stdout.flush()
        d = options.output + "/" + addon
        logname = d + "/error.log"
        try: os.makedirs(d)
        except OSError: pass
        move(options.addons, options.config_dir + "/data/add-ons", addon)
        try:
            info = search(addon)

            version = get_version(addon)
            if info.get("version", "") == version and info.get("parsed", False) == True:
                sys.stdout.write("up to date\n")
                continue
            info["parsed"] = False
            
            parse("{core}{multiplayer}{~add-ons}", "MULTIPLAYER,SKIP_CORE")
            info["eras"] = list_eras(addon)
            info["campaigns"] = list_campaigns(addon)
            info["version"] = version
            sys.stdout.write("ok\n")
        except wmlparser2.WMLError as e:
            ef = open(logname, "w")
            ef.write("<PARSE ERROR>\n")
            ef.write(str(e))
            ef.write("</PARSE ERROR>\n")
            ef.close()
            sys.stdout.write("failed\n")
        finally:
            move(options.config_dir + "/data/add-ons", options.addons, addon)
        
    yaml.safe_dump(batchlist, open(filename, "w"),
        encoding = "utf-8", default_flow_style = False)

def process_campaign_or_era(addon, cid, define, batchlist):
    n = 0
    
    print(addon + ": " + cid + " " + define)

    wesnoth = helpers.WesnothList(
        options.wesnoth,
        options.config_dir,
        options.data_dir,
        options.transdir)
    wesnoth.batchlist = batchlist
    wesnoth.cid = cid
    
    wesnoth.parser.parse_text("{core/units.cfg}", "NORMAL")
    wesnoth.add_units("mainline")
    
    if define == "MULTIPLAYER":
        wesnoth.parser.parse_text("{core}{multiplayer}{~add-ons}", "MULTIPLAYER,SKIP_CORE")
        wesnoth.add_units(cid)
    else:
        if addon == "mainline":
            if cid != "mainline":
                wesnoth.parser.parse_text("{campaigns}", "NORMAL," + define)
                wesnoth.add_units(cid)
        else:
            wesnoth.parser.parse_text("{core}{~add-ons}", "SKIP_CORE," + define)
            wesnoth.add_units(cid)
    
    if addon == "mainline" and cid == "mainline":
        write_animation_statistics(wesnoth)

    wesnoth.add_binary_paths(addon, image_collector)

    if define == "MULTIPLAYER":
        eras = wesnoth.parser.get_all(tag = "era")
        for era in eras:
            wesnoth.add_era(era)
        wesnoth.find_unit_factions()
    else:
        campaigns = wesnoth.parser.get_all(tag = "campaign")
        for campaign in campaigns:
            wesnoth.add_campaign(campaign)

    wesnoth.add_languages(languages)
    wesnoth.add_terrains()
    wesnoth.check_units()

    for isocode in languages:
        
        if addon != "mainline" and isocode != "en_US": continue
        
        if define == "MULTIPLAYER":
            for era in wesnoth.era_lookup.values():
                if era.get_text_val("id") == cid:
                    n = html_output.generate_era_report(addon, isocode, era, wesnoth)
                    break
        else:
            if cid == "mainline":
                n = html_output.generate_campaign_report(addon, isocode, None, wesnoth)

            for campaign in wesnoth.campaign_lookup.values():
                if campaign.get_text_val("id") == cid:
                    n = html_output.generate_campaign_report(addon, isocode, campaign, wesnoth)
                    break
        
        html_output.generate_single_unit_reports(addon, isocode, wesnoth)

    return n

def batch_process():
    batchlist = yaml.load(open(options.batch))
    for addon in batchlist:
        name = addon["name"]
        
        if not options.reparse and addon.get("parsed", False) == True: continue
        
        if name == "mainline":
            worked = True
        else:
            worked = (move(options.addons, options.config_dir + "/data/add-ons", name) == 0)
        d = options.output + "/" + name
        try: os.makedirs(d)
        except OSError: pass
        logname = d + "/error.log"
        
        def err(mess):
            ef = open(logname, "a")
            ef.write(str(mess))
            ef.close()

        html_output.write_error = err
        
        try:
            if not worked:
                print(name + " not found")
                continue
            
            for era in addon.get("eras", []):
                eid = era["id"]
                n = process_campaign_or_era(name, eid, era["define"], batchlist)
                era["units"] = n

            for campaign in addon.get("campaigns", []):
                cid = campaign["id"]
                if cid == None: cid = campaign["define"]
                if cid == None: cid = name
                n = process_campaign_or_era(name, cid, campaign["define"], batchlist)
                campaign["units"] = n
            
        except wmlparser2.WMLError as e:
            ef = open(logname, "a")
            ef.write("<WML ERROR>\n")
            ef.write(str(e))
            ef.write("</WML ERROR>\n")
            ef.close()
            print("    " + name + " failed")
        except Exception as e:
            traceback.print_exc()
            print("    " + name + " failed")
            ef = open(logname, "a")
            ef.write("<INTERNAL ERROR>\n")
            ef.write("please report as bug")
            ef.write("</INTERNAL ERROR>\n")
            ef.close()
        finally:
            if name != "mainline":
                move(options.config_dir + "/data/add-ons", options.addons, name)
                
        addon["parsed"] = True

        yaml.safe_dump(batchlist, open(options.batch, "w"),
            encoding = "utf-8", default_flow_style = False)
        
        try:
            unit_tree.overview.write_addon_overview(os.path.join(options.output,
                name), addon)
        except Exception as e:
            pass

    html_output.html_postprocess_all(batchlist)

def write_unit_ids_UNUSED():
    # Write a list with all unit ids, just for fun.
    uids = wesnoth.unit_lookup.keys()
    def by_race(u1, u2):
        r = cmp(wesnoth.unit_lookup[u1].rid,
            wesnoth.unit_lookup[u2].rid)
        if r == 0: r = cmp(u1, u2)
        return r
    uids.sort(by_race)
    race = None
    f = MyFile(os.path.join(options.output, "uids.html"), "w")
    f.write("<html><body>")
    for uid in uids:
        u = wesnoth.unit_lookup[uid]
        if u.rid != race:
            if race != None: f.write("</ul>")
            f.write("<p>%s</p>\n" % (u.rid,))
            f.write("<ul>")
            race = u.rid
        f.write("<li>%s</li>\n" % (uid, ))
    f.write("</ul>")
    f.write("</body></html>")
    f.close()

def write_animation_statistics(wesnoth):
    # Write animation statistics
    f = html_output.MyFile(os.path.join(options.output, "animations.html"), "w")
    animations.write_table(f, wesnoth)
    f.close()

if __name__ == '__main__':

    # We change the process name to "wmlunits"
    try:
        import ctypes
        libc = ctypes.CDLL("libc.so.6")
        libc.prctl(15, "wmlunits", 0, 0, 0)
    except: # oh well...
        pass

    global options
    global image_collector

    op = optparse.OptionParser()
    op.add_option("-C", "--config-dir",
        help="Specify the user configuration dir (wesnoth --config-path).")
    op.add_option("-D", "--data-dir",
        help="Specify the wesnoth data dir (wesnoth --path).")
    op.add_option("-l", "--language", default="all",
        help="Specify a language to use. Else outputs is produced for all languages.")
    op.add_option("-o", "--output",
        help="Specify the output directory.")
    op.add_option("-n", "--nocopy", action="store_true",
        help="No copying of files. By default all images are copied to the output dir.")
    op.add_option("-w", "--wesnoth",
        help="Specify the wesnoth executable to use. Whatever data " +
        "and config paths that executable is configured for will be " +
        "used to find game files and addons.")
    op.add_option("-t", "--transdir",
        help="Specify the directory with gettext message catalogues. " +
        "Defaults to ./translations.", default="translations")
    op.add_option("-r", "--reparse", action="store_true",
        help="Reparse everything.")
    op.add_option("-a", "--addons",
        help="Specify path to a folder with all addons. This should be " +
        "outside the user config folder.")
    op.add_option("-L", "--list",
        help = "List available eras and campaigns.")
    op.add_option("-B", "--batch",
        help = "Batch process the given list.")
    op.add_option("-A", "--addons-only", action = "store_true",
        help = "Do only process addons (for debugging).")
    op.add_option("-v", "--verbose", action = "store_true")
    options, args = op.parse_args()
    
    html_output.options = options
    helpers.options = options
    unit_tree.overview.options = options

    if not options.output:
        op.print_help()
        sys.exit(-1)

    options.output = os.path.expanduser(options.output)

    if not options.wesnoth:
        options.wesnoth = "wesnoth"

    if not options.transdir:
        options.transdir = os.getcwd()

    image_collector = helpers.ImageCollector(options.wesnoth,
        options.config_dir, options.data_dir)
    html_output.image_collector = image_collector

    # Generate output dir.
    if not os.path.isdir(options.output):
        os.mkdir(options.output)
        
    if options.language == "all":
        languages = []
        parser = wmlparser2.Parser(options.wesnoth, options.config_dir,
            options.data_dir, no_preprocess = False)
        parser.parse_text("{languages}")

        for locale in parser.get_all(tag="locale"):
            isocode = locale.get_text_val("locale")
            name = locale.get_text_val("name")
            if isocode == "ang_GB":
                continue
            languages.append(isocode)
        languages.sort()
    else:
        languages = options.language.split(",")
        
    if options.list:
        list_contents()
    
    if options.batch:
        batch_process()
        
        unit_tree.overview.main(options.output)

        if not options.nocopy:
            copy_images()
    
    html_output.write_index(options.output)

