# -*- coding: utf-8 -*-
"""
    Implementation of CodeWarrior PHY commands.

Copyright 2018-2025 NXP

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of NXP nor the names of its
      contributors may be used to endorse or promote products derived from
      this software without specific prior written permission.

This software is provided by NXP "as is" and any
express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are
disclaimed. In no event shall NXP be liable for any
direct, indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused and
on any theory of liability, whether in contract, strict liability, or tort
(including negligence or otherwise) arising in any way out of the use of
this software, even if advised of the possibility of such damage.
"""

import re
import argparse
import os
import sys
import json
import shlex
import binascii
import struct
import gdb   # @UnresolvedImport  # pylint: disable=F0401


sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)) + "/../../")
import rsdk_test.scripts.utils as utils
COMMANDS_DIR = ''.join([os.path.dirname(os.path.realpath(__file__)), '/'])
from rsdk_test.gdbcommands import cmd_name



def to_int(int_str):
    """
    Convert string to int (JSON does not handle hex-formatted numbers...)
    """
    int_val = 0
    try:
        # If conversion of int in base 10 fails, it's most likely a hex-formatted
        # number
        int_val = int(int_str)
    except: # pylint: disable=W0702
        try:
            int_val = int(int_str, 16)
        except: # pylint: disable=W0702
            raise gdb.GdbError("DDR PHY: %s is not a number" % int_str)
    return int_val

def aligned_incr(counter, increment, alignment):
    """Increment and align"""
    return (counter + increment + alignment - 1) & ~(alignment - 1)


