# -*- coding: utf-8 -*-
# Copyright 2015 Freescale Semiconductor, Inc.
# Copyright 2017-2025 NXP
# SPDX-License-Identifier: BSD-3-Clause

#Implementation of Flash Programmer utility methods

import gdb
import string
import os
import os.path
from errors import Result, Error, ErrorCode, Ok, safecall
from flash.scripts.fperrorinfo import fperr_pretty_print

# Enable or disable warning messages
__show_warning = False

def int_to_bytes(number, length=0):  # helper function
    """ Int/long to byte string. """
    no_of_bits = number.bit_length() + (1 if number < 0 else 0)
    no_of_bytes = (no_of_bits+7) // 8
    retval = bytearray()
    for i in range(no_of_bytes):
        retval.append(number & 0xff)
        number >>= 8
    if length > 0 and len(retval) < length:  # zero pad?
        retval.extend([0] * (length-len(retval)))
    return bytearray(reversed(retval))

def string_to_value(str):
    try:
        if str.startswith('0x'):
            return int(str, 16)
        return int(str)
    except BaseException as e:
        Result(Error(ErrorCode.ValueError, str(e))).unwrap()

def string_to_offset(str):
    try:
        if str.startswith('0x'):
            return int(str, 16)
        return int(str)
    except BaseException as e:
        Result(Error(ErrorCode.ValueError, str(e))).unwrap()

def string_to_size(str):
    value = 0
    if str.startswith('0x'):
        value = int(str , 16)
    elif str.endswith('k') or str.endswith('K'):
        value = 1024 * int(str[:len(str) - 1])
    elif str.endswith('m') or str.endswith('M'):
        value = 1024 * 1024 * int(str[:len(str) - 1])
    elif str.endswith('g') or str.endswith('G'):
        value = 1024 * 1024 * 1024 * int(str[:len(str) - 1])
    else:
        value = int(str , 10)

    if value <= 0:
        Result(Error(ErrorCode.ValueError, 'size can\'t be zero')).unwrap()

    return value

def string_to_abs_path(str):
    abs_file_path = os.path.abspath(str)
    abs_file_path = abs_file_path.replace("\\", "/")
    path_list = abs_file_path.rsplit('/', 1)
    if os.path.isdir(path_list[0]):
        return abs_file_path

    Result(Error(ErrorCode.ValueError, 'F-ERROR: invalid path %s' % path_list[0])).unwrap()

def is_hex_sequence(seq):
    if seq.startswith('0x'):
        hex_digits = set(string.hexdigits)
        return all(c in hex_digits for c in seq[2:])
    return False

def size_to_human_readable(size):
    suffix = 'B'
    for unit in ['','K','M','G','T']:
        if abs(size) < 1024.0:
            if unit == '':
                return '%d%s%s' % (size, unit, suffix)
            else:
                return '%.2f%s%s' % (size, unit, suffix)
        size /= 1024.0
    return '%.2f%s%s' % (size, 'P', suffix)

def get_soc_ctx_from_current(ctx):
    """
        Get SoC context for current context
        For flash programmer current context it is usually a core context
    """
    # check if current context is a SoC context
    cmd_string = 'mon ctx id %s get prop type' % ctx
    result = gdb_execute(cmd_string, False, True)
    if result:
        if 'soc' not in result:  #pylint: disable=unsupported-membership-test
            ctxs = ctx.split(':')
            return ''.join([':', ctxs[1], ':', ctxs[2]])
    return ctx

@safecall
def gdb_execute(command, from_tty = False, to_string = False, check_warning = True):
    """
        Execute a GDB command, remove the echo if it appears,
        handle the exception
    """

    #if it is a monitor command issue warning
    if check_warning is True and command and command.startswith('mon'):
        gdb_warning("Warning: monitor command called through gdb.execute. \n")

    try:
        result = gdb.execute(command, from_tty, to_string)
        if result:
            result = result.rstrip("\n")
        echo_command = "+" + command + "\n"
        if result is not None and result.startswith(echo_command):
            return Result(Ok(result[len(echo_command):]))
        else:
            return Result(Ok(result))
    except gdb.error as exc:
        cmd = 'mon errorinfo'
        error_msg = gdb_execute(cmd, from_tty, True)
        error_msg += "on command:%s" % command
        fperr_pretty_print(error_msg)
        return Result(Error(ErrorCode.FlashUndefinedError, "GDB EXECUTE ERROR"))

def gdb_warning(warning_msg):
    global __show_warning

    if __show_warning is True:
        gdb.write(warning_msg)

import sys
import re
import gdb

class CwFlashProgressBar(object):
    """
        Progress bar.
    """

    def __init__(self):
        """
            Constructor.
        """

        # Progress bar must disable the paging option, so it must save the
        # current state of that option.
        self.current_paging = False

        # Current state of the progress bar.
        self.state = False

        # Enable the progress bar only when in interactive mode.
        self.enabled = False

    def start_progress_bar(self):
        """
            Initialize the progress bar.
        """
        interactive_mode = gdb_execute("show interactive-mode", False, True)
        result = re.search("interactive mode is auto \(currently (.*)\)", interactive_mode)
        if result is None:
            result = re.search("interactive mode is (.*)\.", interactive_mode)

        if (result is not None) and (result.group(1) == "on"):
            self.enabled = True

        if self.enabled:
            pagination = gdb_execute("show pagination", False, True)
            result = re.search("pagination is (.*)\.", pagination)
            if (result is not None) and (result.group(1) == "on"):
                self.current_paging = True

            gdb_execute("set pagination off")
            sys.stdout.flush()

    def end_progress_bar(self):
        """
            Ends progress and restores pagination.
        """
        if self.enabled:
            if self.current_paging:
                gdb_execute("set pagination on")

            sys.stdout.flush()

    def update_progress_bar(self, operation, step, count, bar_length=20):
        """
            Updates the current status of the progress bar.
        """

        if self.enabled:
            #for i in xrange(0, step):
            percent = float(step) / count
            hashes = '#' * int(round(percent * bar_length))
            spaces = ' ' * (bar_length - len(hashes))
            sys.stdout.write('\rPercent: [{0}] {1}%'.format(hashes + spaces, int(round(percent * 100))))
            sys.stdout.flush()
