from tools import *

class AmlogicDevice:
    BASE = None
    SIZE = None

    def __init__(self, em):
        self.em = em

    def read(self, address, size):
        raise Exception('must be implemented')

    def write(self, address, size, value):
        raise Exception('must be implemented')

# //========================================================================
# //  EFUSE
# //========================================================================
# //  APB4_DECODER_NON_SECURE_BASE     32'hFF630000
# //  APB4_DECODER_SECURE_BASE         32'hFF630000


class EfuseDevice(AmlogicDevice):
    # 0xff630000|0xff631fff|efuse
    BASE = 0xff630000
    SIZE = 0x2000
    NAME = "efuse"

    EFUSE_CLR                                          = (BASE + (0x000 << 2))
    EFUSE_START                                        = (BASE + (0x001 << 2))
    EFUSE_WDATA0                                       = (BASE + (0x004 << 2))
    EFUSE_WDATA1                                       = (BASE + (0x005 << 2))
    EFUSE_WDATA2                                       = (BASE + (0x006 << 2))
    EFUSE_WDATA3                                       = (BASE + (0x007 << 2))
    EFUSE_RDATA0                                       = (BASE + (0x008 << 2))
    EFUSE_RDATA1                                       = (BASE + (0x009 << 2))
    EFUSE_RDATA2                                       = (BASE + (0x00a << 2))
    EFUSE_RDATA3                                       = (BASE + (0x00b << 2))
    EFUSE_LIC0                                         = (BASE + (0x00c << 2)) # 0xff630030
    EFUSE_LIC1                                         = (BASE + (0x00d << 2)) # 0xff630034
    EFUSE_LIC2                                         = (BASE + (0x00e << 2))
    EFUSE_LIC3                                         = (BASE + (0x00f << 2))
    KL_START0                                          = (BASE + (0x020 << 2))
    KL_START1                                          = (BASE + (0x021 << 2))
    KL_RESP0_0                                         = (BASE + (0x024 << 2))
    KL_RESP0_1                                         = (BASE + (0x025 << 2))
    KL_RESP0_2                                         = (BASE + (0x026 << 2))
    KL_RESP0_3                                         = (BASE + (0x027 << 2))
    KL_RESP1_0                                         = (BASE + (0x028 << 2))
    KL_RESP1_1                                         = (BASE + (0x029 << 2))
    KL_RESP1_2                                         = (BASE + (0x02a << 2))
    KL_RESP1_3                                         = (BASE + (0x02b << 2))
    KL_RAM                                             = (BASE + (0x040 << 2))
    RNG_SEC_CONFIG_REG1                                = (BASE + (0x081 << 2))
    RNG_SEC_CONFIG_REG2                                = (BASE + (0x082 << 2))
    RNG_SEC_DATA                                       = (BASE + (0x084 << 2))
    RNG_SEC_STS                                        = (BASE + (0x085 << 2)) #0xff630214
    RNG_USR_DATA                                       = (BASE + (0x086 << 2))
    RNG_USR_STS                                        = (BASE + (0x087 << 2))

    EFUSE_RDY = BASE + 0x214

    def __init__(self, em):
        super().__init__(em)

    def read(self, address, size):
        if address == EfuseDevice.EFUSE_RDY:
            self.em.write_ptr(EfuseDevice.EFUSE_RDY, 0x1)
    
    def write(self, address, size, value):
        self.em.write_ptr(address, value)


class UARTDevice(AmlogicDevice):
    BASE = 0xff803000
    SIZE = 0x1000
    NAME = "UART"

    UART_WFIFO = BASE +   (0<<2)
    UART_RFIFO  = BASE +	(1<<2)
    UART_MODE   = BASE +	(2<<2)
    UART_STATUS = BASE + (3<<2)
    UART_IRQCTL = BASE +	(4<<2)
    UART_CTRL   = BASE +	(5<<2)

    UART_MODE_MASK_STP_1BIT                     =(0<<16)
    UART_MODE_MASK_CHAR_8BIT                    =(0<<20)
    UART_MODE_MASK_TX_EN                        =(1<<12)
    UART_MODE_MASK_RX_EN                        =(1<<13)

    UART_STAT_MASK_RFIFO_FULL =                (1<<19)
    UART_STAT_MASK_RFIFO_EMPTY  =               (1<<20)
    UART_STAT_MASK_TFIFO_FULL                   =(1<<21)
    UART_STAT_MASK_TFIFO_EMPTY                  =(1<<22)

    UART_MODE_MASK_RST_TX                       =(1<<22)
    UART_MODE_MASK_RST_RX                       =(1<<23)
    UART_MODE_MASK_CLR_ERR                      =(1<<24)
    UART_CTRL_USE_XTAL_CLK			    =BASE + (1<<24)
    UART_CTRL_USE_NEW_BAUD_RATE		    =BASE + (1<<23)


    def __init__(self, em):
        super().__init__(em)
        self.send_data = bytearray()
        self.recv_data = bytearray()
        self.gsc_ring_buffer_size = 0x1000

    def uart_tx_isfull(self):
        return self.em.mem_read(self.BASE + self.UART_STATUS) & self.UART_STAT_MASK_TFIFO_FULL

    def uart_rx_isfull(self):
        return self.em.mem_read(self.BASE + self.UART_STATUS) & self.UART_STAT_MASK_RFIFO_FULL

    def uart_tx_flush(self):
        #TODO
        # while (!(readl(P_UART_STATUS(UART_PORT_CONS)) & UART_STAT_MASK_TFIFO_EMPTY))
		#    ;
        pass

    def uart_rx_flush():
        #TODO
        # while (!(readl(P_UART_STATUS(UART_PORT_CONS)) & UART_STAT_MASK_RFIFO_EMPTY))
        # 	;
        pass


    def read(self, address, size):
        if address == UARTDevice.UART_STATUS:
            if len(self.send_data) != 0 and self.gsc_ring_buffer_size != 0:
                val = self.em.read_ptr(UARTDevice.UART_STATUS) & ~UARTDevice.UART_STAT_MASK_TFIFO_EMPTY
                self.em.write_ptr(UARTDevice.UART_STATUS, val)
            else:
                val = self.em.read_ptr(UARTDevice.UART_STATUS) | UARTDevice.UART_STAT_MASK_TFIFO_EMPTY
                self.em.write_ptr(UARTDevice.UART_STATUS, val)

        if address == UARTDevice.UART_RFIFO:
            self.gsc_ring_buffer_size -= 1
            byte = self.send_data.pop(0)
            self.em.write_ptr(UARTDevice.UART_RFIFO, byte)

    def write(self, address, size, value):
        if address == UARTDevice.UART_WFIFO:
            self.recv_data.append(value & 0xff)
        else:
            self.em.write_ptr(address, value)

    def has_tx(self):
        return len(self.send_data) != 0

    def put_tx(self, data):
        self.send_data.extend(data)

    def has_rx(self):
        return len(self.recv_data) != 0

    def get_rx(self):
        ret = self.recv_data
        self.recv_data = bytearray()
        return ret
    

