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

#include "argo.h"
#include "global.h"
#include "debug.h"

ARGO_VALUE *argo_get_next_value();
char argo_get_next_char(FILE *f);
int argo_get_prev_char(FILE *f);
int print_error_helper();
int argo_read_string(ARGO_STRING *s, FILE *f);
int argo_read_number(ARGO_NUMBER *n, FILE *f);
ARGO_STRING *argo_read_number_helper(ARGO_CHAR *s, size_t length);
int argo_read_true(ARGO_BASIC *b, FILE *f);
int argo_read_false(ARGO_BASIC *b, FILE *f);
int argo_read_null(ARGO_BASIC *b, FILE *f);
int argo_read_object(ARGO_OBJECT *o, FILE *f);
int argo_read_object_helper(ARGO_VALUE *v, FILE *f);
int argo_read_array(ARGO_ARRAY *a, FILE *f);
int argo_write_basic(ARGO_BASIC *b, FILE *f);
int argo_write_object(ARGO_VALUE *o, FILE *f);
int argo_write_array(ARGO_VALUE *a, FILE *f);
int argo_write_number_helper(ARGO_STRING *s, FILE *f);
int print_indent(FILE *f);

char latest_char;
int argo_chars_read_prev;

/**
 * @brief  Read JSON input from a specified input stream, parse it,
 * and return a data structure representing the corresponding value.
 * @details  This function reads a sequence of 8-bit bytes from
 * a specified input stream and attempts to parse it as a JSON value,
 * according to the JSON syntax standard.  If the input can be
 * successfully parsed, then a pointer to a data structure representing
 * the corresponding value is returned.  See the assignment handout for
 * information on the JSON syntax standard and how parsing can be
 * accomplished.  As discussed in the assignment handout, the returned
 * pointer must be to one of the elements of the argo_value_storage
 * array that is defined in the const.h header file.
 * In case of an error (these include failure of the input to conform
 * to the JSON standard, premature EOF on the input stream, as well as
 * other I/O errors), a one-line error message is output to standard error
 * and a NULL pointer value is returned.
 *
 * @param f  Input stream from which JSON is to be read.
 * @return  A valid pointer if the operation is completely successful,
 * NULL if there is any error.
 */
ARGO_VALUE *argo_read_value(FILE *f)
{
    ARGO_VALUE *v = argo_get_next_value();
    int i = argo_get_next_char(f);
    while (i != EOF)
    {
        if (argo_is_whitespace(i))
        {
            i = argo_get_next_char(f);
            continue;
        }
        else if (argo_is_control(i))
        {
            print_error_helper(i);
            return NULL;
        }
        else if (argo_is_digit(i) || i == '-')
        {
            argo_get_prev_char(f);
            if (argo_read_number(&v->content.number, f))
            {
                return NULL;
            }
            else
            {
                v->type = ARGO_NUMBER_TYPE;
                return v;
            }
        }
        else if (i == '{')
        {
            if (argo_read_object(&v->content.object, f))
            {
                return NULL;
            }
            else
            {
                v->type = ARGO_OBJECT_TYPE;
                return v;
            };
        }
        else if (i == '[')
        {
            if (argo_read_array(&v->content.array, f))
            {
                return NULL;
            }
            else
            {
                v->type = ARGO_ARRAY_TYPE;
                return v;
            };
        }
        else if (i == '"')
        {
            if (argo_read_string(&v->content.string, f))
            {
                return NULL;
            }
            else
            {
                v->type = ARGO_STRING_TYPE;
                return v;
            };
        }
        else if (i == 't')
        {
            if (argo_read_true(&v->content.basic, f))
            {
                return NULL;
            }
            else
            {
                v->type = ARGO_BASIC_TYPE;
                return v;
            };
        }
        else if (i == 'f')
        {
            if (argo_read_false(&v->content.basic, f))
            {
                return NULL;
            }
            else
            {
                v->type = ARGO_BASIC_TYPE;
                return v;
            }
        }
        else if (i == 'n')
        {
            if (argo_read_null(&v->content.basic, f))
            {
                return NULL;
            }
            else
            {
                v->type = ARGO_BASIC_TYPE;
                return v;
            }
        }
        else
        {
            print_error_helper();
            return NULL;
        }
        argo_get_next_char(f);
    }

    return NULL;
}

