import gdb
import json


READY = 0
COMPLETED = 1
TERMINATED = 2
SUSPENDED = 3
SLEEP = 4
QUEUE_SUSP = 5
SEMAPHORE_SUSP = 6
EVENT_SUSP = 7
BLOCK_MEMORY = 8
BYTE_MEMORY = 9
IO_DRIVER = 10
FILE = 11
TCP_IP = 12
MUTEX_SUSP = 13

STATE_NAMES = {
    READY: "Ready",
    COMPLETED: "Completed",
    TERMINATED: "Terminated",
    SUSPENDED: "Suspended",
    SLEEP: "Sleep",
    QUEUE_SUSP: "Queue susp",
    SEMAPHORE_SUSP: "Semaphore susp",
    EVENT_SUSP: "Event susp",
    BLOCK_MEMORY: "Block memory",
    BYTE_MEMORY: "Byte memory",
    IO_DRIVER: "IO driver",
    FILE: "File",
    TCP_IP: "TCP/IP",
    MUTEX_SUSP: "Mutex susp",
}


def get_curr_thread(config, types):
    curr_thread = gdb.parse_and_eval("*_tx_thread_current_ptr")
    return json.dumps(prepare_task_data(curr_thread, config))


def get_azure_threads(config, types):
    current = gdb.parse_and_eval("*_tx_thread_current_ptr")
    created_head = gdb.parse_and_eval("*_tx_thread_created_ptr")
    created_count = gdb.parse_and_eval("_tx_thread_created_count")
    priority_list = gdb.parse_and_eval("_tx_thread_priority_list")

    result = [prepare_task_data(current, config)]
    max_thread_priorities = 32  # defined at tx_thread.h so can't get it in runtime
    thread_priority = 0
    while thread_priority < max_thread_priorities:
        if priority_list[thread_priority] != 0:
            thread_first = priority_list[thread_priority].cast(types.thread_t.pointer()).dereference()
            result.append(prepare_task_data(thread_first, config))
            thread_next = thread_first["tx_thread_ready_next"].cast(types.thread_t.pointer()).dereference()
            while thread_next != thread_first:
                result.append(prepare_task_data(thread_next, config))
                thread_next = thread_next["tx_thread_ready_next"].cast(types.thread_t.pointer()).dereference()
        thread_priority += 1

    thread_count = 0
    current_thread = created_head
    while thread_count < created_count:
        if current_thread["tx_thread_state"] != 0:
            result.append(prepare_task_data(current_thread, config))
        thread_count += 1
        current_thread = current_thread["tx_thread_created_next"].cast(types.thread_t.pointer()).dereference()

    return json.dumps(result)


def prepare_task_data(task, config):
    task_addr = int(task.address)

    try:
        pc_buff = gdb.selected_inferior().read_memory(int(task["callee_saved"]["psp"]) + 60, 4)
        pc = "".join("%.2x" % ord(pc_buff[i]) for i in reversed(range(len(pc_buff))))
    except gdb.error:
        pc = 0

    try:
        result = {
          "name": task["tx_thread_name"].string(),
          "currentPriority": int(task["tx_thread_priority"]),
          "basePriority": int(task["tx_thread_user_priority"]),
          "handle": task_addr,
          "id": int(task["tx_thread_id"]),
          "stackTop": int(task["tx_thread_stack_ptr"]),
          "stackBase": int(task["tx_thread_stack_start"]),
          "stackEnd": int(task["tx_thread_stack_end"]),
          "programCounter": int("0x%s" % pc, 0),
          "eventObjectPtr": 0,
        }
    except gdb.error:
        return {}

    try:
        result["state"] = STATE_NAMES[int(task["tx_thread_state"])]
    except KeyError:
        result["state"] = "Unknown (" + str(int(task["tx_thread_state"])) + ")"

    return result
