#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "DataType.h"

#define ARRAY_NAME 30

static int typeNameIndex = 0;
static int typeNameSwap = 0;
static char typeName[2][ARRAY_NAME];

static void dtAppend(const char* s) {
    int index = 0;
    while(typeNameIndex < (ARRAY_NAME - 1) && s[index] != '\0') {
        typeName[typeNameSwap][typeNameIndex] = s[index];
        index++;
        typeNameIndex++;
    }
    typeName[typeNameSwap][typeNameIndex] = '\0';
}

const char* dtGetName(Structs* sts, DataType dt) {
    typeNameSwap = !typeNameSwap;
    typeNameIndex = 0;
    if(dt.structId > 0) {
        dtAppend(sts->data[dt.structId - 1].name);
    } else {
        switch(dt.type) {
            case DT_INT: dtAppend("int"); break;
            case DT_FLOAT: dtAppend("float"); break;
            case DT_BOOL: dtAppend("bool"); break;
            default: dtAppend("unknown");
        }
    }
    for(unsigned int i = 0; i < dt.pointers; i++) {
        dtAppend("*");
    }
    if(dt.reference) {
        dtAppend("&");
    }
    return typeName[typeNameSwap];
}

int dtGetSize(DataType dt) {
    switch(dtAsInt(dt)) {
        case DT_INT: return sizeof(int);
        case DT_FLOAT: return sizeof(float);
        case DT_BOOL: return sizeof(bool);
        default:
            if(dt.structId > 0) {
                return 0;
            }
            return sizeof(int);
    }
}

DataType dtInt() {
    DataType dt = {DT_INT, 0, 0, 0};
    return dt;
}

DataType dtFloat() {
    DataType dt = {DT_FLOAT, 0, 0, 0};
    return dt;
}

DataType dtBool() {
    DataType dt = {DT_BOOL, 0, 0, 0};
    return dt;
}

DataType dtVoid() {
    DataType dt = {DT_VOID, 0, 0, 0};
    return dt;
}

DataType dtStruct(Struct* st) {
    DataType dt = {DT_STRUCT, 0, 0, st->id};
    return dt;
}

DataType dtToReference(DataType dt) {
    dt.reference = 1;
    return dt;
}

DataType dtToArray(DataType dt, int dimension) {
    dt.pointers = dimension;
    return dt;
}

bool dtCompare(DataType a, DataType b) {
    return dtAsInt(a) == dtAsInt(b);
}

int dtMaxDimensions() {
    return 15;
}

unsigned int dtAsInt(DataType dt) {
    unsigned int i;
    memcpy(&i, &dt, sizeof(dt));
    return i;
}

bool dtIsArray(DataType dt) {
    return dt.pointers > 0;
}

void stAddVariable(Struct* st, const char* name, DataType type) {
    int index = st->amount;
    st->amount++;
    st->vars = realloc(st->vars, sizeof(StructVariable) * st->amount);
    st->vars[index].name = name;
    st->vars[index].type = type;
}

void stsInit(Structs* sts) {
    sts->capacity = 4;
    sts->entries = 0;
    sts->data = malloc(sizeof(Struct) * sts->capacity);
}

void stsDelete(Structs* sts) {
    for(int i = 0; i < sts->entries; i++) {
        free(sts->data[i].vars);
    }
    free(sts->data);
}

Struct* stsSearch(Structs* sts, const char* name) {
    for(int i = 0; i < sts->entries; i++) {
        if(strcmp(sts->data[i].name, name) == 0) {
            return sts->data + i;
        }
    }
    return NULL;
}

Struct* stsAdd(Structs* sts, const char* name) {
    if(sts->entries >= sts->capacity) {
        sts->capacity *= 2;
        sts->data = realloc(sts->data, sizeof(Struct) * sts->capacity);
    }
    int index = sts->entries++;
    sts->data[index].id = index;
    sts->data[index].amount = 0;
    sts->data[index].name = name;
    sts->data[index].vars = NULL;
    return sts->data + index;
}