ARGO_VALUE *argo_get_next_value()
{
    ARGO_VALUE *v = argo_value_storage;
    if (argo_next_value >= NUM_ARGO_VALUES)
    {
        fprintf(stderr, "Nums of values exceed the maxium \"%d\" values.", NUM_ARGO_VALUES);
        abort();
    }
    v += argo_next_value++;
    return v;
}
int print_error_helper()
{
    fprintf(stderr, "Unexpected charactor %c at [%d, %d].", latest_char, argo_lines_read, argo_chars_read);
    return -1;
}

char argo_get_next_char(FILE *f)
{
    char c = fgetc(f);
    latest_char = c;
    argo_chars_read_prev = argo_chars_read;
    argo_chars_read++;
    if (c == '\n')
    {
        argo_lines_read++;
        argo_chars_read = 0;
    }
    return c;
}

int argo_get_prev_char(FILE *f)
{
    char c = ungetc(latest_char, f);
    argo_chars_read--;
    if (c == '\n')
    {
        argo_lines_read--;
        argo_chars_read = argo_chars_read_prev;
    }
    return 0;
}

/**
 * @brief  Read JSON input from a specified input stream, attempt to
 * parse it as a JSON string literal, and return a data structure
 * representing the corresponding string.
 * @details  This function reads a sequence of 8-bit bytes from
 * a specified input stream and attempts to parse it as a JSON string
 * literal, according to the JSON syntax standard.  If the input can be
 * successfully parsed, then a pointer to a data structure representing
 * the corresponding value is returned.
 * In case of an error (these include failure of the input to conform
 * to the JSON standard, premature EOF on the input stream, as well as
 * other I/O errors), a one-line error message is output to standard error
 * and a NULL pointer value is returned.
 *
 * @param f  Input stream from which JSON is to be read.
 * @return  Zero if the operation is completely successful,
 * nonzero if there is any error.
 */
int argo_read_string(ARGO_STRING *s, FILE *f)
{
    ARGO_CHAR c = argo_get_next_char(f);
    while (c != EOF)
    {
        if (argo_is_control(c))
        {
            return print_error_helper();
        }
        if (c == '"')
        {
            break;
        }
        if (c == '\\')
        {
            c = argo_get_next_char(f);
            int tmp = 0, value = 0;
            switch (c)
            {
            case '/':
                c = '/';
                break;
            case '"':
                c = '"';
                break;
            case '\\':
                c = '\\';
                break;
            case 'b':
                c = '\b';
                break;
            case 'f':
                c = '\f';
                break;
            case 'n':
                c = '\n';
                break;
            case 'r':
                c = '\r';
                break;
            case 't':
                c = '\t';
                break;
            case 'u':

                for (size_t i = 0; i < 4; i++)
                {
                    c = argo_get_next_char(f);
                    if (!argo_is_hex(c))
                    {
                        return print_error_helper();
                    }
                    if (argo_is_digit(c))
                    {
                        tmp = c - '0';
                    }
                    else if (c <= 'f' && c >= 'a')
                    {
                        tmp = c - 'a' + 10;
                    }
                    else
                    {
                        tmp = c - 'A' + 10;
                    }
                    value = value * 16 + tmp;
                }
                c = value;
                break;
            default:
                break;
            }
        }
        argo_append_char(s, c);
        c = argo_get_next_char(f);
    }
    return 0;
}

