/* lexer.c */

#include "computorv1.h"

// token is alphabet letter, like 'x' or 'y'
static bool token_is_variable(const char *input, int input_pos, int *token_size)
{
    if (ft_isalpha(input[input_pos]))
    {
        *token_size = 1;
        return true;
    }
    return false;
}

// token is int "123"
static bool token_is_number_int(const char *input, int input_pos, int *token_size)
{
    int number_size;
    int max_number_size;

    if (!ft_isdigit(input[input_pos]))
    {
        return false;
    }

    number_size = 1;
    max_number_size = 16; // max size for int
    while (number_size <= max_number_size)
    {
        if (ft_isdigit(input[input_pos + number_size]))
        {
            number_size++;
        }
        else if (input[input_pos + number_size] == '.')
        {
            if (ft_isdigit(input[input_pos + number_size + 1]))
            {
                // number is double
                return false;
            }
            else
                break;
        }
        else
            break;
    }
    if (number_size > max_number_size)
    {
        stop_errors(&input[input_pos]);
    }
    *token_size = number_size;
    return true;
}

// token is double "123.456"
static bool token_is_number_double(const char *input, int input_pos, int *token_size)
{
    int number_size;
    int max_number_size;
    bool has_dot;

    if (!ft_isdigit(input[input_pos]))
    {
        return false;
    }

    has_dot = false;
    number_size = 1;
    max_number_size = 24; // max char needed to represent double : 1 sign + 1 point + 17 fractinoal part + 5 exponent
    while (number_size <= max_number_size)
    {
        if (ft_isdigit(input[input_pos + number_size]))
        {
            number_size++;
        }
        else if (input[input_pos + number_size] == '.')
        {
            if (has_dot)
            {
                // number is not a valid token, it has 2 dots
                return false;
            }
            if (!ft_isdigit(input[input_pos + number_size + 1]))
            {
                // number is not valid token, it has a dot with no number after the dot
                return false;
            }
            has_dot = true;
            number_size++;
        }
        else
            break;
    }
    if (number_size > max_number_size)
    {
        stop_errors(&input[input_pos]);
    }
    *token_size = number_size;
    return true;
}

// token is superscript int (e.g., ², ³, ⁴, ⁵, etc.)
static bool token_is_number_int_super(const char *input, int input_pos, int *token_size)
{
    int digit_size = 0;
    int number_size = 0;
    int max_number_size = 16; // same max size as regular integers

    // check if first character is superscript
    if (!ft_isdigit_superscript(input + input_pos, NULL))
    {
        return false;
    }

    // iterate to find full superscript number
    while (number_size < max_number_size)
    {
        if (ft_isdigit_superscript(input + input_pos + number_size, &digit_size))
        {
            // Increment by the length of the UTF-8 character (2 or 3 bytes)
            number_size += digit_size;
        }
        else
        {
            break;
        }
    }

    if (number_size == 0)
    {
        return false;
    }

    *token_size = number_size;
    return true;
}

// token is '^' or "**"
static bool token_is_power(const char *input, int input_pos, int *token_size)
{
    if (input[input_pos] == '^')
    {
        *token_size = 1;
        return true;
    }
    if (ft_memcmp(&input[input_pos], "**", 2) == 0)
    {
        *token_size = 2;
        return true;
    }
    return false;
}

// token is '+'
static bool token_is_sign_plus(const char *input, int input_pos, int *token_size)
{
    if (input[input_pos] == '+')
    {
        *token_size = 1;
        return true;
    }
    return false;
}

// token is '-'
static bool token_is_sign_minus(const char *input, int input_pos, int *token_size)
{
    if (input[input_pos] == '-')
    {
        *token_size = 1;
        return true;
    }
    return false;
}

// token is '*'
static bool token_is_factor_multiplication(const char *input, int input_pos, int *token_size)
{
    if (input[input_pos] == '*')
    {
        *token_size = 1;
        return true;
    }
    return false;
}

