# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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.
import pytest
import os
import shlex

import tvm
from tvm.driver.tvmc.main import _main
from tvm.driver.tvmc.config_options import convert_config_json_to_cli, get_configs_json_dir


def test_parse_json_config_file_one_target():
    tokens = convert_config_json_to_cli(
        {"targets": [{"kind": "llvm"}], "output": "resnet50-v2-7-autotuner_records.json"}
    )
    expected_tokens = [{"target": "llvm"}, {"output": "resnet50-v2-7-autotuner_records.json"}]

    assert len(tokens) == len(expected_tokens)
    assert tokens == expected_tokens


def test_parse_json_config_file_multipile_targets():
    tokens = convert_config_json_to_cli(
        {
            "targets": [{"kind": "llvm"}, {"kind": "c", "mcpu": "cortex-m55"}],
            "tuning-records": "resnet50-v2-7-autotuner_records.json",
            "pass-config": {"tir.disable_vectorizer": "1"},
        }
    )
    expected_tokens = [
        {"target_c_mcpu": "cortex-m55"},
        {"target": "llvm, c"},
        {"tuning_records": "resnet50-v2-7-autotuner_records.json"},
        {"pass_config": ["tir.disable_vectorizer=1"]},
    ]

    assert len(tokens) == len(expected_tokens)
    assert tokens == expected_tokens


def test_parse_json_config_file_executor():
    tokens = convert_config_json_to_cli(
        {
            "executor": {"kind": "aot", "interface-api": "c"},
            "inputs": "imagenet_cat.npz",
            "max-local-memory-per-block": "4",
            "repeat": "100",
        }
    )
    expected_tokens = [
        {"executor": "aot"},
        {"executor_aot_interface_api": "c"},
        {"inputs": "imagenet_cat.npz"},
        {"max_local_memory_per_block": "4"},
        {"repeat": "100"},
    ]

    assert len(tokens) == len(expected_tokens)
    assert tokens == expected_tokens


def test_parse_json_config_file_target_and_executor():
    tokens = convert_config_json_to_cli(
        {
            "targets": [
                {"kind": "ethos-u -accelerator_config=ethos-u55-256"},
                {"kind": "c", "mcpu": "cortex-m55"},
                {"kind": "cmsis-nn"},
            ],
            "executor": {"kind": "aot", "interface-api": "c", "unpacked-api": "1"},
            "inputs": "imagenet_cat.npz",
            "max-local-memory-per-block": "4",
            "repeat": "100",
        }
    )
    expected_tokens = [
        {"target_c_mcpu": "cortex-m55"},
        {"target": "ethos-u -accelerator_config=ethos-u55-256, c, cmsis-nn"},
        {"executor": "aot"},
        {"executor_aot_interface_api": "c"},
        {"executor_aot_unpacked_api": "1"},
        {"inputs": "imagenet_cat.npz"},
        {"max_local_memory_per_block": "4"},
        {"repeat": "100"},
    ]

    assert len(tokens) == len(expected_tokens)
    assert tokens == expected_tokens


def test_parse_json_config_file_runtime():
    tokens = convert_config_json_to_cli(
        {
            "targets": [
                {"kind": "cmsis-nn", "from_device": "1"},
                {"kind": "c", "mcpu": "cortex-m55"},
            ],
            "runtime": {"kind": "crt"},
            "inputs": "imagenet_cat.npz",
            "output": "predictions.npz",
            "pass-config": {"tir.disable_vectorize": "1", "relay.backend.use_auto_scheduler": "0"},
        }
    )
    expected_tokens = [
        {"target_cmsis-nn_from_device": "1"},
        {"target_c_mcpu": "cortex-m55"},
        {"target": "cmsis-nn, c"},
        {"runtime": "crt"},
        {"inputs": "imagenet_cat.npz"},
        {"output": "predictions.npz"},
        {"pass_config": ["tir.disable_vectorize=1", "relay.backend.use_auto_scheduler=0"]},
    ]

    assert len(tokens) == len(expected_tokens)
    assert tokens == expected_tokens


@tvm.testing.requires_cmsisnn
def test_tvmc_cl_compile_run_config_file(tflite_mobilenet_v1_1_quant, tmpdir_factory):
    compile_config_file = "compile_config_test.json"
    pytest.importorskip("tflite")

    output_dir = tmpdir_factory.mktemp("mlf")
    input_model = tflite_mobilenet_v1_1_quant
    output_file = os.path.join(output_dir, "mock.tar")

    # Compile the input model and generate a Model Library Format (MLF) archive.
    tvmc_cmd = (
        f"tvmc compile --config {compile_config_file} {input_model} --output {output_file} "
        f"--output-format mlf"
    )
    tvmc_args = shlex.split(tvmc_cmd)[1:]
    _main(tvmc_args)
    assert os.path.exists(output_file), "Could not find the exported MLF archive."

    # Run the MLF archive. It must fail since it's only supported on micro targets.
    tvmc_cmd = f"tvmc run {output_file}"
    tvmc_args = tvmc_cmd.split(" ")[1:]
    exit_code = _main(tvmc_args)
    on_error = "Trying to run a MLF archive must fail because it's only supported on micro targets."
    assert exit_code != 0, on_error


def test_tvmc_get_configs_json_dir(tmpdir_factory, monkeypatch):
    # Reset global state
    monkeypatch.setattr(tvm.driver.tvmc.config_options, "CONFIGS_JSON_DIR", None)

    # Get default directory for reference
    default_dir = get_configs_json_dir()

    # Set custom dir which does not exist -> ignore
    monkeypatch.setattr(tvm.driver.tvmc.config_options, "CONFIGS_JSON_DIR", None)
    monkeypatch.setenv("TVM_CONFIGS_JSON_DIR", "not_a_directory")
    result = get_configs_json_dir()
    assert_msg = "Non-existent directory passed via TVM_CONFIGS_JSON_DIR should be ignored."
    assert result == default_dir, assert_msg

    # Set custom dir which does exist
    monkeypatch.setattr(tvm.driver.tvmc.config_options, "CONFIGS_JSON_DIR", None)
    configs_dir = tmpdir_factory.mktemp("configs")
    monkeypatch.setenv("TVM_CONFIGS_JSON_DIR", str(configs_dir))
    result = get_configs_json_dir()
    assert_msg = (
        "Custom value passed via TVM_CONFIGS_JSON_DIR should be used instead of default one."
    )
    assert result != default_dir and result is not None, assert_msg