/**
 * @brief  Read JSON input from a specified input stream, attempt to
 * parse it as a JSON number, and return a data structure representing
 * the corresponding number.
 * @details  This function reads a sequence of 8-bit bytes from
 * a specified input stream and attempts to parse it as a JSON numeric
 * literal, according to the JSON syntax standard.  If the input can be
 * successfully parsed, then a pointer to a data structure representing
 * the corresponding value is returned.  The returned value must contain
 * (1) a string consisting of the actual sequence of characters read from
 * the input stream; (2) a floating point representation of the corresponding
 * value; and (3) an integer representation of the corresponding value,
 * in case the input literal did not contain any fraction or exponent parts.
 * In case of an error (these include failure of the input to conform
 * to the JSON standard, premature EOF on the input stream, as well as
 * other I/O errors), a one-line error message is output to standard error
 * and a NULL pointer value is returned.
 *
 * @param f  Input stream from which JSON is to be read.
 * @return  Zero if the operation is completely successful,
 * nonzero if there is any error.
 */
int argo_read_number(ARGO_NUMBER *n, FILE *f)
{
    int has_exp = 0, num_started = 0, exp_started = 0, has_separator = 0, valid_int = 1, valid_float = 1, valid_string = 1, num_is_positive = 1, digits_after_separator = 0;
    long int_value = 0;
    double float_value = 0;
    ARGO_CHAR *string_value = argo_digits;
    size_t length = 0;
    char c = argo_get_next_char(f);
    while (1)
    {
        if (!(argo_is_digit(c) || c == '+' || c == '-' || c == 'e' || c == 'E' || c == '.'))
        {
            break;
        }
        if (length < 9)
        {
            ARGO_CHAR *ptr = string_value;
            ptr += length;
            if (c == 'E')
            {
                c = 'e';
            }

            *ptr = c;
            length++;
        }
        else
        {
            valid_string = 0;
        }
        if (argo_is_digit(c))
        {
            if (valid_int)
            {
                int_value = int_value * 10 + (c - '0');
                if (num_started && int_value == 0 && c == '0')
                {
                    return print_error_helper();
                }
            }
            if (valid_float)
            {
                if (has_separator)
                {
                    digits_after_separator++;
                    int divide_num = 1;
                    for (size_t i = 0; i < digits_after_separator; i++)
                    {
                        divide_num *= 10;
                    }
                    float_value += ((double)(c - '0') / (divide_num));
                }
                else
                {
                    float_value = float_value * 10 + (c - '0');
                }
            }
        }
        switch (c)
        {
        case '-':
            if (!num_started)
            {
                num_is_positive = -1;
                c = argo_get_next_char(f);
                continue;
            }
            else if (has_exp && !exp_started)
            {
            }
            else
            {
                return print_error_helper();
            }
            break;
        case '+':
            if (has_exp && !exp_started)
            {
            }
            else
            {
                return print_error_helper();
            }
            break;
        case 'e':
        case 'E':
            if (has_exp)
            {
                return print_error_helper();
            }
            valid_float = 0;
            valid_int = 0;
            has_exp = 1;
            c = argo_get_next_char(f);
            continue;
            break;
        case '.':
            if (has_separator)
            {
                return print_error_helper();
            }
            valid_int = 0;
            has_separator = 1;
            break;
        default:
            break;
        }
        num_started = 1;
        if (has_exp)
        {
            exp_started = 1;
        }
        c = argo_get_next_char(f);
    }
    if (!valid_int && !valid_float && !valid_string)
    {
        fprintf(stderr, "num at [%d, %d] is in exponential format and too long(larger than 10 digits", argo_lines_read, argo_chars_read);
    }
    int_value *= num_is_positive;
    float_value *= num_is_positive;
    n->int_value = int_value;
    n->float_value = float_value;
    for (size_t i = 0; i < length; i++)
    {
        ARGO_CHAR *tmp = string_value;
        tmp += (i);
        argo_append_char(&n->string_value, *tmp);
    }
    ARGO_STRING *tmp = argo_read_number_helper(n->string_value.content, n->string_value.length);
    n->string_value.capacity = tmp->capacity;
    n->string_value.length = tmp->length;
    n->string_value.content = tmp->content;
    n->valid_int = valid_int;
    n->valid_float = valid_float;
    n->valid_string = valid_string;
    argo_get_prev_char(f);
    return 0;
}

