#include "key_value_pair.h"

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

char *key_value_pair_to_string(key_value_pair *kv)
{
    char *str = malloc(sizeof(char) * (strlen(kv->key) + strlen(kv->value) + 1));
    if (str == NULL) return NULL;

    strcpy(str, kv->key);
    strcat(str, kv->value);

    return str;
}

char *key_value_pair_list_to_string(key_value_pair **head){
    key_value_pair *kvp_it = *head;

    char *ret = NULL;

    while(kvp_it != NULL){
        char *kvp2str = kvp_it->toString(kvp_it);
        if(kvp2str == NULL){
            kvp_it = kvp_it->next;
            continue;
        }
        if(!ret){
            ret = strdup(kvp2str);
            free(kvp2str);
            kvp_it = kvp_it->next;
        }
        else{
            char *tmp = realloc(ret, strlen(ret) + strlen(kvp2str) + 1);
            if(!tmp){
                free(ret);
                perror("Failed to reallocate memory for ret");
                free(kvp2str);
                return NULL;
            }
            ret = tmp;
            strcat(ret, kvp2str);
            free(kvp2str);
            kvp_it = kvp_it->next;
        }
    }
    return ret;
}

// initializes a new instance of key_value_pair with given values on given pointer
key_value_pair *init_key_value_pair(const char *key, const char *value)
{
    key_value_pair *kv = (key_value_pair*)malloc(sizeof(key_value_pair));
    if (kv == NULL)
    {
        perror("Failed to allocate memory for key_value_pair");
        return NULL;
    }
    kv->key = strdup(key);
    if (kv->key == NULL)
    {
        free(kv);
        perror("Failed to allocate memory for key_value_pair->key");
        return NULL;
    }
    kv->value = strdup(value);
    if (kv->value == NULL)
    {
        free(kv->key);
        free(kv);
        perror("Failed to allocate memory for key_value_pair->value");
        return NULL;
    }

    kv->next = NULL;
    kv->toString = key_value_pair_to_string;

    return kv;
}

// frees the given instance of struct key_value_pair
void free_key_value_pair(key_value_pair **kv)
{
    if (*kv == NULL) return;

    free((*kv)->key);
    free((*kv)->value);
    free(*kv);

    *kv = NULL;
}

// frees whole key_value_pair list structure
void free_key_value_pairs(key_value_pair **head, key_value_pair **tail)
{
    while (*head != NULL)
    {
        key_value_pair *toFree = *head;
        *head = (*head)->next;
        free_key_value_pair(&toFree);
    }
    *tail = NULL;
}


// adds given key_value_pair to list
int add_key_value_pair(key_value_pair *kv, key_value_pair **head, key_value_pair **tail)
{
    if (!head || !*head)
    {
        *head = kv;
        *tail = kv;
    }
    else
    {
        key_value_pair *iterator = *head;
        while (iterator->next != NULL)
        {
            if (strcmp(kv->key, iterator->key) == 0)
            {
                char *bak = iterator->value;
                iterator->value = (char*)realloc(iterator->value, strlen(iterator->value) + strlen(kv->value) + 1);
                if (iterator->value == NULL)
                {
                    iterator->value = bak;
                    return -1;
                }
                strcat(iterator->value, kv->value);
                free_key_value_pair(&kv);
                return 0;
            }
            iterator = iterator->next;
        }
        iterator->next = kv;
        *tail = kv;
    }
    return 0;
}

// removes given key_value_pair from list
void remove_key_value_pair(key_value_pair *kv, key_value_pair **head, key_value_pair **tail)
{
    if (*head == NULL || kv == NULL) return;
    if (*head == kv)
    {
        *head = (*head)->next;
        if (*tail == kv) *tail = NULL;
        return;
    }

    key_value_pair *iterator = *head;
    if (iterator == NULL) return;

    while (iterator != NULL)
    {
        if (iterator->next == kv)
        {
            iterator->next = kv->next;
            if (*tail == kv) *tail = iterator;
            return;
        }
        iterator = iterator->next;
    }
}

void extract_key_value_pairs(char *red_str, key_value_pair **head, key_value_pair **tail, bool doReduce){
    char *it = red_str;
    bool reading_value = false;
    char *word_start = it;
    size_t word_length = 0;
    size_t value_length = 0;

    while(true){
        if(!*it || (isalpha(*it) && reading_value)){
            char key[word_length + 1];
            strncpy(key, word_start, word_length);
            key[word_length] = '\0';
            char value[16];
            if(doReduce)
                snprintf(value, 16, "%d", (int)value_length);
            else{
                strncpy(value, word_start + word_length, value_length);
                value[value_length] = '\0';
            }

            key_value_pair *kv = init_key_value_pair(key, value);
            add_key_value_pair(kv, head, tail);

            // reset
            word_length = 0;
            value_length = 0;
            word_start = it;
            reading_value = false;

            if(!*it) break;
            continue;
        }
        if(isalpha(*it)){
            word_length++;
            it++;
        }
        else{
            reading_value = true; // reassigning true is faster than if checks every iteration
            value_length++;
            it++;
        }
    }
}
