#include "arc/math/config.h"
#include <stdio.h>
#include <stdlib.h>
#include "arc/std/array.h"
#include "arc/std/string.h"
#include "arc/std/errno.h"
#include "arc/std/config.h"
#include "arc/math/point.h"
#include "arc/math/rectangle.h"

// #define ARC_DEFAULT_CONFIG
#include "arc/std/defaults/config.h"

void ARC_MathConfig_Init(ARC_Config *config){
    ARC_Config_AddKeyCString(config, (char *)"ARC_Point" ,  9, ARC_Point_Read    , ARC_Point_Delete    );
    ARC_Config_AddKeyCString(config, (char *)"ARC_Rect"  ,  8, ARC_Rect_Read     , ARC_Rect_Delete     );
    ARC_Config_AddKeyCString(config, (char *)"ARC_Rect[]", 10, ARC_RectArray_Read, ARC_RectArray_Delete);
}

uint64_t ARC_MathConfig_GetIndexAndErrorCheck(ARC_String *string, char *search, uint64_t searchLength){
    uint64_t separator = ARC_String_FindCString(string, ",", 1);

    if(separator == ~(uint64_t)0){
        arc_errno = ARC_ERRNO_DATA;
    }

    return separator;
}

uint8_t ARC_Point_Read(ARC_Config *config, ARC_String *string, void **value){
    ARC_Config_Get(config, string, value);
    if(*value){
        return 1;
    }

    if(string->data[0] != '{' || string->data[string->length - 1] != '}'){
        ARC_DEBUG_LOG_ERROR_WITH_VARIABLES("in ARC_Point_Read(config, string, value); no matching curly braces: %s", string->data);
        arc_errno = ARC_ERRNO_DATA;
        return 0;
    }

    uint64_t separator = ARC_MathConfig_GetIndexAndErrorCheck(string, ",", 1);
    if(arc_errno){
        return 0;
    }

    ARC_String *xString, *yString;
    ARC_String_CopySubstring(&xString, string, 1            , separator - 1                   );
    ARC_String_CopySubstring(&yString, string, separator + 1, string->length - (separator + 2));

    ARC_Point *point = (ARC_Point *)malloc(sizeof(ARC_Point));
    point->x = (int32_t)ARC_String_ToInt64_t(xString);
    point->y = (int32_t)ARC_String_ToInt64_t(yString);

    ARC_String_Destroy(xString);
    ARC_String_Destroy(yString);

    *value = point;
    return 0;
}

uint8_t ARC_Rect_Read(ARC_Config *config, ARC_String *string, void **value){
    ARC_Config_Get(config, string, value);
    if(*value){
        return 1;
    }

    if(string->data[0] != '{' || string->data[string->length - 1] != '}'){
        arc_errno = ARC_ERRNO_DATA;
        ARC_DEBUG_LOG_ERROR_WITH_VARIABLES("in ARC_Rect_Read(config, string, value); no matching curly braces: %s", string->data);
        return 0;
    }

    ARC_String *current;
    ARC_String_CopySubstring(&current, string, 1, string->length - 2);

    ARC_String *temp, *tempStripped;
    int32_t x, y, w, h;
    int64_t separator;

    //x
    separator = ARC_MathConfig_GetIndexAndErrorCheck(current, ",", 1);
    if(arc_errno){
        return 0;
    }

    ARC_String_CopySubstring(&temp, current, 0, separator - 1);
    ARC_String_StripEndsWhitespace(&tempStripped, temp);
    x = ARC_String_ToInt64_t(tempStripped);
    ARC_String_Destroy(temp);
    ARC_String_Destroy(tempStripped);

    temp = current;
    ARC_String_CopySubstring(&current, temp, separator + 1, temp->length - (separator + 1));
    ARC_String_Destroy(temp);

    //y
    separator = ARC_MathConfig_GetIndexAndErrorCheck(current, ",", 1);
    if(arc_errno){
        return 0;
    }

    ARC_String_CopySubstring(&temp, current, 0, separator - 1);
    ARC_String_StripEndsWhitespace(&tempStripped, temp);
    y = ARC_String_ToInt64_t(tempStripped);
    ARC_String_Destroy(temp);
    ARC_String_Destroy(tempStripped);

    temp = current;
    ARC_String_CopySubstring(&current, temp, separator + 1, temp->length - (separator + 1));
    ARC_String_Destroy(temp);

    //w
    separator = ARC_MathConfig_GetIndexAndErrorCheck(current, ",", 1);
    if(arc_errno){
        return 0;
    }

    ARC_String_CopySubstring(&temp, current, 0, separator - 1);
    ARC_String_StripEndsWhitespace(&tempStripped, temp);
    w = ARC_String_ToInt64_t(tempStripped);
    ARC_String_Destroy(temp);
    ARC_String_Destroy(tempStripped);

    temp = current;
    ARC_String_CopySubstring(&current, temp, separator + 1, temp->length - (separator + 1));
    ARC_String_Destroy(temp);

    //h
    separator = current->length;
    if(arc_errno){
        return 0;
    }

    ARC_String_CopySubstring(&temp, current, 0, separator);
    ARC_String_StripEndsWhitespace(&tempStripped, temp);
    h = ARC_String_ToInt64_t(tempStripped);
    ARC_String_Destroy(temp);
    ARC_String_Destroy(tempStripped);
    ARC_String_Destroy(current);

    *value = malloc(sizeof(ARC_Rect));
    ((ARC_Rect *) *value)->x = x;
    ((ARC_Rect *) *value)->y = y;
    ((ARC_Rect *) *value)->w = w;
    ((ARC_Rect *) *value)->h = h;
    return 0;
}