ARGO_STRING *argo_read_number_helper(ARGO_CHAR *s, size_t length)
{
    ARGO_VALUE *tmp = argo_get_next_value();
    int *c = s;
    int num = 0, exp = 0, exp_tmp = 0, is_before_separator = 1, num_is_plus = 1, started = 0, exp_is_plus = 1, is_num = 1, integer_is_zero = 0, fractional_is_zero = 1;
    for (size_t index = 0; index < length; index++, c++)
    {
        if (is_num)
        {
            if (argo_is_digit(*c))
            {
                num = num * 10 + (*c - '0');
                if (is_before_separator && num)
                {
                    exp += 1;
                }
                if (integer_is_zero && fractional_is_zero)
                {
                    if (*c == '0')
                    {
                        exp -= 1;
                    }
                    else
                    {
                        fractional_is_zero = 0;
                    }
                }
            }
            else if (argo_is_exponent(*c))
            {
                started = 0, is_num = 0;
                continue;
            }
            else if (*c == '-' && !started)
            {
                num_is_plus = -1;
            }
            else if (*c == '+' && !started)
            {
            }
            else if (*c == '.' && is_before_separator)
            {
                is_before_separator = 0;
                if (!num)
                {
                    integer_is_zero = 1;
                }
            }
            else
            {
                return NULL;
            }
            started = 1;
        }
        else
        {
            if (argo_is_digit(*c))
            {
                exp_tmp = exp_tmp * 10 + (*c - '0');
            }
            else if (*c == '-' && !started)
            {
                exp_is_plus = -1;
            }
            else if (*c == '+' && !started)
            {
            }
            else
            {
                return NULL;
            }
            started = 1;
        }
    }
    exp = exp_is_plus * exp_tmp + exp;
    if (num_is_plus == -1)
    {
        argo_append_char(&tmp->content.string, '-');
    }
    while (num % 10 == 0 && num != 0)
    {
        num /= 10;
    }
    argo_append_char(&tmp->content.string, '0');
    argo_append_char(&tmp->content.string, '.');
    int rev = 0;
    while (num > 0)
    {
        int lsb = num % 10;
        num /= 10;
        rev = rev * 10 + lsb;
    }
    while (rev > 0)
    {
        int lsb = rev % 10;
        rev /= 10;
        argo_append_char(&tmp->content.string, (lsb + '0'));
    }

    if (exp)
    {
        argo_append_char(&tmp->content.string, 'e');
        if (exp < 0)
        {
            argo_append_char(&tmp->content.string, '-');
            exp *= -1;
        }
        while (exp > 0)
        {
            int lsb = exp % 10;
            exp /= 10;
            rev = rev * 10 + lsb;
        }
        while (rev > 0)
        {
            int lsb = rev % 10;
            rev /= 10;
            argo_append_char(&tmp->content.string, (lsb + '0'));
        }
    }
    return &tmp->content.string;
}

int argo_read_true(ARGO_BASIC *b, FILE *f)
{
    char c = argo_get_next_char(f);
    if (c == 'r')
    {
        c = argo_get_next_char(f);
        if (c == 'u')
        {
            c = argo_get_next_char(f);
            if (c == 'e')
            {
                *b = ARGO_TRUE;
                return 0;
            }
        }
    }
    return print_error_helper();
}

int argo_read_false(ARGO_BASIC *b, FILE *f)
{
    char c = argo_get_next_char(f);
    if (c == 'a')
    {
        c = argo_get_next_char(f);
        if (c == 'l')
        {
            c = argo_get_next_char(f);
            if (c == 's')
            {
                c = argo_get_next_char(f);
                if (c == 'e')
                {
                    *b = ARGO_FALSE;
                    return 0;
                }
            }
        }
    }
    return print_error_helper();
}

int argo_read_null(ARGO_BASIC *b, FILE *f)
{
    char c = argo_get_next_char(f);
    if (c == 'u')
    {
        c = argo_get_next_char(f);
        if (c == 'l')
        {
            c = argo_get_next_char(f);
            if (c == 'l')
            {
                *b = ARGO_NULL;
                return 0;
            }
        }
    }
    return print_error_helper();
}

