###########################################################################
# This file provides maintenance on the various language files
# 1. Create new "xx/cards_xx.json" files that have entries ordered as:
#     a. the card_tag entries in "cards_db.json"
#     b. the group_tag entries as found in "cards_db.json"
#     c. the super group entries (grouping across all expansions"
#     d. any unused entries existing in the file (assumed to be work in progress)
#
# 2. Create new "sets_db.json" and "xx/cards_xx.json" with entries sorted alphabetically
#
# All output is in the designated output directory.  Original files are not overwritten.
###########################################################################

import os
import os.path
import io
import codecs
import json
from shutil import copyfile
import argparse
import collections

LANGUAGE_DEFAULT = "en_us"  # default language, which takes priority
LANGUAGE_XX = "xx"  # language for starting a translation


def get_lang_dirs(path):
    # Find all valid languages.
    languages = []
    for name in os.listdir(path):
        dir_path = os.path.join(path, name)
        if os.path.isdir(dir_path):
            cards_file = os.path.join(dir_path, "cards_" + name + ".json")
            sets_file = os.path.join(dir_path, "sets_" + name + ".json")
            if os.path.isfile(cards_file) and os.path.isfile(sets_file):
                languages.append(name)
    return languages


def get_json_data(json_file_path):
    print(("reading {}".format(json_file_path)))
    # Read in the json from the specified file
    with codecs.open(json_file_path, "r", "utf-8") as json_file:
        data = json.load(json_file)
    assert data, "Could not load json at: '%r' " % json_file_path
    return data


def json_dict_entry(entry, separator=""):
    #  Return a nicely formated json dict entry.
    #  It does not include the enclosing {} and removes trailing white space
    json_data = json.dumps(entry, indent=4, ensure_ascii=False, sort_keys=True)
    json_data = json_data.strip(
        "{}"
    ).rstrip()  # Remove outer{} and then trailing whitespace
    return separator + json_data


# Multikey sort
# see: http://stackoverflow.com/questions/1143671/python-sorting-list-of-dictionaries-by-multiple-keys
def multikeysort(items, columns):
    from operator import itemgetter

    for c in columns[::-1]:
        items = sorted(items, key=itemgetter(c))
    return items