class TestCommand(gdb.Command): # pylint: disable=too-few-public-methods
    """Initialize DDR-PHY.
usage: rsdk-test [-h] -i <init file> -b <board> [-d <ddr type>] [-t <dimm type>]
                    [-c <controller id>] -f <frequency> -wa <address>
                    -ws <size> [--skip-download]

optional arguments:
  -h, --help            show this help message and exit
  
  -o <operation>, --operation <operation>
                        specify the operation to be executed
  -S, --skip-download
                        skip download of algorithm and IMEM firmware
    """

    #make sure that invoke gets called only once
    instance = None
    
    def __init__(self):    
        
        """
            Constructor.
        """
        super(TestCommand, self).__init__(cmd_name.CMD_DS_DDR_COMMAND, gdb.COMMAND_USER,
                                         gdb.COMPLETE_COMMAND)

        parser = argparse.ArgumentParser(prog='rsdk-test',
                                         description='Run one rsdk test.')

        parser.add_argument('-i', '--init', 
                            metavar = '',
                            default = '',
                            help = 'specify board initialization script')
        parser.add_argument('--no_soc_init',
                            action="store_true",
                            help = 'don\'t initialize target it is already \
                            initialized')
        
        # Specifies full path to a .json file
        parser.add_argument('-d', '--dir_test',
                            default = '',
                            help='TEST directory',
                            required=False)
                            
        parser.add_argument('-e', '--elf_test',
                            help='TEST ELF file',
                            required=True)         
         
        self.parser = parser
        
        self.in0_addr = 0
        self.in1_addr = 0
        self.in2_addr = 0
        self.in3_addr = 0
        self.in4_addr = 0
        self.out0_addr = 0
        self.out1_addr = 0
        self.test_dir = ""
        self.init = True
        
    def invoke(self, arguments, from_tty):        
        """
            Function that will be executed when command is invoked.
        """
        self.dont_repeat()        
        print("arguments = " + str (arguments))
        try:
            args = self.parser.parse_args(shlex.split(arguments))  
        except SystemExit:
            return
        if args.no_soc_init:
             self.init = False
        self.params = {}
        if args.dir_test:
            self.test_dir = args.dir_test
            with open(self.test_dir + "/param.json") as json_file:
                self.params = json.load(json_file)        
       
        try:
            self.board_init(args)
        except SystemExit:
           pass
           
        if args.elf_test:
            self.execute_operation(args.elf_test)
         

    def execute_operation(self, elf_file_path):
        """
        Execute test operations

        Args:
            op_type (int): type of operation to be performed

        Returns:
            Nothing
        """        

        # Load elf file
        if  self.init:
            self.download_elf(elf_file_path)
        
        #utils.gdb_execute("directory {}".format(elf_file_path + "../../../source"))

        # Execute the operation        
        self.set_bkp_and_execute_algorithm("DEBUG_START")
                
        # Load test params
        self.load_params()        
        
        # Load in.lod file to target
        self.download_binary(self.test_dir + "/in.lod", format(hex(self.in0_addr).rstrip("L")))
        
        # Execute the kernel(s) on target
        self.set_bkp_and_execute_algorithm("DEBUG_LOOP")
        
        # Save test results in output file
        end_addr =  hex(self.out0_addr + self.params['TC_out_sz']).rstrip("L")        
        self.save_output( self.test_dir + "/out_1.lod", format(hex(self.out0_addr).rstrip("L")), end_addr)      
        
        
    def load_params(self):
        """
        Load params from json       


        Returns:
            Nothing
        """
        #set gKernelId variable
         
        utils.gdb_execute("set variable gKernelId={}".format(self.params['TC_kernel_id']))
                
        #set gpIns & gpOuts variables

        outcomm = utils.gdb_execute("p &gDataBuf")
        parts = outcomm.split()
        
        self.in0_addr = int(parts[-1].strip(),16)
        self.in1_addr = self.in0_addr + self.params['TC_in0_sz']
        self.in2_addr = self.in1_addr + self.params['TC_in1_sz']
        self.in3_addr = self.in2_addr + self.params['TC_in2_sz']
        self.in4_addr = self.in3_addr + self.params['TC_in3_sz']
        self.out0_addr = self.in4_addr + self.params['TC_in4_sz']
        self.out1_addr = self.out0_addr + self.params['TC_out0_sz']
        
        #utils.gdb_execute("set variable gpIn0.ui=&gDataBuf")        
        utils.gdb_execute("set variable gpIn0.ui={}".format(hex(self.in0_addr).rstrip("L")))
        utils.gdb_execute("set variable gpIn1.ui={}".format(hex(self.in1_addr).rstrip("L")))
        utils.gdb_execute("set variable gpIn2.ui={}".format(hex(self.in2_addr).rstrip("L")))
        utils.gdb_execute("set variable gpIn3.ui={}".format(hex(self.in3_addr).rstrip("L")))
        utils.gdb_execute("set variable gpIn4.ui={}".format(hex(self.in4_addr).rstrip("L")))
        utils.gdb_execute("set variable gpOut0.ui={}".format(hex(self.out0_addr).rstrip("L")))
        utils.gdb_execute("set variable gpOut1.ui={}".format(hex(self.out1_addr).rstrip("L")))
        
        #set gParam variables
        TC_params = self.params['TC_param']
       
        index = 0
        while index < len(TC_params):                      
            utils.gdb_execute("set variable gParam[{}]= {}".format(index, TC_params[index]))
            index += 1
      
        
    def download_binary(self, filename, address):
        """
        Downloads the given binary at the given address 
        """               

        try:           
           utils.gdb_execute('restore {} binary {}'.format(filename.replace("\\", "/"), address))
        except:
            raise gdb.GdbError('Cannot download binary file')  

    def save_output(self, filename, address_start, address_stop):
                

        try:           
           utils.gdb_execute('dump binary memory {} {} {}'.format(filename.replace("\\", "/"), address_start, address_stop  ))
        except:
            raise gdb.GdbError('Cannot download binary file')  

        
        
        
    def set_bkp_and_execute_algorithm(self,where):
        """
        Set the PC at where point, run, wait for the core to stop, set test params
        

        Args:
            None

        Returns:
            Nothing
        """
        utils.gdb_execute("del breakpoints")    
        utils.gdb_execute("b {}".format(where))      
            
            
        print("Running ELF on target until " + where)
        utils.gdb_execute("cont")
        
        # Wait for the algorithm to stop
        while True:
            
            state = utils.gdb_execute("monitor core-mode")
            if "debug" in state:
                break       

    def download_elf(self,filename):
        """
        Loads symbolics and downloads the given elf 
        """        
        
        try:           
           utils.gdb_execute('file {}'.format(filename.replace("\\", "/"))) 
           utils.gdb_execute('load')
        except :
            raise gdb.GdbError('Cannot load elf file')        

    def board_init(self, args):        
        ret = False
        if TestCommand.instance is None:
            from rsdk_test.scripts.target_init import start
            ret = start(args)
            if ret is True:
                TestCommand.instance = self
            else:
                print('T-ERROR: fail to initialize the board.')
        else:
            print('Test already started')
        return ret

def create_test_command():
    """Create PHY command"""
    TestCommand()
    
def start_rsdk_test(arguments):
    """
       Add ddr inititializer services
    """    
    instance = TestCommand()
    print("arguments = " + str(arguments))
    return instance.invoke(arguments, False)    