// token is '/' or ':'
static bool token_is_factor_division(const char *input, int input_pos, int *token_size)
{
    if (input[input_pos] == '/')
    {
        *token_size = 1;
        return true;
    }
    if (input[input_pos] == ':')
    {
        *token_size = 1;
        return true;
    }
    return false;
}

// token is '='
static bool token_is_equal(const char *input, int input_pos, int *token_size)
{
    if (input[input_pos] == '=')
    {
        *token_size = 1;
        return true;
    }
    return false;
}

/**
 * LEXER
 */
void lexerize(const char *input, s_token *tokens)
{
    int tokens_count;
    int input_pos;
    int token_size;

    tokens_count = 0;
    input_pos = 0;
    while (input[input_pos])
    {
        token_size = 0;

        if (input[input_pos] == '\0')
        {
            break;
        }

        if (token_is_variable(input, input_pos, &token_size))
        {
            tokens[tokens_count].type = TOKEN_VARIABLE;
            tokens[tokens_count].tag = TOKEN_NO_TAG;
            tokens[tokens_count].value_char = 'x';
        }
        else if (token_is_number_int(input, input_pos, &token_size))
        {
            tokens[tokens_count].type = TOKEN_NUMBER_INT;
            tokens[tokens_count].tag = TOKEN_NUMBER;
            tokens[tokens_count].value_int = ft_atoi(&input[input_pos]);
        }
        else if (token_is_number_double(input, input_pos, &token_size))
        {
            tokens[tokens_count].type = TOKEN_NUMBER_DOUBLE;
            tokens[tokens_count].tag = TOKEN_NUMBER;
            tokens[tokens_count].value_double = ft_atof(&input[input_pos]);
        }
        else if (token_is_number_int_super(input, input_pos, &token_size))
        {
            tokens[tokens_count].type = TOKEN_NUMBER_INT_SUPER;
            tokens[tokens_count].tag = TOKEN_NUMBER;
            tokens[tokens_count].value_int = ft_atoi_superscript(&input[input_pos]);
        }
        else if (token_is_power(input, input_pos, &token_size))
        {
            tokens[tokens_count].type = TOKEN_POWER;
            tokens[tokens_count].tag = TOKEN_NO_TAG;
            tokens[tokens_count].value_char = '^';
        }
        else if (token_is_sign_plus(input, input_pos, &token_size))
        {
            tokens[tokens_count].type = TOKEN_SIGN_PLUS;
            tokens[tokens_count].tag = TOKEN_SIGN;
            tokens[tokens_count].value_char = input[input_pos];
        }
        else if (token_is_sign_minus(input, input_pos, &token_size))
        {
            tokens[tokens_count].type = TOKEN_SIGN_MINUS;
            tokens[tokens_count].tag = TOKEN_SIGN;
            tokens[tokens_count].value_char = input[input_pos];
        }
        else if (token_is_factor_multiplication(input, input_pos, &token_size))
        {
            tokens[tokens_count].type = TOKEN_FACTOR_MULT;
            tokens[tokens_count].tag = TOKEN_FACTOR;
            tokens[tokens_count].value_char = input[input_pos];
        }
        else if (token_is_factor_division(input, input_pos, &token_size))
        {
            tokens[tokens_count].type = TOKEN_FACTOR_DIV;
            tokens[tokens_count].tag = TOKEN_FACTOR;
            tokens[tokens_count].value_char = input[input_pos];
        }
        else if (token_is_equal(input, input_pos, &token_size))
        {
            tokens[tokens_count].type = TOKEN_EQUAL;
            tokens[tokens_count].tag = TOKEN_NO_TAG;
            tokens[tokens_count].value_char = '=';
        }
        else
        {
            stop_errors(&input[input_pos]);
        }

        tokens_count++;
        if (token_size == 0)
        {
            stop_errors(&input[input_pos]);
        }
        input_pos += token_size;
    }

    tokens[tokens_count].type = TOKEN_END;
    tokens[tokens_count].tag = TOKEN_NO_TAG;
    tokens[tokens_count].value_char = '\0';
}
