# pyOCD debugger
# Copyright (c) 2021 Chris Reed
# SPDX-License-Identifier: Apache-2.0
#
# 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.

import argparse
from typing import List
import logging

from .base import SubcommandBase
from ..core.helpers import ConnectHelper
from ..flash.file_programmer import FileProgrammer
from ..utility.cmdline import (
    convert_session_options,
    int_base_0,
)

LOG = logging.getLogger(__name__)

class LoadSubcommand(SubcommandBase):
    """! @brief `pyocd load` and `flash` subcommand."""
    
    NAMES = ['load', 'flash']
    HELP = "Load one or more images into target device memory."
    EPILOG = "Supported file formats are: binary, Intel hex, and ELF32."
    DEFAULT_LOG_LEVEL = logging.WARNING
    
    ## @brief Valid erase mode options.
    ERASE_OPTIONS = [
        'auto',
        'chip',
        'sector',
        ]

    @classmethod
    def get_args(cls) -> List[argparse.ArgumentParser]:
        """! @brief Add this subcommand to the subparsers object."""
        parser = argparse.ArgumentParser(description=cls.HELP, add_help=False)

        parser_options = parser.add_argument_group("load options")
        parser_options.add_argument("-e", "--erase", choices=cls.ERASE_OPTIONS, default='sector',
            help="Choose flash erase method. Default is sector.")
        parser_options.add_argument("-a", "--base-address", metavar="ADDR", type=int_base_0,
            help="Base address used for the address where to write a binary. Defaults to start of flash. "
                 "Only allowed if a single binary file is being loaded.")
        parser_options.add_argument("--trust-crc", action="store_true",
            help="Use only the CRC of each page to determine if it already has the same data.")
        parser_options.add_argument("--format", choices=("bin", "hex", "elf"),
            help="File format. Default is to use the file's extension. If multiple files are provided, then "
                 "all must be of this type.")
        parser_options.add_argument("--skip", metavar="BYTES", default=0, type=int_base_0,
            help="Skip programming the first N bytes. Binary files only.")

        parser.add_argument("file", metavar="<file-path>", nargs="+",
            help="File to write to memory.")
        
        return [cls.CommonOptions.COMMON, cls.CommonOptions.CONNECT, parser]
    
    def invoke(self) -> int:
        """! @brief Handle 'load' subcommand."""
        self._increase_logging(["pyocd.flash.loader", __name__])
        
        # Validate arguments.
        
        
        session = ConnectHelper.session_with_chosen_probe(
                            project_dir=self._args.project_dir,
                            config_file=self._args.config,
                            user_script=self._args.script,
                            no_config=self._args.no_config,
                            pack=self._args.pack,
                            unique_id=self._args.unique_id,
                            target_override=self._args.target_override,
                            frequency=self._args.frequency,
                            blocking=(not self._args.no_wait),
                            connect_mode=self._args.connect_mode,
                            options=convert_session_options(self._args.options))
        if session is None:
            LOG.error("No target device available")
            return 1
        with session:
            programmer = FileProgrammer(session,
                            chip_erase=self._args.erase,
                            trust_crc=self._args.trust_crc)
            for filename in self._args.file:
                LOG.info("Loading %s", filename)
                programmer.program(filename,
                                base_address=self._args.base_address,
                                skip=self._args.skip,
                                file_format=self._args.format)

        return 0



@@ -681,16 +681,41 @@
  logging
+%0Afrom pathlib import Path
 %0A%0Afrom .
@@ -2791,16 +2791,158 @@
  memory.
+ Binary files can have an optional base address appended to the file %22%0A                 %22name as '@%3Caddress%3E', for instance 'app.bin@0x20000'.
 %22)%0A     
@@ -3209,32 +3209,255 @@
 uments.%0A        
+if (self._args.base_address is not None) and (len(self._args.file) %3E 1):%0A            raise ValueError(%22--base-address cannot be set when loading more than one file; %22%0A                    %22use a base address suffix instead%22)
 %0A        %0A      
@@ -4557,40 +4557,647 @@
     
-LOG.info(%22Loading %25s%22, filename)
+# Look for a base address suffix.%0A                if %22@%22 in filename:%0A                    filename, suffix = filename.rsplit(%22@%22, 1)%0A                    base_address = int_base_0(suffix)%0A                else:%0A                    base_address = self._args.base_address%0A                %0A                # Resolve our path.%0A                file_path = Path(filename).expanduser().resolve()%0A                filename = str(file_path)%0A                %0A                if base_address is None:%0A                    LOG.info(%22Loading %25s%22, filename)%0A                else:%0A                    LOG.info(%22Loading %25s at %25#010x%22, filename, base_address)%0A
 %0A   
@@ -5283,27 +5283,16 @@
 address=
-self._args.
 base_add

Load subcommand: base address suffix, argument validation, path resolving.