#include <stdio.h>
#include <stdlib.h>
#ifdef WIN32
#include <windows.h>
#endif

#include "SoftFloat/source/include/softfloat.h"
#include "internals.h"

#define _FLOAT_INCLUDE_F16_H
#define _FLOAT_INCLUDE_F32_H
extern void f16_print(float16_t v);
extern void f32_print(float32_t v);

#define CREATE_VAR(name) __##name = {name}
#define ADD_PREFIX(name) __##name

#define OPERATION_DISPATCH(OP1, OP2, ...)                                                                                        \
    case OP1: {                                                                                                                  \
        if (fmt == HALF) {                                                                                                       \
            float16_t REPEAT(CREATE_VAR, __VA_ARGS__);                                                                           \
            f16_print(f16_##OP2(REPEAT(ADD_PREFIX, __VA_ARGS__)));                                                               \
        } else {                                                                                                                 \
            float32_t REPEAT(CREATE_VAR, __VA_ARGS__);                                                                           \
            f32_print(f32_##OP2(REPEAT(ADD_PREFIX, __VA_ARGS__)));                                                               \
        }                                                                                                                        \
    } break

NORETURN void die() {
    fprintf(stderr,
            "Usage: \n"
            "<fmt> <округление> <число>\n"
            "<fmt> <округление> <операция2> <число1> <число2>\n"
            "<fmt> <округление> <операция3> <число1> <число2> <число3>\n");
    exit(1);
}

int skkv_to_sf(int rt) {
    switch (rt) {
        case 0:
            return softfloat_round_minMag;
        case 1:
            return softfloat_round_near_even;
        case 2:
            return softfloat_round_max;
        case 3:
            return softfloat_round_min;
        default:
            unreachable();
    }
}


float16_t f16_mad(float16_t a, float16_t b, float16_t c) { return f16_add(f16_mul(a, b), c); }
float32_t f32_mad(float32_t a, float32_t b, float32_t c) { return f32_add(f32_mul(a, b), c); }

THREAD_LOCAL uint_fast8_t softfloat_detectTininess = 0;
THREAD_LOCAL uint_fast8_t softfloat_exceptionFlags = 0;
THREAD_LOCAL uint_fast8_t softfloat_roundingMode;

int main(int argc, char **argv) {
    #ifdef WIN32
        SetConsoleCP(CP_UTF8);
        SetConsoleOutputCP(CP_UTF8);
    #endif

    u32 a = 0, b = 0, c = 0;
    Operation operation = PRN;

    switch (argc) {
        case 4:
            sscanf(argv[3], "%X", &a);
            break;
        case 7:
            sscanf(argv[6], "%X", &c);
        case 6:
            sscanf(argv[3], "%c", (char *) &operation);
            sscanf(argv[4], "%X", &a);
            sscanf(argv[5], "%X", &b);
            break;
        default:
            die();
    }

    Format fmt = *argv[1];
    softfloat_roundingMode = skkv_to_sf(*argv[2] - '0');

    switch (operation) {
        OPERATION_DISPATCH(PRN, nop, a);
        OPERATION_DISPATCH(ADD, add, a, b);
        OPERATION_DISPATCH(SUB, sub, a, b);
        OPERATION_DISPATCH(DIV, div, a, b);
        OPERATION_DISPATCH(MUL, mul, a, b);
        OPERATION_DISPATCH(FMA, mulAdd, a, b, c);
        OPERATION_DISPATCH(MAD, mad, a, b, c);
    }
}
