# -*- coding: utf-8 -*-
"""
    Implementation of Flash Programmer blank check command

Copyright 2015 Freescale Semiconductor, Inc.
Copyright 2017-2018, 2024 NXP.

SPDX-License-Identifier: BSD-3-Clause

"""

import gdb   # @UnresolvedImport  # pylint: disable=F0401
import argparse

from flash.gdbcommands.base_command import CwFlashBase
from flash.gdbcommands.cmd_name import CMD_CW_FLASH_BLANK_CHECK

from flash.scripts.utils import string_to_offset
from flash.scripts.utils import string_to_size
from flash.scripts.utils import size_to_human_readable
from flash.scripts.remote import execute_remote


###################################################################
######### flash programmer blank check command
###################################################################
class CwFlashBlankCheck(CwFlashBase):
    """
usage: [-h] [-n {NUMBER | all}] offset size

S32DS Flash Programming blank check command.

positional arguments:
  offset                offset in device address range
  size                  size of memory section to be checked

optional arguments:
  -h, --help            show this help message and exit

  -n, --number
                        number of mismatches shown
    """

    # Define erase sector values
    erase_value = 0xFF
    """"
        Since user can check all flash device or a large range,
        we should read 512K chunks to improve usability
    """
    buffer_size = 512 * 1024

    def __init__(self):
        """
            Register new command with GDB
            Add a parameter parser to easily specify positional offset
            and size parameters
        """
        super(CwFlashBlankCheck, self).__init__(CMD_CW_FLASH_BLANK_CHECK,
        gdb.COMMAND_USER, gdb.COMPLETE_COMMAND)
        parser = argparse.ArgumentParser(description='S32DS \
                                         Flash Programming blank check command.')
        parser.add_argument('offset',
                            type = string_to_offset,
                            help = 'offset in device address range')
        parser.add_argument('size',
                            type = string_to_size,
                            help = 'size of memory section to be checked')

        group = parser.add_mutually_exclusive_group()

        group.add_argument('-n', '--number',
                            type = int,
                            default = 10,
                            help = 'number of mismatches shown')
        group.add_argument('-a', '--all',
                            action="store_true",
                            help = 'number of mismatches shown')
        self._parser = parser
        CwFlashBlankCheck.print_result = None

    def _set_erase_value(self, from_tty):
        """"
            Call this method to get device erase value
        """
        cmd_string = 'mon flash current'
        current_flash = execute_remote(cmd_string, from_tty, to_string = True, no_wait = True)

        cmd_string = 'mon ctx get current'
        current_ctx = execute_remote(cmd_string, from_tty, to_string = True)

        current_ctx = current_ctx.rstrip('\n')
        cmd_string = 'mon ctx id %s:%s info' % (current_ctx, current_flash)
        ctxs = current_ctx.split(':')
        if len(ctxs) >= 3:
            device = ':%s:%s:%s' % (ctxs[1], ctxs[2], current_flash)
            cmd_string = 'mon ctx id %s info' % device
        result = execute_remote(cmd_string, from_tty, to_string = True)

        # Separate each property with '|'
        result = result.replace('\n', '|')
        # Get erase_value
        index = result.find("erase_value")
        # Set default value to 0xFF if no property is set
        if index == -1:
            return
        erase_prop = result[index:].split('|')
        # Remove any blank space
        erase_prop = erase_prop[0].replace(' ', '')
        # Get erase value
        erase_prop = erase_prop.split(':')
        self.erase_value = int(erase_prop[1], 16)

    def execute(self, args, from_tty):
        """
            Execute command called with args parameters
            Build command string in the form of a monitor command
            and call internal gdb execution.
        """
        # Set erase value if any
        self._set_erase_value(from_tty)

        size = 0
        out_string = ""

        output_number = -1
        verbose = False

        if args.all:
            verbose = True
        elif args.number:
            output_number = args.number

        remaining_size = args.size
        current_addr = args.offset

        while remaining_size:
            # Read data in 512KB chunks
            if remaining_size >= self.buffer_size:
                remaining_size -= self.buffer_size
                size = self.buffer_size
            else:
                # Final chunk
                size = remaining_size;
                remaining_size = 0;

            cmd_string = 'mon flash dump offset 0x%x size 0x%x' \
                % (current_addr, size)

            #execute command
            ctx_str = execute_remote(cmd_string, from_tty, to_string = True, no_wait = True)

            buff = bytearray.fromhex(ctx_str)

            index = 0
            for byte in buff:
                if output_number == 0 and verbose is False:
                    self._output = out_string
                    return

                # Compare each byte with device erase value
                if byte != self.erase_value:
                    if out_string:
                        out_string += "\n"

                    output_number -= 1
                    out_string += ("Expecting 0x%02x but found 0x%02x at offset 0x%x") \
                                    % (self.erase_value, byte, (current_addr + index))

                index += 1

            # Update current address
            current_addr += size

        if out_string == "":
            self._output = ("Blank check from 0x%x to 0x%x succeeded") \
                            % (args.offset, args.offset + args.size - 1)
        else:
            self._output = out_string

    def begin_command(self):
        gdb.write('Blank checking....\n')

    def show_result(self):
        if self._output:
            gdb.write('%s\n' % self._output)
        summary = 'Blank check %s in %.2fs\n' % \
            (size_to_human_readable(self._args.size), self._time)
        gdb.write(summary)

def create_command():
    """
        Instantiate flash programmer blank check command
    """
    CwFlashBlankCheck()
