#!/usr/bin/env python

# Copyright (C) 2020 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

# This script calls Inkscape to rasterize several images into png files.
# The images end up in the final position of the source tree.
# Each image is generated as normal and high resolution variant.
# Each png file is afterwards optimized with optipng.

import argparse
import os
import re
import subprocess
import sys
import xml.etree.ElementTree as ET

from distutils import spawn


def qtcRoot():
    return os.path.abspath(
        os.path.join(os.path.dirname(sys.argv[0]), '../../..')).replace('\\', '/')


def svgIDs(svgFile, svgElementFilter):
    # The svg element IDs of images to export. They correspond to the
    # path and base name of each image in the Qt Creator sources.
    svgIDs = []
    svgTree = ET.ElementTree()
    svgTree.parse(os.path.join(qtcRoot(), svgFile))
    svgTreeRoot = svgTree.getroot()
    pattern = re.compile(svgElementFilter)
    for svgElement in svgTreeRoot.iter():
        try:
            svgElementID = svgElement.attrib['id']
            if '/' in svgElementID and pattern.match(svgElementID):
                svgIDs.append(svgElementID)
        except Exception:
            pass

    print("\n==== {} elements found which match {}"
          .format(len(svgIDs), svgElementFilter))
    return svgIDs


def pngName(svgID, scale):
    # File name is relative to qtcRoot()
    return svgID + ("" if scale == 1 else "@{}x".format(scale)) + ".png"


def checkDirectories(svgIDs):
    invalidDirectories = []
    for id in svgIDs:
        dirForId = os.path.abspath(os.path.join(qtcRoot(), id, os.pardir))
        if not os.path.isdir(dirForId):
            invalidDirectories.append(id)

    if invalidDirectories:
        print("\n==== {} IDs for which the output directory is missing:"
              .format(len(invalidDirectories)))
        print("\n".join(invalidDirectories))
        sys.exit("Output directories are missing.")


def printOutUnexported(svgIDs, scaleFactors):
    unexported = []
    partiallyExported = []
    for id in svgIDs:
        exportedCount = 0
        for scaleFactor in scaleFactors:
            if os.path.isfile(os.path.join(qtcRoot(), pngName(id, scaleFactor))):
                exportedCount += 1
        if exportedCount == 0:
            unexported.append(id)
        elif (exportedCount < len(scaleFactors)):
            partiallyExported.append(id)

    if partiallyExported:
        print("\n==== {} IDs for which not each .png is exported:"
              .format(len(partiallyExported)))
        print("\n".join(partiallyExported))
    if unexported:
        print("\n==== {} IDs for which all .pngs are missing:"
              .format(len(unexported)))
        print("\n".join(unexported))
    if partiallyExported or unexported:
        input("\nPress Enter to continue...")


def exportPngs(svgIDs, svgFile, scaleFactors, inkscape):
    inkscapeProcess = subprocess.Popen([inkscape, '--shell'],
                                       stdin=subprocess.PIPE,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE,
                                       cwd=qtcRoot())
    actions = ["file-open:" + svgFile]
    for id in svgIDs:
        for scale in scaleFactors:
            actions += [
                "export-id:{}".format(id),
                "export-id-only",
                "export-dpi:{}".format(scale * 96),
                "export-filename:{}".format(pngName(id, scale)),
                "export-do"
            ]
    actions += ["quit-inkscape"]
    actionLine = "; ".join(actions) + "\n"
    print("Exporting pngs for {} Ids in {} scale factors."
          .format(len(svgIDs), len(scaleFactors)))
    inkscapeProcess.communicate(input=actionLine.encode())


def optimizePngs(svgIDs, scaleFactors, optipng):
    for id in svgIDs:
        for scale in scaleFactors:
            png = pngName(id, scale)
            print("Optimizing: {}".format(png))
            try:
                subprocess.check_call([optipng,
                                       "-o7",
                                       "-strip", "all",
                                       png],
                                      stderr=subprocess.DEVNULL,
                                      cwd=qtcRoot())
            except subprocess.CalledProcessError:
                sys.exit("Failed to optimize {}.".format(png))


def export(svgFile, filter, scaleFactors, inkscape, optipng):
    ids = svgIDs(svgFile, filter)
    if not ids:
        sys.exit("{} does not match any Id.".format(filter))

    checkDirectories(ids)
    printOutUnexported(ids, scaleFactors)
    exportPngs(ids, svgFile, scaleFactors, inkscape)
    optimizePngs(ids, scaleFactors, optipng)


def main():
    parser = argparse.ArgumentParser(description='Export svg elements to .png '
                                     'files and optimize the png. '
                                     'Requires Inkscape 1.x and optipng in Path.')
    parser.add_argument('filter',
                        help='a RegExp filter for svg element Ids, e.g.: .*device.*')
    args = parser.parse_args()

    inkscape = spawn.find_executable("inkscape")
    if inkscape is None:
        sys.exit("Inkscape was not found in Path.")

    optipng = spawn.find_executable("optipng")
    if optipng is None:
        sys.exit("Optipng was not found in Path.")

    export("src/tools/icons/qtcreatoricons.svg", args.filter, [1, 2],
           inkscape, optipng)

    return 0


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