#include "variant.h"
#include "ctype.h"
#include "mirror.h"
#include "string.h"
#include "strutil.h"
#include "drop.h"
#include "debug.h"

Variant variant_from_str(const char* str) {
    size_t length = strlen(str);
    if(isdigit(str[0])) {
        // variant is a number
        // convert to double
        return NumberVariant(atof(str));
    } else if(str[0] == '"') {
        // variant should be a string
        // format: "<content>"
        // copy the input string without the quotes
        size_t result_length = strfirst(str+1, str+length, '"');
        char* string = malloc(result_length);
        strncpy(string, str+1, result_length-1);
        string[result_length] = '\0';
        return StringVariant(string, result_length);
    } else if(strncmp(str, "Vector(", 7) == 0) {
        // variant is a vector
        // format: Vector(<x>, <y>)
        // get the location of the brackets
        long close_bracket = strfirst(str, str + length, ')');
        if(close_bracket == -1) goto variant_from_str_err;
        char buf[24]; buf[23] = '\0';
        // copy what is within the brackets
        // 7 is the number of characters in "Vector(", which should be skipped
        strncpy(buf, str + 7, close_bracket - 7);
        length = strlen(buf);
        long comma_ind = strfirst(buf, buf+length, ',');
        if(comma_ind == -1) goto variant_from_str_err;
        // split the string at the comma by inserting a null terminator
        buf[comma_ind] = '\0';
        return VectorVariant(MakeVector(atof(buf), atof(buf + comma_ind + 1)));
    }
variant_from_str_err:
    return UndefinedVariant();
}

void destroy_variant(Variant* self) {
    destroy_contained(self);
    free(self);
}

void destroy_contained(Variant* self) {
    switch(self->type) {
    default: break;
    case Variant_String:
        if(self->as_string != NULL)
            free(self->as_string);
        self->as_string = NULL;
        break;
    case Variant_Object:
        if(TC_MIRRORS(self->as_object, Drop)) {
            Drop drop = TC_CAST(self->as_object, Drop);
            drop.tc->drop(drop.data);
        }
        self->as_object.data = NULL;
        self->as_object.tc = NULL;
        break;
    }
}