int argo_read_object(ARGO_OBJECT *o, FILE *f)
{
    char c = argo_get_next_char(f);
    o->member_list = argo_get_next_value();
    ARGO_VALUE *starter = o->member_list;
    starter->type = ARGO_NO_TYPE;
    starter->next = starter;
    starter->prev = starter;
    ARGO_VALUE *prev = starter;
    ARGO_VALUE *next = argo_get_next_value();

    while (1)
    {
        while (argo_is_whitespace(c))
        {
            c = argo_get_next_char(f);
        }

        if (c == '}')
        {
            break;
        }
        else if (c == '\"')
        {
            if (argo_read_string(&next->name, f))
            {
                return -1;
            }
        }
        else
        {
            return -1;
        }
        c = argo_get_next_char(f);

        while (argo_is_whitespace(c))
        {
            c = argo_get_next_char(f);
        }

        if (c != ':')
        {
            return -1;
        }
        if (argo_read_object_helper(next, f))
        {
            return -1;
        }
        next->prev = prev;
        prev->next = next;
        next->next = starter;
        starter->prev = next;
        prev = next;
        next = argo_get_next_value();
        c = argo_get_next_char(f);
        while (argo_is_whitespace(c))
        {
            c = argo_get_next_char(f);
        }
        if (c == '}')
        {
            break;
        }
        else if (c == ',')
        {
            c = argo_get_next_char(f);
            continue;
        }
    }
    return 0;
}

int argo_read_object_helper(ARGO_VALUE *v, FILE *f)
{
    ARGO_VALUE *tmp = argo_read_value(f);
    if (tmp == NULL)
    {
        return -1;
    }
    v->type = tmp->type;
    v->content = tmp->content;
    return 0;
}

int argo_read_array(ARGO_ARRAY *a, FILE *f)
{
    char c = argo_get_next_char(f);
    a->element_list = argo_get_next_value();
    ARGO_VALUE *starter = a->element_list;
    starter->type = ARGO_NO_TYPE;
    starter->next = starter;
    starter->prev = starter;
    ARGO_VALUE *prev = starter;
    ARGO_VALUE *next = argo_get_next_value();

    while (1)
    {
        while (argo_is_whitespace(c))
        {
            c = argo_get_next_char(f);
        }

        if (c == ']')
        {
            break;
        }
        else
        {
            argo_get_prev_char(f);
        }

        if (argo_read_object_helper(next, f))
        {
            return -1;
        }
        next->prev = prev;
        prev->next = next;
        next->next = starter;
        starter->prev = next;
        prev = next;
        next = argo_get_next_value();
        c = argo_get_next_char(f);
        while (argo_is_whitespace(c))
        {
            c = argo_get_next_char(f);
        }
        if (c == ']')
        {
            break;
        }
        else if (c == ',')
        {
            c = argo_get_next_char(f);
            continue;
        }
    }
    return 0;
}

/**
 * @brief  Write canonical JSON representing a specified value to
 * a specified output stream.
 * @details  Write canonical JSON representing a specified value
 * to specified output stream.  See the assignment document for a
 * detailed discussion of the data structure and what is meant by
 * canonical JSON.
 *
 * @param v  Data structure representing a value.
 * @param f  Output stream to which JSON is to be written.
 * @return  Zero if the operation is completely successful,
 * nonzero if there is any error.
 */
int argo_write_value(ARGO_VALUE *v, FILE *f)
{
    switch (v->type)
    {
    case ARGO_BASIC_TYPE:
        return argo_write_basic(&v->content.basic, f);
        break;
    case ARGO_NUMBER_TYPE:
        return argo_write_number(&v->content.number, f);
        break;
    case ARGO_STRING_TYPE:
        return argo_write_string(&v->content.string, f);
        break;
    case ARGO_OBJECT_TYPE:
        return argo_write_object(v->content.object.member_list, f);
        break;
    case ARGO_ARRAY_TYPE:
        return argo_write_array(v->content.array.element_list, f);
        break;
    default:
        return 1;
        break;
    }
    return 0;
}

