# Copyright 2025 NXP

#SPDX-License-Identifier: BSD-3-Clause

import logging
import time
from enum import Enum
from pyparsing import ZeroOrMore, ParseException, Suppress
from queue import Empty, Queue
from threading import Thread


GDB_PROMPT = "(gdb)"
GDB_ANSWER_TIMEOUT = 30
GDB_LINE = GDB_PROMPT + Suppress(ZeroOrMore(GDB_PROMPT))

class WaitState(Enum):
    wait_answer_from_stdout = 1
    gdb_prompt_found        = 2


def make_and_print_line(char_list, queue):
    line = ''.join(map(str, char_list))
    del char_list[:]
    queue.put(line)
    line = line.strip('\n')
    logging.info(line)


def enqueue_raw_output(out, queue):
    char_list = []
    while True:
        ch = out.read(1)
        char_list.append(ch)
        if ch == '\n':
            make_and_print_line(char_list, queue)
        if ''.join(map(str, char_list[-(1 + len(GDB_PROMPT)):-1])) == GDB_PROMPT:
            char_list.append('\n')
            make_and_print_line(char_list, queue)


def find_gdb_prompt(line):
    try:
        parsed_line = GDB_LINE.parseString(line).asList()
        return parsed_line[0] == GDB_PROMPT
    except ParseException:
        return False


def read_and_parse(queue):
    parse_state = WaitState.wait_answer_from_stdout
    start_time = time.time()
    list_of_string = []

    while True:
        try:
            line = queue.get_nowait()
        except Empty:
            if parse_state == WaitState.gdb_prompt_found:
                return list_of_string
            else:
                if (time.time() - start_time) > GDB_ANSWER_TIMEOUT:
                    raise EOFError
        else:
            if find_gdb_prompt(line):
                parse_state = WaitState.gdb_prompt_found
            else:
                line = line.replace('\n', '')
                if line:
                    list_of_string.append(line)
                start_time = time.time()
            queue.task_done()


def start_gdb_parsing(gdb_proc):
    queue_out = Queue()
    thread_out = Thread(target=enqueue_raw_output, args=(gdb_proc.stdout, queue_out))
    thread_out.daemon = True
    thread_out.start()
    read_and_parse(queue_out)
    return thread_out, queue_out


def send_cmd_to_gdb(proc, str_cmd, queue):
    proc.stdin.write(str_cmd)
    proc.stdin.flush()
    return read_and_parse(queue)


def stop_gdb(gdb_proc):
    gdb_proc.stdin.close()
    gdb_proc.terminate()
    gdb_proc.wait()