#!/usr/bin/env python3

# Note: SVG templates are heavily based on these:
# https://boardgamegeek.com/thread/813176/card-templates/page/1

import json
import math
import os
import subprocess
import sys

from lxml import etree

if getattr(sys, 'frozen', False): # we are running in a bundle
    bundle_dir = sys._MEIPASS
else: # we are running in a normal Python environment
    bundle_dir = os.path.dirname(os.path.abspath(__file__))

CARD_WIDTH = 181
CARD_HEIGHT = 253

with open(bundle_dir + "/templates/card.json") as f:
    CARD_JSON_TEMPLATE = json.load(f)

def setText(tree, id, text):
    element = tree.find('.//*[@id="' + id + '"]')
    if element is None:
        print("id", id, "not found")
        return
    elif element.tag == "{http://www.w3.org/2000/svg}flowRoot":
        for e in element.findall("{http://www.w3.org/2000/svg}flowPara"):
            element.remove(e) # clear child paragraphs
        lines = str(text).splitlines()
        for line in lines:
            etree.SubElement(element,
                             "{http://www.w3.org/2000/svg}flowPara").text=line
    else:
        element.text = str(text)

def removeElement(tree, id):
    mark = tree.find('.//*[@id="' + id + '"]')
    if mark is not None:
        mark.getparent().remove(mark)

def makeSVG(base, properties):
    tree = etree.parse(base)
    for id, text in properties.items():
        if id not in ["count", "art"]:
            setText(tree, id, text)

    # remove HP mark if there is no hp
    if "hp" not in properties:
        removeElement(tree, "hpMark")

    # remove setup box if there is no setup
    if "setup" not in properties:
        removeElement(tree, "setupBox")

    if "art" in properties:
        art = tree.find('.//*[@id="art"]')
        if art is not None:
            art.set("{http://www.w3.org/1999/xlink}href",
                    "file://" + bundle_dir + "/" + properties["art"])
    return tree

def makeCardJson(nickname, description, cardID, outJson=None):
    card = CARD_JSON_TEMPLATE.copy()
    card.update({"Nickname": nickname,
                 "Description": description,
                 "CardID": 100 + cardID})
    if outJson is not None:
        outJson['ObjectStates'][0]['DeckIDs'].append(100 + cardID)
        outJson['ObjectStates'][0]['ContainedObjects'].append(card)
    return card

def makeDoubleSidedCardJson(nickname, descriptionFront,
                            descriptionBack, cardID, outJson):
    cardBack = makeCardJson(nickname, descriptionBack, cardID + 1)

    card = makeCardJson(nickname, descriptionFront, cardID, outJson)
    card.update({"States": {"2": cardBack}})
    return card

def addCardToBase(svg, baseImg, cardsPerRow, cardNum):
    for e in svg.findall('{http://www.w3.org/2000/svg}g'):
        e.set("transform",
              "{} translate({} {})".format(
                  e.get("transform", ""),
                  str((cardNum % cardsPerRow) * CARD_WIDTH),
                  str(int(cardNum / cardsPerRow) * CARD_HEIGHT)))
    baseImg.getroot().append(svg.getroot())

def makeFace(outSVG, cardsPerRow, cardNum, deckType, cardFile, card):
    path = os.path.join(bundle_dir, "images", deckType, cardFile)
    fig = makeSVG(path, card)
    addCardToBase(fig, outSVG, cardsPerRow, cardNum)

def makeDoubleSidedFace(outSVG, cardsPerRow, cardNum, deckType, cardFile,
                        cardFront, cardBack):
    makeFace(outSVG, cardsPerRow, cardNum, deckType, cardFile, cardFront)
    makeFace(outSVG, cardsPerRow, cardNum + 1, deckType, cardFile, cardBack)

def makeCards(deckJson, outfile, outdir, host):
    # number of cards in x and y direction
    deckType = deckJson["type"]
    if deckType in ["hero", "villain"]:
        cardsPerRow = math.ceil(math.sqrt(len(deckJson['deck']) + len(deckJson['character']) * 2))
    else:
        cardsPerRow = math.ceil(math.sqrt(len(deckJson['deck']) * 2))
    outSVG = etree.ElementTree(
        etree.Element('svg', attrib={'width': str(cardsPerRow * CARD_WIDTH) + "pt",
                                     'height': str(cardsPerRow * CARD_HEIGHT) + "pt",
                                     'version': "1.2",
                                     'xmlns': "http://www.w3.org/2000/svg"}))

    with open(bundle_dir + "/templates/deck.json") as f:
        outJson = json.load(f)
    outJson['ObjectStates'][0]['CustomDeck']['1'].update(
        {"NumWidth": cardsPerRow,
         "NumHeight": cardsPerRow,
         "FaceURL": host + outfile + ".png",
         "BackURL": "http://cloud-3.steamusercontent.com/ugc/156906385556221451/CE2C3AFE1759790CB0B532FFD636D05A99EC91F4/"})

    cardNum = 0
    # Make a card for each hero character card
    if deckType == "hero":
        for card in deckJson['character']:
            makeDoubleSidedCardJson(card['name'], "Active",
                                    "Incapacitated", cardNum, outJson)
            makeFace(outSVG, cardsPerRow, cardNum, deckType, "charFront.svg", card)
            cardNum += 1
            makeFace(outSVG, cardsPerRow, cardNum, deckType, "charBack.svg", card)
            cardNum += 1

    # Make a character and instructions card for each villain card
    elif deckType == "villain":
        for card in deckJson['character']:
            front = card["front"]
            front["name"] = card["name"]
            back = card["back"]
            back["name"] = card["name"]

            # character card
            makeDoubleSidedCardJson(card['name'], "Front", "Back",
                                    cardNum, outJson)
            makeDoubleSidedFace(outSVG, cardsPerRow, cardNum, deckType,
                                "character.svg", front, back)
            cardNum += 2

            # instructions card
            makeDoubleSidedCardJson(card['name'] + " instructions",
                                    "Front", "Back", cardNum, outJson)
            makeDoubleSidedFace(outSVG, cardsPerRow, cardNum, deckType,
                                "instructions.svg", front, back)
            cardNum += 2

    # Make a card for each normal card
    for card in deckJson['deck']:
        for i in range(0, card.get('count', 1)):
            makeCardJson(card['name'], card.get('keywords', ""),
                         cardNum, outJson)
        makeFace(outSVG, cardsPerRow, cardNum, deckType, "card.svg", card)
        cardNum += 1

    # write SVG and convert to PNG
    outSVG.write(os.path.join(outdir, outfile) + ".svg")
    command = ["inkscape", "-z",
               "-f", os.path.join(outdir, outfile) + ".svg",
               "-w", str(cardsPerRow * CARD_WIDTH * 5),
               "-e", os.path.join(outdir, outfile) + ".png"]
    print("To regenerate PNG after editing SVG, run:\n    " + " ".join(command))
    subprocess.run(command)

    # Write TTS deck json
    with open(os.path.join(outdir, outfile) + ".json", "w") as f:
        json.dump(outJson, f)

if __name__ == '__main__':
    if len(sys.argv) < 3:
        print("not enough arguments!")
        inputJson = input("Input file: ")
        outfile = input("Output file (no suffix): ")
    else:
        inputJson = sys.argv[1]
        outfile = sys.argv[2]

    with open(inputJson) as f:
        deckJson = json.load(f)

    makeCards(deckJson, outfile, os.getcwd(), "file://" + os.getcwd() + "/")
