# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
import subprocess
import time


def find_gdb_path():
    """ Find the first arm gdb on our path"""
    prioritized_names = ['pebble-gdb', 'arm-none-eabi-gdb-py', 'arm-none-eabi-gdb']
    for name in prioritized_names:
        try:
            which_all_cmd = 'which %s' % name
            out = subprocess.check_output(which_all_cmd, shell=True)
        except subprocess.CalledProcessError, e:
            if e.returncode == 1:
                continue  # `which` returns with 1 when nothing is found
            raise e
        path = out.splitlines()[0]
        logging.info("Found %s at %s" % (name, path))
        return path
    return None


class GDBDriver(object):
    def __init__(self, elf_path, gdb_path=None, server_port=1234):
        self.gdb_path = gdb_path or find_gdb_path()
        if not self.gdb_path:
            raise Exception("pebble-gdb not found on your path, nor"
                            " was it specified using the `gdb_path` argument")
        self.elf_path = elf_path
        self.server_port = server_port
        self.pipe = None
        self.interface = GDBInterface(self)

    def _gdb_command(self):
        cmd = self.gdb_path
        cmd += " %s" % self.elf_path
        cmd += " -ex=\"target remote :%u\"" % self.server_port
        return cmd

    def start(self):
        if self.pipe:
            raise Exception("GDB Already running.")

        # Run GDB:
        cmd = self._gdb_command()
        try:
            self.pipe = subprocess.Popen(cmd, stdin=subprocess.PIPE,
                                         shell=True)
        except:
            logging.error("Failed to start GDB.\nCommand: `%s`" % cmd)
            return
        time.sleep(0.1)  # FIXME
        logging.info("GDB started.")

    def stop(self):
        if self.pipe:
            self.pipe.kill()
            logging.info("GDB stopped.")
            self.pipe = None

    def write_stdin(self, cmd):
        if not self.pipe:
            logging.error("GDB not running")
            return
        self.pipe.stdin.write(cmd)

    def send_signal(self, signal):
        self.pipe.send_signal(signal)


class GDBInterface(object):
    def __init__(self, gdb_driver):
        assert gdb_driver
        self.gdb_driver = gdb_driver

    def _send(self, cmd):
        self.gdb_driver.write_stdin(cmd)

    def _send_signal(self, signal):
        self.gdb_driver.send_signal(signal)

    def interrupt(self):
        self._send_signal(signal.SIGINT)

    def cont(self):
        self._send("c\n")

    def source(self, script_file_name):
        self._send("source %s\n" % script_file_name)

    def set(self, var_name, expr):
        self._send("set %s=%s\n" % (var_name, expr))

    def disable_breakpoints(self):
        self._send("dis\n")

    def set_pagination(self, enabled):
        enabled_str = "on" if enabled else "off"
        self._send("set pagination %s\n" % enabled_str)