class TimerDevice(AmlogicDevice):
    # https://github.com/hardkernel/u-boot/blob/154ddfc826e72c902232169b676b2a4e59c75171/board/hardkernel/odroidn2/firmware/timing.c#L456
    BASE = 0xffd0f000
    SIZE = 0x1000
    NAME = "isa_timer"

    TIMER_CURRENT_TIME = BASE + 0x188

    def __init__(self, em):
        super().__init__(em)

    TIMER_CURRENT_TIME2 = BASE + 0x1C
    TIMER_END_TIME = BASE + 0x38
    TIMER_WAIT_TIME = BASE + 0x40

    def read(self, address, size):
        if address == TimerDevice.TIMER_CURRENT_TIME:
            val = int(time.clock_gettime_ns(0)/1000) & 0xffffffff
            self.em.write_ptr(TimerDevice.TIMER_CURRENT_TIME, val)

    def write(self, address, size, data):
        self.em.write_ptr(address, data)

class GPIODevice(AmlogicDevice):
    #define P_PIN_MUX_REG3		(*((volatile unsigned *)(0xff634400 + (0x2f << 2))))
    #define P_PIN_MUX_REG4		(*((volatile unsigned *)(0xff634400 + (0x30 << 2))))
    #define P_PIN_MUX_REG10		(*((volatile unsigned *)(0xff634400 + (0x36 << 2))))
    BASE = 0xff634400
    SIZE = 0x1000
    NAME = "GPIO"

    P_PIN_MUX_REG3		= BASE + (0x2f << 2)
    P_PIN_MUX_REG4		= BASE + (0x30 << 2)
    P_PIN_MUX_REG10		= BASE + (0x36 << 2)

    def __init__(self, em):
        super().__init__(em)
        self.logger = setup_logger("GPIODevice")
        self.logger.setLevel(logging.INFO)

    def read(self, address, size):
        gpio_offset = ((address - GPIODevice.BASE) // 4)
        self.logger.debug(f'[{hex(self.em.pc)}]|Reading from GPIO {gpio_offset}')
        

    def write(self, address, size, value):
        gpio_offset = ((address - GPIODevice.BASE) // 4)
        self.logger.debug(f'[{hex(self.em.pc)}] | Writing to GPIO {gpio_offset} Address: {hex(address)} Value: {value} Size: {size}')
        self.em.write_ptr(address, value)

class USBPHYDevice(AmlogicDevice):
    NAME = "USBPHY"
    NAME2 = "usbphy21"
    BASE = 0xff63a000
    SIZE = 0x2000
    
    def __init__(self, em):
        super().__init__(em)

    def read(self, address, size):
        pass

    def write(self, address, size, value):
        self.em.write_ptr(address, value)

class USB1Device(AmlogicDevice):
    NAME = "USB1"
    NAME2 = "usbphy21"
    BASE = 0xff400000
    SIZE = 1 * MB
    
    def __init__(self, em):
        super().__init__(em)
        self.logger = setup_logger("USB1")
        self.logger.setLevel(logging.DEBUG)

    def read(self, address, size):
        self.logger.debug(f"Address: {hex(address)} size:{hex(size)}")
        if address == 0xff400040:
            # self.em.write_ptr(address, 0x4f543fff)
            # self.em.write_ptr(address, 1)
            pass
        elif address == 0xff400014 or address == 0xff400018 or address == 0xff400004:
            # self.em.write_ptr(address, 1)
            pass
        

    def write(self, address, size, value):
        self.em.write_ptr(address, value)

class WatchdogDevice(AmlogicDevice):
    # TODO??
    def __init__(self, em):
        super().__init__(em)