#include <stdlib.h>

#include "ssl_log.h"
#include "ssl_file_list.h"
#include "platform.h"
#include "types.h"

typedef struct{
    uint32_t size;
    void * pointer;
    uint32_t used;
}memlist_t, *memlist_ptr;

static memlist_t memlist[1024] = {0,};

void init_memlist(){
    plt_memset(memlist, 0, sizeof(memlist_t) * 1024);
}

uint32_t malloc_used(void *p, uint32_t s){
    /*MLOG_M();*/

    int i = 0;
    for(i = 0; i < 1024; i++){
        if (memlist[i].used && memlist[i].pointer == p) {
            MLOG_E("duplicated allocation");
            abort();
        }
    }

    for(i = 0; i < 1024; i++){
        if (memlist[i].used == 0) {
            memlist[i].used = 1;
            memlist[i].pointer = p;
            memlist[i].size = s;
            *(int*)((char*)p - 4) = 0xabcdcdef;
            *(int*)((char*)p + s) = 0xcdefabcd;
            break;
        }
    }

    if(i == 1024){
        MLOG_E("allocated memory can not be tracked");
        abort();
    }

    return i;
}

uint32_t free_used(void *p){
    int i = 0;
    int found = -1;
    for(i = 0; i < 1024; i++){
        if (memlist[i].used) {

            if (memlist[i].pointer == p) {
                memlist[i].used = 0;
                found = i;
            }

            char *q = memlist[i].pointer;

            if (*(int*)(q-4) != 0xabcdcdef) {
                MLOG_E("corrupted memory block %p, prefix = %x", q, *(int*)(q-4));
                abort();
            }
            if (*(int*)(q+memlist[i].size) != 0xcdefabcd) {
                MLOG_E("corrupted memory block %p, postfix = %x", q, *(int*)(q+memlist[i].size));
                abort();
            }
        }
    }

    if (found == -1) {
        MLOG_E("double free??");
        abort();
    }
    return found;
}

#ifdef MEMORY_DEBUG
void dump_meminfo(const char* op, uint32_t i){
    memlist_ptr m = &memlist[i];
    MLOG_M("[ %s ] pointer: %p", op, m->pointer);
    MLOG_M("[ %s ] size : %d", op, m->size);
    MLOG_M("[ %s ] index: %d", op, i);
}

void free_mem_leaked(){
    int i =0;

    for(i = 0; i < 1024; i++){
        if (memlist[i].used == 1) {
            void *p = memlist[i].pointer;
            size_t size = memlist[i].size;
            MLOG_M("[ LEAKED MEM ->  %p(%d) ]", p, size);
            /*target_free(p);*/
        }
    }
}
#endif

size_t stat_mem_info(){
    int i = 0;
    size_t used = 0;

    for(i = 0; i < 1024; i++){
        if (memlist[i].used == 1) {
            used += memlist[i].size;
        }
    }

    MLOG_M("[ USED MEM ->  0X%08X ]", used);

    return used;
}

void *_plt_malloc(const char *func, int lineno, int s){
    void *p = target_malloc(s + 8);
    /*uint32_t  i = 0;*/
    malloc_used((char*)p + 4, s);
    /*dump_meminfo("MALLOC->", i);*/
    MLOG_M("[ %s(%d) -> %p ( %d ) ]", func, lineno, (char*)p + 4, s);

    return (char*)p + 4;
}

void _plt_free(const char *func, int lineno, void *p){
    if (p == NULL)
        return;

    MLOG_M("[ %s(%d) <- %p ]", func, lineno, p);
    uint32_t i = free_used(p);
    MLOG_M("[ freed %s(%d) <- %p (%d) ]", func, lineno, p, memlist[i].size);
    /*MLOG_M("[ %s(%d) <- %p ( %d ) ]", func, lineno, p, memlist[i].size);*/
    /*dump_meminfo("FREE<-", i);*/
    target_free((char*)p - 4);

    p = NULL;
}

char *plt_strdup(const char *src){
    char *dst = plt_malloc(plt_strlen(src) + 1);
    if(NULL == dst ) {
        return NULL;
    }

    plt_strcpy(dst, src);

    return dst;
}

#ifdef __ARMCC__
char *_plt_strsep(char **str, const char *delims)
{
    char *token;

    if (!*str) {
        /* No more tokens */
        return NULL;
    }

    token = *str;
    while (**str != '\0') {
        if (strchr(delims, **str)) {
            **str = '\0';
            (*str)++;
            return token;
        }
        (*str)++;
    }

    /* There is no other token */
    *str = NULL;

    return token;
}
#endif
