#!/usr/bin/python3
#
# rpicam-apps bug report generator.
# Copyright (C) 2021, Raspberry Pi Ltd.
#
import argparse
from datetime import datetime
import select
import subprocess
import sys
import time


class Report:
    def __init__(self, id, file):
        self._id = id
        self._cmds = []
        self._strs = []
        self._file = file

    def __run_cmd(self, cmd):
        print(f"** {cmd} **", file=self._file)
        try:
            p = subprocess.run(
                cmd,
                text=True,
                check=False,
                shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
            )
            print(p.stdout, file=self._file)
        except RuntimeError as e:
            print(f"Error: {e}", file=self._file)

    def add_cmd(self, c):
        self._cmds.append(c)

    def add_str(self, s):
        self._strs.append(s)

    def exec(self):
        print(f"{'-' * 80}\n{self._id}\n{'-' * 80}", file=self._file)

        for c in self._cmds:
            self.__run_cmd(c)

        for s in self._strs:
            print(s, file=self._file)


def run_prog(cmd, t):
    cmd = cmd.split(" ")
    out = []
    try:
        start = time.time()
        p = subprocess.Popen(
            cmd,
            text=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            errors="ignore",
        )
        poll = select.poll()
        poll.register(p.stdout, select.POLLIN)

        while p.poll() is None:
            if poll.poll(0):
                line = p.stdout.readline()
                print(line, end="", flush=True)
                out.append(line)

            if (t != 0) and (time.time() - start > t):
                p.kill()
                out = out + p.communicate()[0].splitlines(keepends=True)
                out.append("Error: ***** TIMEOUT *****")
                break

    except KeyboardInterrupt:
        p.kill()
        out = out + p.communicate()[0].splitlines(keepends=True)
        out.append("Error: ***** INTERRUPT *****")

    p.wait()
    return "".join(out)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="rpicam-apps Bug Report Generator")
    parser.add_argument(
        "-o", help="Report filename", type=str, default="bug-report.txt"
    )
    parser.add_argument(
        "-t",
        help="Timeout (seconds) for the command to run. A value of 0 \
                                    disables the timeout.",
        type=float,
        default=0,
    )
    parser.add_argument(
        "-c",
        help='Command to run, e.g. -c "rpicam-still -t 1000 -o test.jpg"',
        type=str,
    )
    args = parser.parse_args()

    # This is the app the user is actually running.
    app = "rpicam-hello"
    if args.c:
        app = args.c.split(" ")[0]
        # Can we identify the app?  If not, use rpicam-hello for version checks.
        if not any(
            [
                s in app
                for s in [
                    "rpicam-still",
                    "rpicam-vid",
                    "rpicam-hello",
                    "rpicam-raw",
                    "rpicam-jpeg",
                ]
            ]
        ):
            app = "rpicam-hello"

    reports = []
    with open(args.o, "wt") as file:
        title = Report("rpicam-apps Bug Report", file)
        title.add_str(f"Date: {datetime.now().strftime('%d-%m-%Y (%H:%M:%S)')}")
        title.add_str(f"Command: {' '.join(sys.argv)}\n")
        reports.append(title)

        hwinfo = Report("Hardware information", file)
        hwinfo.add_cmd("hostname")
        hwinfo.add_cmd("cat /proc/cpuinfo")
        reports.append(hwinfo)

        config = Report("Configuration", file)
        config.add_cmd("cat /boot/firmware/cmdline.txt")
        config.add_cmd("cat /boot/firmware/config.txt")
        reports.append(config)

        logs = Report("Logs", file)
        logs.add_cmd("dmesg")
        logs.add_cmd("sudo vclog log --msg")
        logs.add_cmd("sudo vclog log --assert")
        reports.append(logs)

        mem = Report("Memory", file)
        mem.add_cmd("cat /proc/meminfo")
        mem.add_cmd("sudo cat /sys/kernel/debug/dma_buf/bufinfo")
        mem.add_cmd("sudo cat /sys/kernel/debug/vcsm-cma/state")
        reports.append(mem)

        media = Report("Media Devices", file)
        for i in range(5):
            media.add_cmd(f"media-ctl -d {i} -p")
        reports.append(media)

        # Get the camera list with the same program specified in the run command
        cam = Report("Cameras", file)
        cam.add_cmd(f"{app} --list-cameras")
        reports.append(cam)

        # Get the version with the same program specified in the run command
        ver = Report("Versions", file)
        ver.add_cmd("uname -a")
        ver.add_cmd("cat /etc/os-release")
        ver.add_cmd("vcgencmd version")
        ver.add_cmd(f"{app} --version")
        reports.append(ver)

        # Run the actual application before executing the reports!
        if args.c:
            cmd_out = run_prog(args.c, args.t)

            # Report for the command output
            cmd = Report(args.c, file)
            cmd.add_str(cmd_out)
            reports.append(cmd)

        for r in reports:
            r.exec()

        print(f"\nBug report generated to {args.o}")
        print("Please upload this file when you create a new bug report at:")
        print("https://github.com/raspberrypi/rpicam-apps/issues/")
