/* computorv1.h */

#ifndef COMPUTORV1_H
#define COMPUTORV1_H

#include "libft.h"
#include <stdio.h>  // tmp for printf, for float debug
#include <stdarg.h> // for va_list
#include <stdbool.h>
#include <errno.h>  // for errno
#include <string.h> // for strerror

#include <math.h> // tmp

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * MAIN.C
 */

typedef enum
{
    MODE_ARGV,  //
    MODE_STDIN, //
    MODE_LOOP,  //
} e_program_mode;

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * COMPUTORV1.C
 */

void launch_computorv1(char *input);

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * LEXER.C
 */

typedef enum
{
    TOKEN_VARIABLE,         // x, y, etc.
    TOKEN_NUMBER_INT,       // int
    TOKEN_NUMBER_DOUBLE,    // double
    TOKEN_NUMBER_INT_SUPER, // superscript int, like '²'
    TOKEN_POWER,            // ^ or **
    TOKEN_SIGN_PLUS,        // +
    TOKEN_SIGN_MINUS,       // -
    TOKEN_FACTOR_MULT,      // *
    TOKEN_FACTOR_DIV,       // / or :
    TOKEN_EQUAL,            // =
    TOKEN_END               // null (end of input)
} e_token_type;

typedef enum
{
    TOKEN_NO_TAG,
    TOKEN_NUMBER,
    TOKEN_SIGN,
    TOKEN_FACTOR,
} e_token_tag;

typedef struct
{
    e_token_type type;
    e_token_tag tag;
    union
    {
        char value_char;
        int value_int;
        double value_double;
    };
} s_token;

void lexerize(const char *input, s_token *tokens);

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * PARSER.C
 */

typedef enum
{
    TERM_LEFT,  // a in "a = b"
    TERM_RIGHT, // b in "a = b"
    TERM_END,   // last term
} e_term_position;

typedef enum
{
    TERM_PLUS,  // +
    TERM_MINUS, // -
    TERM_NULL,  // null -> for the last term
} e_term_sign;

typedef struct
{
    e_term_position position;
    e_term_sign sign;
    double coefficient;
    int exponent;
} s_term;

void parse(s_token *tokens, s_term *terms, int terms_count_max);

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * REDUCE.C
 */

void reduce(s_term *terms, double *polynom);

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * SOLVE.C
 */

typedef enum
{
    DELTA_PLUS,  // +
    DELTA_MINUS, // -
    DELTA_ZERO,  // 0
} e_delta_sign;

typedef struct
{
    e_delta_sign delta_sign;     // DELTA_PLUS or DELTA_MINUS or DELTA_ZERO
    double delta_absolute;       // |Δ| == |b² - 4ac|
    int first_term_gcd;          // gcd(b, 2a)
    int first_term_numerator;    // -b / gcd
    int first_term_denominator;  // 2a / gcd
    double first_term;           // double (-b / 2a)
    int second_term_gcd;         // gcd(√|Δ|, 2a)
    int second_term_numerator;   // √|Δ| / gcd
    int second_term_denominator; // 2a / gcd
    double second_term;          // double (√|Δ| / 2a)
    double solution1;            // first_term + second_term
    double solution2;            // first_term - second_term
} s_solution;

void solve(const double *polynom, s_solution *solution);

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * UTILS/ERRORS.C
 */

void print_state();
void stop_errors(const char *format, ...);

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * UTILS/PRINT_ENUMS.C
 */

const char *token_type_to_str(e_token_type type);
const char *token_tag_to_str(e_token_tag tag);
const char *term_position_to_str(e_term_position pos);
const char *term_sign_to_str(e_term_sign sign);
const char *delta_sign_to_str(e_delta_sign sign);

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * UTILS/PRINTER.C
 */

void print_debug(const char *description, ...);
void print_before_solution(double *polynom, int max_exponent);

/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * GLOBALS
 */

extern char *input_g_err;
extern s_token *tokens_g_err;
extern s_term *terms_g_err;
extern double *polynom_g_err;
extern int polynom_len_g_err;
extern bool flag_debug_mode;
extern bool flag_loop_mode;
extern s_solution *solution_g_err;

#endif