void ARC_RectArray_ReadRect(ARC_Config* config, ARC_String *stripped, uint64_t index, uint64_t length, uint64_t *arrayIndex, void **value){
    ARC_String *substr, *temp;
    ARC_String_CopySubstring(&temp, stripped, index, length);
    ARC_String_StripEndsWhitespace(&substr, temp);
    ARC_String_Destroy(temp);

    // reading in reference
    ARC_Rect *tempRect;
    ARC_Config_Get(config, substr, (void **) &tempRect);
    if(tempRect){
        ARC_String_Destroy(substr);

        ((ARC_Rect *)((ARC_Array *) *value)->data)[*arrayIndex] = *tempRect;
        ++*arrayIndex;

        return;
    }

    //reading in value
    ARC_Rect_Read(config, substr, (void **) &tempRect);
    if(arc_errno){
        ARC_DEBUG_LOG_ERROR_WITH_VARIABLES("in ARC_RectArray_ReadRect(config, string, index, length, arrayIndex, value); failed to read rect: %s", substr->data);
        ARC_String_Destroy(substr);
        return;
    }

    ((ARC_Rect *)((ARC_Array *) *value)->data)[*arrayIndex] = *tempRect;
    ++*arrayIndex;

    ARC_Rect_Delete(config, substr, (void *)tempRect);
    ARC_String_Destroy(substr);
}

uint8_t ARC_RectArray_Read(ARC_Config* config, ARC_String *string, void **value){
    ARC_Config_Get(config, string, value);
    if(*value){
        return 1;
    }

    if(string->data[0] != '{' || string->data[string->length - 1] != '}'){
        arc_errno = ARC_ERRNO_DATA;
        ARC_DEBUG_LOG_ERROR_WITH_VARIABLES("in ARC_RectArray_Read(config, string, value); no matching curly braces: %s", string->data);
        return 0;
    }

    ARC_String *temp, *stripped;
    ARC_String_CopySubstring(&temp, string, 1, string->length - 2);
    ARC_String_StripEndsWhitespace(&stripped, temp);
    ARC_String_Destroy(temp);

    uint64_t arraySize = 1;
    int64_t  encapsulated = 0;
    for(uint64_t i = 0; i < stripped->length; i++){
        if(stripped->data[i] == '{'){
            encapsulated++;
            continue;
        }

        if(stripped->data[i] == '}'){
            encapsulated--;
            continue;
        }

        if(!encapsulated && stripped->data[i] == ','){
            arraySize++;
        }
    }
    
    if(encapsulated){
        arc_errno = ARC_ERRNO_DATA;
        //TODO: Fix this for windows SMFH
        // ARC_DEBUG_LOG(arc_errno, "in ARC_RectArray_Read(config, data, subdata, value); after looping encapsulated was %ld", encapsulated);
        ARC_String_Destroy(stripped);
        return 0;
    }

    *value = malloc(sizeof(ARC_Array));
    ((ARC_Array *) *value)->data = malloc(sizeof(ARC_Rect) * arraySize);
    ((ARC_Array *) *value)->size = arraySize;

    uint64_t index = 0;
    arraySize = 0;
    encapsulated = 0;
    for(uint64_t i = 0; i < stripped->length; i++){
        if(stripped->data[i] == '{'){
            encapsulated++;
            continue;
        }

        if(stripped->data[i] == '}'){
            encapsulated--;
            continue;
        }

        if(!encapsulated && stripped->data[i] == ','){
            ARC_RectArray_ReadRect(config, stripped, index, i - index, &arraySize, value);
            if(arc_errno){
                return 0;
            }

            index = i + 1;

            if(arraySize == ((ARC_Array *) *value)->size){
                break;
            }
        }
    }

    if(arraySize != ((ARC_Array *) *value)->size){
        ARC_RectArray_ReadRect(config, stripped, index, stripped->length - index, &arraySize, value);
    }
    ARC_String_Destroy(stripped);
    return 0;
}

void ARC_Point_Delete(ARC_Config* config, ARC_String *string, void *value){
    free((ARC_Point *)value);
}

void ARC_Rect_Delete(ARC_Config* config, ARC_String *string, void *value){
    free((ARC_Rect *)value);
}

void ARC_RectArray_Delete(ARC_Config* config, ARC_String *string, void *value){
    //TODO free value->data
    free((ARC_Array *)value);
}