# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Helps work with ninja."""

from absl import flags
from absl import logging
from nanoemoji.util import rel, quote_if_path
import ninja_syntax
import os
from pathlib import Path
import subprocess
import sys
from typing import MutableSequence

FLAGS = flags.FLAGS


# internal flags, typically client wouldn't change
flags.DEFINE_string("build_dir", "build/", "Where build runs.")
flags.DEFINE_bool("gen_ninja", True, "Whether to regenerate build.ninja")
flags.DEFINE_bool("exec_ninja", True, "Whether to run ninja.")


def _str_path(arg):
    if isinstance(arg, Path):
        return str(arg)
    if isinstance(arg, MutableSequence):
        return _str_paths(arg)
    return arg


def _str_paths(args):
    # Path in particular kerplodes nw.build
    if isinstance(args, dict):
        return {_str_path(k): _str_path(v) for k, v in args.items()}
    elif isinstance(args, list) or isinstance(args, tuple):
        return [_str_path(v) for v in args]
    else:
        raise ValueError(f"What to do with {type(args)}")


class NinjaWriter:
    def __init__(self, output_f):
        self._nw = ninja_syntax.Writer(output_f)
        self.comment("Generated by nanoemoji")
        self.newline()

    def rule(self, *args, **kwargs):
        self._nw.rule(*args, **kwargs)

    def build(self, *args, **kwargs):
        # ninja already shell-quotes paths containing spaces/special characters when
        # when expanding $in or $out variables in rule commands, however other
        # user-defined variables are not quoted automatically. So when we pass a path
        # as a variable, we need to make sure it's correctly quoted. Other string
        # variables we leave unquoted as they may contain multiple arguments (e.g.
        # $pngquant_flags).
        variables = {
            k: quote_if_path(v) for k, v in kwargs.pop("variables", {}).items()
        }
        self._nw.build(
            *_str_paths(args),
            **{k: _str_paths(v) for k, v in kwargs.items()},
            variables=variables,
        )

    def newline(self):
        self._nw.newline()

    def comment(self, comment):
        self._nw.comment(comment)


def build_dir() -> Path:
    return Path(FLAGS.build_dir).resolve()


def rel_build(path: Path) -> Path:
    return rel(build_dir(), path)


def gen_ninja() -> bool:
    return FLAGS.gen_ninja


def module_rule(
    nw,
    mod_name,
    arg_pattern,
    rspfile=None,
    rspfile_content=None,
    rule_name=None,
    allow_external=False,
):
    if not rule_name:
        rule_name = mod_name
    if not allow_external:
        mod_name = "nanoemoji." + mod_name
    nw.rule(
        rule_name,
        f"{sys.executable} -m {mod_name} -v {FLAGS.verbosity} {arg_pattern}",
        rspfile=rspfile,
        rspfile_content=rspfile_content,
    )


def maybe_run_ninja(build_file: Path):
    ninja_cmd = ["ninja", "-C", os.path.dirname(build_file)]
    if FLAGS.exec_ninja:
        logging.info(" ".join(ninja_cmd))
        subprocess.run(ninja_cmd, check=True)
    else:
        logging.info("To run: " + " ".join(ninja_cmd))