def main(args):
    ###########################################################################
    # Get all the languages, and place the default language first in the list
    ###########################################################################
    languages = get_lang_dirs(args.card_db_dir)
    languages.remove(LANGUAGE_DEFAULT)
    languages.insert(0, LANGUAGE_DEFAULT)
    if LANGUAGE_XX not in languages:
        languages.append(LANGUAGE_XX)
    print("Languages:")
    print(languages)
    print()

    ###########################################################################
    #  Make sure the directories exist to hold the output
    ###########################################################################

    #  main output directory
    if not os.path.exists(args.output_dir):
        os.makedirs(args.output_dir)

    #  each language directory
    for lang in languages:
        #  Make sure the directory is there to hold the file
        lang_dir = os.path.join(args.output_dir, lang)
        if not os.path.exists(lang_dir):
            os.makedirs(lang_dir)

    ###########################################################################
    #  Get the types_db information
    #  Store in a list in the order found in types[]. Ordered by card_type
    #  1. card_tags, 2. group_tags, 3. super groups
    ###########################################################################
    type_parts = set()

    # Get the card data
    type_data = get_json_data(os.path.join(args.card_db_dir, "types_db.json"))

    # Sort the cards by cardset_tags, then card_tag
    sorted_type_data = multikeysort(type_data, ["card_type"])

    with io.open(
        os.path.join(args.output_dir, "types_db.json"), "w", encoding="utf-8"
    ) as f:
        json.dump(sorted_type_data, f, indent=4, ensure_ascii=False)

    type_parts = list(set().union(*[set(t["card_type"]) for t in sorted_type_data]))
    type_parts.sort()
    print("Unique Types:")
    print(type_parts)
    print()

    ###########################################################################
    #  Get the labels_db information
    #  Store in a list in the order found.
    ###########################################################################
    all_labels = []

    # Get the card data
    label_data = get_json_data(os.path.join(args.card_db_dir, "labels_db.json"))

    all_labels = list(set().union(*[set(label["names"]) for label in label_data]))

    with io.open(
        os.path.join(args.output_dir, "labels_db.json"), "w", encoding="utf-8"
    ) as f:
        json.dump(label_data, f, indent=4, ensure_ascii=False)

    all_labels.sort()
    print("Labels: ")
    print(all_labels)
    print()
    ###########################################################################
    # Fix up all the xx/types_xx.json files
    # Place entries in alphabetical order
    # If entries don't exist:
    #    If the default language, set from information in the "types_db.json" file,
    #    If not the default language, set based on information from the default language.
    # Lastly, keep any extra entries that are not currently used, just in case needed
    #    in the future or is a work in progress.
    ###########################################################################
    for lang in languages:
        lang_file = "types_" + lang + ".json"
        fname = os.path.join(args.card_db_dir, lang, lang_file)
        if os.path.isfile(fname):
            lang_type_data = get_json_data(fname)
        else:
            lang_type_data = {}

        for t in sorted(type_parts):
            if t not in lang_type_data:
                if lang == LANGUAGE_DEFAULT:
                    lang_type_data[t] = t
                    lang_type_default = lang_type_data
                else:
                    lang_type_data[t] = lang_type_default[t]

        with io.open(
            os.path.join(args.output_dir, lang, lang_file), "w", encoding="utf-8"
        ) as f:
            json.dump(lang_type_data, f, indent=4, ensure_ascii=False)

        if lang == LANGUAGE_DEFAULT:
            lang_type_default = lang_type_data  # Keep for later languages

    ###########################################################################
    #  Get the cards_db information
    #  Store in a list in the order found in cards[]. Ordered as follows:
    #  1. card_tags, 2. group_tags, 3. super groups
    ###########################################################################

    # Get the card data
    card_data = get_json_data(os.path.join(args.card_db_dir, "cards_db.json"))

    cards = set(card["card_tag"] for card in card_data)
    groups = set(card["group_tag"] for card in card_data if "group_tag" in card)
    super_groups = set(["events", "landmarks"])

    # Sort the cardset_tags
    for card in card_data:
        card["cardset_tags"].sort()
        # But put all the base cards together by moving to front of the list
        if "base" in card["cardset_tags"]:
            card["cardset_tags"].remove("base")
            card["cardset_tags"].insert(0, "base")

    # Sort the cards by cardset_tags, then card_tag
    sorted_card_data = multikeysort(card_data, ["cardset_tags", "card_tag"])

    with io.open(
        os.path.join(args.output_dir, "cards_db.json"), "w", encoding="utf-8"
    ) as lang_out:
        json.dump(sorted_card_data, lang_out, indent=4, ensure_ascii=False)

    # maintain the sorted order, but expand with groups and super_groups
    cards = [c["card_tag"] for c in sorted_card_data]
    cards.extend(sorted(groups))
    cards.extend(sorted(super_groups))

    print("Cards:")
    print(cards)
    print()

    ###########################################################################
    # Fix up all the cards_xx.json files
    # Place entries in the same order as given in "cards_db.json".
    # If entries don't exist:
    #    If the default language, set base on information in the "cards_db.json" file,
    #    If not the default language, set based on information from the default language.
    # Lastly, keep any extra entries that are not currently used, just in case needed
    #    in the future or is a work in progress.
    ###########################################################################
    for lang in languages:

        #  contruct the cards json file name
        lang_file = "cards_" + lang + ".json"
        fname = os.path.join(args.card_db_dir, lang, lang_file)
        if os.path.isfile(fname):
            lang_data = get_json_data(fname)
        else:
            lang_data = {}

        sorted_lang_data = collections.OrderedDict()
        fields = ["description", "extra", "name"]
        for card_tag in cards:
            lang_card = lang_data.get(card_tag)
            # print(f'looking at {card_tag}: {lang_card}')
            if not lang_card or lang == LANGUAGE_XX:
                #  Card is missing, need to add it
                lang_card = {}
                if lang == LANGUAGE_DEFAULT:
                    #  Default language gets bare minimum.  Really need to add by hand.
                    lang_card["extra"] = ""
                    lang_card["name"] = card
                    lang_card["description"] = ""
                    lang_card["untranslated"] = fields
                    lang_default = lang_data
                else:
                    #  All other languages should get the default languages' text
                    lang_card["extra"] = lang_default[card_tag]["extra"]
                    lang_card["name"] = lang_default[card_tag]["name"]
                    lang_card["description"] = lang_default[card_tag]["description"]
                    lang_card["untranslated"] = fields
            else:
                # Card exists, figure out what needs updating (don't update default language)
                if lang != LANGUAGE_DEFAULT:
                    if "untranslated" in lang_card:
                        #  Has an 'untranslated' field.  Process accordingly
                        if not lang_card["untranslated"]:
                            #  It is empty, so just remove it
                            del lang_card["untranslated"]
                        else:
                            #  If a field remains untranslated, then replace with the default languages copy
                            for field in fields:
                                if field in lang_card["untranslated"]:
                                    lang_card[field] = lang_default[card_tag][field]
                    else:
                        #  Need to create the 'untranslated' field and update based upon existing fields
                        untranslated = []
                        for field in fields:
                            if field not in lang_data[card_tag]:
                                lang_card[field] = lang_default[card_tag][field]
                                untranslated.append(field)
                        if untranslated:
                            #  only add if something is still needing translation
                            lang_card["untranslated"] = untranslated
            lang_card["used"] = True
            sorted_lang_data[card_tag] = lang_card
        unused = [c for c in lang_data.values() if "used" not in c]
        print(
            f'unused in {lang}: {len(unused)}, used: {len([c for c in lang_data.values() if "used" in c])}'
        )
        print([c["name"] for c in unused])
        # Now keep any unused values just in case needed in the future
        for card_tag in lang_data:
            lang_card = lang_data.get(card_tag)
            if "used" not in lang_card:
                if lang != LANGUAGE_XX:
                    lang_card["untranslated"] = [
                        "Note: This card is currently not used."
                    ]
                sorted_lang_data[card_tag] = lang_card
            else:
                del lang_card["used"]

        #  Process the file
        with io.open(
            os.path.join(args.output_dir, lang, lang_file), "w", encoding="utf-8"
        ) as lang_out:
            json.dump(sorted_lang_data, lang_out, indent=4, ensure_ascii=False)

        if lang == LANGUAGE_DEFAULT:
            lang_default = lang_data  # Keep for later languages

    ###########################################################################
    # Fix up the sets_db.json file
    # Place entries in alphabetical order
    ###########################################################################
    lang_file = "sets_db.json"
    set_data = get_json_data(os.path.join(args.card_db_dir, lang_file))

    with io.open(
        os.path.join(args.output_dir, lang_file), "w", encoding="utf-8"
    ) as lang_out:
        json.dump(set_data, lang_out, sort_keys=True, indent=4, ensure_ascii=False)

    print("Sets:")
    print(set(set_data))
    print()

    ###########################################################################
    # Fix up all the xx/sets_xx.json files
    # Place entries in alphabetical order
    # If entries don't exist:
    #    If the default language, set from information in the "sets_db.json" file,
    #    If not the default language, set based on information from the default language.
    ###########################################################################
    for lang in languages:
        lang_file = "sets_" + lang + ".json"
        fname = os.path.join(args.card_db_dir, lang, lang_file)
        if os.path.isfile(fname):
            lang_set_data = get_json_data(fname)
        else:
            lang_set_data = {}

        for s in sorted(set_data):
            if s not in lang_set_data:
                lang_set_data[s] = {}
                if lang == LANGUAGE_DEFAULT:
                    lang_set_data[s]["set_name"] = s.title()
                    lang_set_data[s]["text_icon"] = set_data[s]["text_icon"]
                    if "short_name" in set_data[s]:
                        lang_set_data[s]["short_name"] = set_data[s]["short_name"]
                    if "set_text" in set_data[s]:
                        lang_set_data[s]["set_text"] = set_data[s]["set_text"]
                else:
                    lang_set_data[s]["set_name"] = lang_default[s]["set_name"]
                    lang_set_data[s]["text_icon"] = lang_default[s]["text_icon"]
                    if "short_name" in lang_default[s]:
                        lang_set_data[s]["short_name"] = lang_default[s]["short_name"]
                    if "set_text" in lang_default[s]:
                        lang_set_data[s]["set_text"] = lang_default[s]["set_text"]
            else:
                if lang != LANGUAGE_DEFAULT:
                    for x in lang_default[s]:
                        if x not in lang_set_data[s] and x != "used":
                            lang_set_data[s][x] = lang_default[s][x]

        if lang == LANGUAGE_DEFAULT:
            lang_default = lang_set_data  # Keep for later languages

        with io.open(
            os.path.join(args.output_dir, lang, lang_file), "w", encoding="utf-8"
        ) as lang_out:
            json.dump(lang_set_data, lang_out, ensure_ascii=False, indent=4)

    ###########################################################################
    # bonuses_xx files
    ###########################################################################
    for lang in languages:
        # Special case for xx.  Reseed from default language
        fromLanguage = lang
        if lang == LANGUAGE_XX:
            fromLanguage = LANGUAGE_DEFAULT

        copyfile(
            os.path.join(
                args.card_db_dir, fromLanguage, "bonuses_" + fromLanguage + ".json"
            ),
            os.path.join(args.output_dir, lang, "bonuses_" + lang + ".json"),
        )

    ###########################################################################
    # translation.txt
    ###########################################################################
    copyfile(
        os.path.join(args.card_db_dir, "translation.md"),
        os.path.join(args.output_dir, "translation.md"),
    )

    # Since xx is the starting point for new translations,
    # make sure xx has the latest copy of translation.txt
    copyfile(
        os.path.join(args.card_db_dir, LANGUAGE_XX, "translation.txt"),
        os.path.join(args.output_dir, LANGUAGE_XX, "translation.txt"),
    )


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--card_db_dir",
        default=os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "..", "src", "domdiv", "card_db"
        ),
        help="directory of card data",
    )
    parser.add_argument(
        "--output_dir",
        default=os.path.join(
            os.path.dirname(os.path.abspath(__file__)), ".", "card_db"
        ),
        help="directory for output data",
    )
    args = parser.parse_args()
    main(args)