/**
 * @brief  Write canonical JSON representing a specified string
 * to a specified output stream.
 * @details  Write canonical JSON representing a specified string
 * to specified output stream.  See the assignment document for a
 * detailed discussion of the data structure and what is meant by
 * canonical JSON.  The argument string may contain any sequence of
 * Unicode code points and the output is a JSON string literal,
 * represented using only 8-bit bytes.  Therefore, any Unicode code
 * with a value greater than or equal to U+00FF cannot appear directly
 * in the output and must be represented by an escape sequence.
 * There are other requirements on the use of escape sequences;
 * see the assignment handout for details.
 *
 * @param v  Data structure representing a string (a sequence of
 * Unicode code points).
 * @param f  Output stream to which JSON is to be written.
 * @return  Zero if the operation is completely successful,
 * nonzero if there is any error.
 */
int argo_write_basic(ARGO_BASIC *b, FILE *f)
{
    switch (*b)
    {
    case ARGO_NULL:
        fprintf(f, ARGO_NULL_TOKEN);
        break;
    case ARGO_TRUE:
        fprintf(f, ARGO_TRUE_TOKEN);
        break;
    case ARGO_FALSE:
        fprintf(f, ARGO_FALSE_TOKEN);
        break;
    default:
        return -1;
        break;
    }
    if (indent_level == 0 && global_options & PRETTY_PRINT_OPTION)
    {
        fprintf(f, "\n");
    }
    return 0;
}

/**
 * @brief  Write canonical JSON representing a specified number
 * to a specified output stream.
 * @details  Write canonical JSON representing a specified number
 * to specified output stream.  See the assignment document for a
 * detailed discussion of the data structure and what is meant by
 * canonical JSON.  The argument number may contain representations
 * of the number as any or all of: string conforming to the
 * specification for a JSON number (but not necessarily canonical),
 * integer value, or floating point value.  This function should
 * be able to work properly regardless of which subset of these
 * representations is present.
 *
 * @param v  Data structure representing a number.
 * @param f  Output stream to which JSON is to be written.
 * @return  Zero if the operation is completely successful,
 * nonzero if there is any error.
 */

int argo_write_number(ARGO_NUMBER *n, FILE *f)
{
    if (n->valid_int)
    {
        fprintf(f, "%ld", n->int_value);
    }
    else if (n->valid_float && n->float_value < 1 && n->float_value >= 0.1)
    {
        fprintf(f, "%f", n->float_value);
    }
    else if (n->valid_string)
    {
        return argo_write_number_helper(&n->string_value, f);
    }
    else
    {
        return -1;
    }
    if (indent_level == 0 && global_options & PRETTY_PRINT_OPTION)
    {
        fprintf(f, "\n");
    }
    return 0;
}

