# Copyright (c) 2023, Antmicro <www.antmicro.com>
#
# Based on J-Link runner
# Copyright (c) 2017 Linaro Limited.
# SPDX-License-Identifier: Apache-2.0

"""
Runner that implements flashing with SiLabs Simplicity Commander binary tool.
See SiLabs UG162: "Simplicity Commander Reference Guide" for more info.
"""

import os
import shlex
from runners.core import ZephyrBinaryRunner, RunnerCaps, FileType


DEFAULT_APP = 'commander'


class SiLabsCommanderBinaryRunner(ZephyrBinaryRunner):
    def __init__(self, cfg, device, dev_id, commander, dt_flash, erase, speed, tool_opt):
        super().__init__(cfg)
        self.file = cfg.file
        self.file_type = cfg.file_type
        self.hex_name = cfg.hex_file
        self.bin_name = cfg.bin_file
        self.elf_name = cfg.elf_file
        self.device = device
        self.dev_id = dev_id
        self.commander = commander
        self.dt_flash = dt_flash
        self.erase = erase
        self.speed = speed

        self.tool_opt = []
        for opts in [shlex.split(opt) for opt in tool_opt]:
            self.tool_opt += opts

    @classmethod
    def name(cls):
        return 'silabs_commander'

    @classmethod
    def capabilities(cls):
        return RunnerCaps(commands={'flash'},
                          dev_id=True, flash_addr=True, erase=True,
                          tool_opt=True, file=True)

    @classmethod
    def dev_id_help(cls) -> str:
        return '''Device identifier. Use it to select the J-Link Serial Number
                  of the device connected over USB.'''

    @classmethod
    def tool_opt_help(cls) -> str:
        return "Additional options for Simplicity Commander, e.g. '--noreset'"

    @classmethod
    def do_add_parser(cls, parser):
        # Required:
        parser.add_argument('--device', required=True,
                            help='device part number')

        # Optional:
        parser.add_argument('--commander', default=DEFAULT_APP,
                            help='path to Simplicity Commander executable')
        parser.add_argument('--speed', default=None,
                            help='JTAG/SWD speed to use')

    @classmethod
    def do_create(cls, cfg, args):
        return SiLabsCommanderBinaryRunner(
                cfg, args.device,
                dev_id=args.dev_id,
                commander=args.commander,
                dt_flash=args.dt_flash,
                erase=args.erase,
                speed=args.speed,
                tool_opt=args.tool_opt)

    def do_run(self, command, **kwargs):
        self.require(self.commander)

        opts = ['--device', self.device]
        if self.erase:
            opts.append('--masserase')
        if self.dev_id:
            opts.extend(['--serialno', self.dev_id])
        if self.speed is not None:
            opts.extend(['--speed', self.speed])

        # Get the build artifact to flash

        if self.dt_flash:
            flash_addr = self.flash_address_from_build_conf(self.build_conf)
        else:
            flash_addr = 0

        if self.file is not None:
            # use file provided by the user
            if not os.path.isfile(self.file):
                raise ValueError(f'Cannot flash; file ({self.file}) not found')

            flash_file = self.file

            if self.file_type == FileType.HEX:
                flash_args = [flash_file]
            elif self.file_type == FileType.BIN:
                flash_args = ['--binary', '--address', f'0x{flash_addr:x}', flash_file]
            else:
                raise ValueError('Cannot flash; this runner only supports hex and bin files')

        else:
            # use hex or bin file provided by the buildsystem, preferring .hex over .bin
            if self.hex_name is not None and os.path.isfile(self.hex_name):
                flash_file = self.hex_name
                flash_args = [flash_file]
            elif self.bin_name is not None and os.path.isfile(self.bin_name):
                flash_file = self.bin_name
                flash_args = ['--binary', '--address', f'0x{flash_addr:x}', flash_file]
            else:
                raise ValueError(f'Cannot flash; no hex ({self.hex_name}) or bin ({self.bin_name}) files found.')

        args = [self.commander, 'flash'] + opts + self.tool_opt + flash_args

        self.logger.info('Flashing file: {}'.format(flash_file))
        self.check_call(args)
