#include <stdint.h>
#include <stddef.h>

//  Create external function at 0x00006f88
extern void maybe_usb_setup_read(char endpoint,void *fun,uint32_t target_buffer);
extern void dwc3_ep0_start_trans(char endpoint,uint32_t target_buf, uint32_t len);
extern int usb_event_handler(void);
extern uint32_t get_endpoint_recv_buffer(char endpoint);
extern void exynos_sleep(int endpoint,uint32_t timeout);
extern void usb_send(uint32_t address,uint32_t size);
extern uint32_t recv_buffer;
extern uint32_t data_received;

int mystrlen(char *data) {
    int i=0;
    while(1) {
        if(data[i++] == '\0'){
            break;
        }
    }
    return i-1;
}

#ifdef RELOC_DEBUGGER
#define recv_buffer 0x020c6200 
#define data_received 0x020c6000
#else
#define recv_buffer 0x206f000 //0x02021800 + 0x3000
#define data_received 0x206f100
#endif

#ifdef RELOC_DEBUGGER_2
#define recv_buffer 0x14AC4200
#define data_received 0x14AC4000
#else
#define recv_buffer 0x206f000 //0x02021800 + 0x3000
#define data_received 0x206f100
#endif

void recv_data_cb(uint32_t endpoint, uint32_t len){
    char *dest_buf = (char *)recv_buffer;
    volatile void *dref = (void *)data_received;
    
    void *rbuf = get_endpoint_recv_buffer(endpoint);
    for(int i= 0; i < len; i++){
        dest_buf[i] = *(char *)(void *)((int)rbuf + i);
    }
    *(uint8_t *)dref = 1; // Mark as ready
}

void recv_data(void *address, uint32_t size){
    // 
    volatile void *dref = (void *)data_received;
    *(uint8_t *)dref = 0;

    maybe_usb_setup_read(2, recv_data_cb, size);
    uint32_t rbuf = get_endpoint_recv_buffer(2);
    dwc3_ep0_start_trans(2, rbuf, size);
    while(1){
        usb_event_handler();
        if(*(uint8_t *)dref == 1){
            break;
        }
        // exynos_sleep(2, 1000);
    }
    // Copy to destination location
    char *dest_buf = (uint64_t)address;
    for(int i= 0; i < size; i++){
        dest_buf[i] = *(char *)(void *)((int)recv_buffer + i);
    }
}

void send_data_cb(uint32_t endpoint, uint32_t len){
    // Tell event handler that the data was received
    volatile void *dref = (void *)data_received;
    *(uint8_t *)dref = 1; // Mark as ready
}

void send(void *address, uint32_t size, uint32_t *error){
    volatile void *dref = (void *)data_received;
    *(uint8_t *)dref = 0;
    maybe_usb_setup_read(0x1, send_data_cb, size);
    // uint32_t rbuf = get_endpoint_recv_buffer(1);
    dwc3_ep0_start_trans(1, (uint64_t)address, size);
    while(1){
        usb_event_handler();
        if(*(uint8_t *)dref == 1){
            break;
        }
        // exynos_sleep(1, 1000);
    }
}

void usb_log(char * msg, uint32_t * error){
	send(msg, mystrlen(msg), error);    
}

void concrete_main(uint64_t debugger){

}