int argo_write_number_helper(ARGO_STRING *s, FILE *f)
{
    int *c = s->content;
    int num = 0, exp = 0, exp_tmp = 0, is_before_separator = 1, num_is_plus = 1, started = 0, exp_is_plus = 1, is_num = 1, integer_is_zero = 0, fractional_is_zero = 1;
    for (size_t index = 0; index < s->length; index++, c++)
    {
        if (is_num)
        {
            if (argo_is_digit(*c))
            {
                num = num * 10 + (*c - '0');
                if (is_before_separator && num)
                {
                    exp += 1;
                }
                if (integer_is_zero && fractional_is_zero)
                {
                    if (*c == '0')
                    {
                        exp -= 1;
                    }
                    else
                    {
                        fractional_is_zero = 0;
                    }
                }
            }
            else if (argo_is_exponent(*c))
            {
                started = 0, is_num = 0;
                continue;
            }
            else if (*c == '-' && !started)
            {
                num_is_plus = -1;
            }
            else if (*c == '+' && !started)
            {
            }
            else if (*c == '.' && is_before_separator)
            {
                is_before_separator = 0;
                if (!num)
                {
                    integer_is_zero = 1;
                }
            }
            else
            {
                return -1;
            }
            started = 1;
        }
        else
        {
            if (argo_is_digit(*c))
            {
                exp_tmp = exp_tmp * 10 + (*c - '0');
            }
            else if (*c == '-' && !started)
            {
                exp_is_plus = -1;
            }
            else if (*c == '+' && !started)
            {
            }
            else
            {
                return -1;
            }
            started = 1;
        }
    }
    exp = exp_is_plus * exp_tmp + exp;
    if (num_is_plus == -1)
    {
        fprintf(f, "-");
    }
    while (num % 10 == 0 && num != 0)
    {
        num /= 10;
    }

    fprintf(f, "0.%d", num);
    if (exp && num != 0)
    {
        fprintf(f, "e%d", exp);
    }
    return 0;
}
int argo_write_string(ARGO_STRING *s, FILE *f)
{
    fprintf(f, "\"");
    int *c = s->content;
    for (size_t index = 0; index < s->length; index++, c++)
    {
        switch (*c)
        {
        case ARGO_BS:
            fprintf(f, "\\b");
            break;
        case ARGO_FF:
            fprintf(f, "\\f");
            break;
        case ARGO_LF:
            fprintf(f, "\\n");
            break;
        case ARGO_CR:
            fprintf(f, "\\r");
            break;
        case ARGO_HT:
            fprintf(f, "\\t");
            break;
        case ARGO_BSLASH:
            fprintf(f, "\\\\");
            break;
        case ARGO_QUOTE:
            fprintf(f, "\\\"");
            break;
        default:
            if (argo_is_control(*c))
            {
                int tmp = 0xf;
                char lsb = *c & 0xff;
                if (lsb <= 0xf)
                {
                    fprintf(f, "\\u000%x", lsb);
                }
                else
                {
                    fprintf(f, "\\u00%x", lsb);
                }
            }
            else
            {
                if (*c <= 0xff)
                {
                    fprintf(f, "%c", *c);
                }
                else if (*c <= 0xfff)
                {
                    fprintf(f, "\\u0%x", *c);
                }
                else
                {
                    fprintf(f, "\\u%x", *c);
                }
            }
            break;
        }
    }
    fprintf(f, "\"");
    if (indent_level == 0 && global_options & PRETTY_PRINT_OPTION)
    {
        fprintf(f, "\n");
    }
    return 0;
}

int argo_write_object(ARGO_VALUE *o, FILE *f)
{
    fprintf(f, "{");
    indent_level++;
    ARGO_VALUE *ptr = o;
    while (ptr->next->type != ARGO_NO_TYPE)
    {
        print_indent(f);
        argo_write_string(&ptr->next->name, f);
        fprintf(f, ":");
        if (global_options & PRETTY_PRINT_OPTION)
        {
            fprintf(f, " ");
        }

        argo_write_value(ptr->next, f);
        ptr = ptr->next;
        if (ptr->next->type != ARGO_NO_TYPE)
        {
            fprintf(f, ",");
        }
    }
    indent_level--;
    print_indent(f);
    fprintf(f, "}");
    if (indent_level == 0 && global_options & PRETTY_PRINT_OPTION)
    {
        fprintf(f, "\n");
    }
    return 0;
}

int argo_write_array(ARGO_VALUE *a, FILE *f)
{
    fprintf(f, "[");
    indent_level++;
    ARGO_VALUE *ptr = a;
    while (ptr->next->type != ARGO_NO_TYPE)
    {
        print_indent(f);
        argo_write_value(ptr->next, f);
        ptr = ptr->next;
        if (ptr->next->type != ARGO_NO_TYPE)
        {
            fprintf(f, ",");
        }
    }
    indent_level--;
    print_indent(f);
    fprintf(f, "]");
    if (indent_level == 0 && global_options & PRETTY_PRINT_OPTION)
    {
        fprintf(f, "\n");
    }

    return 0;
}

int print_indent(FILE *f)
{
    if (global_options & PRETTY_PRINT_OPTION)
    {
        fprintf(f, "\n");
        for (size_t i = 0; i < indent_level; i++)
        {
            for (size_t j = 0; j < (global_options & 0x0fffffff); j++)
            {
                fprintf(f, " ");
            }
        }
    }
    return 0;
}