# ------------------------------------------------------------------------------
# Copyright (c) Lukas Vik. All rights reserved.
# ------------------------------------------------------------------------------

import datetime
from collections import OrderedDict

from tsfpga.git_utils import git_commands_are_available, get_git_commit
from tsfpga.svn_utils import svn_commands_are_available, get_svn_revision_information


class Bit:

    def __init__(self, idx, name, description):
        self.idx = idx
        self.name = name
        self.description = description


# mode: (mode_readable, description)
REGISTER_MODES = {
    "r": ("Read", "PS can read a value that PL provides."),
    "w": ("Write", "PS can write a value that is available for PL usage."),
    "r_w": (
        "Read, Write",
        "PS can write a value and read it back. The written value is available for PL usage."),
    "wpulse": ("Write-pulse", "PS can write a value that is asserted for one clock cycle in PL."),
    "r_wpulse": (
        "Read, Write-pulse",
        "PS can read a value that PL provides. "
        "PS can write a value that is asserted for one clock cycle in PL."),
}


class Register:

    def __init__(self, name, idx, mode, description=""):
        self.name = name
        self.idx = idx
        self.mode = mode
        self.description = description
        self.bits = []

    def append_bit(self, bit_name, bit_description):
        idx = len(self.bits)
        bit = Bit(idx, bit_name, bit_description)

        self.bits.append(bit)
        return bit

    @property
    def mode_readable(self):
        """
        A human-readable representation of the register mode.
        """
        return REGISTER_MODES[self.mode][0]

    @property
    def address(self):
        address_int = 4 * self.idx
        num_nibbles_needed = 4
        formatting_string = "0x{:0%iX}" % num_nibbles_needed
        return formatting_string.format(address_int)

    @property
    def is_ps_readable(self):
        """
        Return true if the register is readable by PS.
        """
        return self.mode in ["r", "r_w", "r_wpulse"]

    @property
    def is_ps_writeable(self):
        """
        Return true if the register is writeable by PS.
        """
        return self.mode in ["w", "r_w", "wpulse", "r_wpulse"]


class RegisterList:

    def __init__(self, name):
        self.name = name
        self.registers = OrderedDict()

    def append(self, register_name, mode):
        idx = len(self.registers)
        register = Register(register_name, idx, mode)

        self.registers[register_name] = register
        return register

    @staticmethod
    def generated_info():
        """
        A string informing the user that a file is automatically generated
        """
        info = "File automatically generated by tsfpga"
        return info

    @staticmethod
    def generated_source_info():
        """
        A string containing info about the source of the generated register information
        """
        commit_info = ""
        if git_commands_are_available():
            commit_info = "from " + get_git_commit() + " "
        elif svn_commands_are_available():
            commit_info = "from " + get_svn_revision_information() + " "

        time_info = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")

        info = f"Generated by tsfpga {commit_info}at {time_info}."
        return info

    def iterate_registers(self):
        for register in self.registers.values():
            yield register
