#!/usr/bin/env python3

import os
import sys
import serial.tools.list_ports
from test_common import (
    DeviceUnderTestAeadUARTP,
    eprint,
    test_main,
    OpenOcd,
    FileMutex
)


def get_serial():
    ports = serial.tools.list_ports.comports()
    devices = [
        p.device
        for p in ports
        if p.serial_number == 'FT2XAA99'
    ]
    devices.sort()
    eprint("Serial port for LonganNano is %s" % devices[-1])
    return devices[-1]


class LonganNano(DeviceUnderTestAeadUARTP):
    RAM_SIZE = 0x5000

    def __init__(self, build_dir):
        DeviceUnderTestAeadUARTP.__init__(self)

        self.uart_device = get_serial()
        devname = os.path.basename(self.uart_device)
        self.lock = FileMutex('/var/lock/lwc-compare.%s.lock' % devname)
        self.build_dir = build_dir
        self.template_path = os.path.dirname(sys.argv[0])

        self.firmware_path = os.path.join(
            build_dir, 'firmware.elf')
        self.firmware_bin_path = os.path.join(
            build_dir, 'firmware.bin')
        self.ram_pattern_path = os.path.join(
            self.template_path, 'empty_ram.bin')
        self.ram_dump_path = os.path.join(
            build_dir, 'ram_dump.bin')
        self.openocd_cfg_path = os.path.join(
            self.template_path, 'openocd.cfg')

        oocd_exe = '$HOME/.platformio/packages/tool-openocd-gd32v/bin/openocd'
        oocd_exe = os.path.expandvars(oocd_exe)
        self.ocd = OpenOcd(self.openocd_cfg_path,
            executable=oocd_exe, tcl_port=6665)

        self.ser = serial.Serial(
            self.uart_device,
            baudrate=4*115200,
            timeout=5)

    def firmware_size(self):
        return os.stat(self.firmware_bin_path).st_size

    def flash(self):
        eprint("Flashing firmware...")
        ocd_cmd = 'program %s verify' % self.firmware_path
        res = self.ocd.send(ocd_cmd)
        eprint(res)
        assert res == ''
        eprint("Firmware flashed.")

        ocd_cmd = 'reset halt'
        res = self.ocd.send(ocd_cmd)
        eprint(res)
        assert res == ''

        ocd_cmd = 'load_image %s 0x20000000' % self.ram_pattern_path
        res = self.ocd.send(ocd_cmd)
        if res.startswith('20480 bytes written at address 0x20000000'):
            eprint("RAM loaded.")
        else:
            raise Exception("RAM load error", res)
            

        ocd_cmd = 'resume'
        res = self.ocd.send(ocd_cmd)
        eprint(res)
        assert res == ''

        self.ser = serial.Serial(
            self.uart_device,
            baudrate=4*115200,
            timeout=5)
        self.reset()

    def reset(self, halt=False):
        ocd_cmd = 'reset halt' if halt else 'reset run'
        res = self.ocd.send(ocd_cmd)
        eprint(res)
        assert res == ''
        eprint("Reset!")

    def dump_ram(self):

        res = self.ocd.send('halt')
        eprint(res)
        assert res == ''

        res = self.ocd.send(
            'dump_image %s 0x20000000 0x%x' %
            (self.ram_dump_path, LonganNano.RAM_SIZE))
        eprint(res)
        assert res.startswith("dumped 20480 bytes in ")

        eprint("RAM dumped.")

        with open(self.ram_dump_path, 'rb') as ram:
            ram = ram.read()
            if len(ram) != LonganNano.RAM_SIZE:
                raise Exception(
                    "RAM dump was %d bytes instead of %d" %
                    (len(ram), LonganNano.RAM_SIZE))

        res = self.ocd.send('resume')
        eprint(res)
        assert res == ''

        return ram


if __name__ == "__main__":
    sys.exit(test_main(LonganNano, 0x0200, sys.argv))
