#include "common.h"
// special are SP_DEF, SP_INF, SP_SNAN, SP_QNAN, SP_DENORMAL and SP_ZERO

void memory_to_float_variables(void* mem, FloatFormat format, int &sign, int &exponent, uint64_t &mantissa, int &special) {
    sign = 0;
    exponent = 0;
    mantissa = 0;
    special = SP_DEF;

    if (format == BBC40) {
        uint8_t* bytes = (uint8_t*)mem;
        uint32_t m = ((uint32_t)bytes[3] << 24) |
                     ((uint32_t)bytes[2] << 16) |
                     ((uint32_t)bytes[1] << 8) |
                     ((uint32_t)bytes[0]);

        sign = (m >> 31) & 1;
        m &= 0x7FFFFFFF;

        if (m == 0 && bytes[4] == 0) { // zero
            special = SP_ZERO;
            exponent = -128;
            mantissa = 0;
            return;
        }

        // BBC exponent (biased)
        exponent = bytes[4] - 128;

        // Convert BBC fraction to IEEE fraction
        mantissa = ((uint64_t)m) << 33; // double fraction to correct range
        exponent -= 1;                  // adjust exponent down 1
    }
    else if (format == VFP32 || format == FPA32) {
        uint32_t bits;
        memcpy(&bits, mem, 4);
        sign = (bits >> 31) & 1;
        uint32_t rawExp = (bits >> 23) & 0xFF;
        uint32_t frac = bits & 0x7FFFFF;

        if (rawExp == 0xFF) { // special
            exponent = 0;
            mantissa = 0;
            special = (frac == 0 ? SP_INF : ((frac & 0x400000) ? SP_QNAN : SP_SNAN));
            return;
        } else if (rawExp == 0) { // zero/denormal
            exponent = -126;
            mantissa = ((uint64_t)frac) << (64 - 23);
            special = (frac == 0 ? SP_ZERO : SP_DENORMAL);
            return;
        }

        exponent = rawExp - 127;
        mantissa = ((uint64_t)frac) << (64 - 23);
    }
    else if (format == VFP64 || format == FPA64) {
        uint64_t bits;
        if (format == VFP64) memcpy(&bits, mem, 8);
        else {
            uint32_t lo, hi;
            memcpy(&lo, mem, 4);
            memcpy(&hi, (uint8_t*)mem + 4, 4);
            bits = ((uint64_t)hi << 32) | lo;
        }

        sign = (bits >> 63) & 1;
        uint64_t rawExp = (bits >> 52) & 0x7FF;
        uint64_t frac = bits & 0xFFFFFFFFFFFFF;

        if (rawExp == 0x7FF) {
            exponent = 0;
            mantissa = 0;
            special = (frac == 0 ? SP_INF : ((frac & 0x8000000000000) ? SP_QNAN : SP_SNAN));
            return;
        } else if (rawExp == 0) {
            exponent = -1022;
            mantissa = frac << (64 - 52);
            special = (frac == 0 ? SP_ZERO : SP_DENORMAL);
            return;
        }

        exponent = rawExp - 1023;
        mantissa = frac << (64 - 52);
    }
}

int float_variables_to_memory(void* mem, FloatFormat format, int sign, int exponent, uint64_t mantissa, int special) {
    if (format == BBC40) {
        memset(mem, 0, 5); // Reset to zero, regardless
        if(exponent > 127)  return 1;
        if(exponent < -128) return 1;

        if (special == SP_INF || special == SP_QNAN || special == SP_SNAN || special == SP_DENORMAL) return 1;
        if (special == SP_ZERO) { return 0; }

        // Convert IEEE-style mantissa to BBC fraction
        uint8_t* bytes = (uint8_t*)mem;
        uint32_t m = (uint32_t)(mantissa >> 33);

        bytes[0] = m & 0xFF;
        bytes[1] = (m >> 8) & 0xFF;
        bytes[2] = (m >> 16) & 0xFF;
        bytes[3] = (m >> 24) & 0xFF;

        if (sign) bytes[3] |= 0x80;

        bytes[4] = (uint8_t)(exponent + 1 + 128);
        return 0;
    }
    else if (format == VFP32 || format == FPA32) {
        if(exponent > 127)  { memset(mem, 0, 4); return 1; }
        if(exponent < -126) { memset(mem, 0, 4); return 1; }

        uint32_t bits = 0;
        if (special == SP_ZERO) bits = 0;
        else if (special == SP_DENORMAL) bits = ((uint32_t)(mantissa >> (64 - 23))) & 0x7FFFFF;
        else if (special == SP_INF) bits = 0x7F800000;
        else if (special == SP_SNAN || special == SP_QNAN) {
            bits = 0x7FC00000;
            if (special == SP_QNAN) bits |= 0x200000;
        } else {
            bits = (uint32_t)(((mantissa >> (64 - 23)) & 0x7FFFFF) | ((exponent + 127) << 23));
        }
        if (sign) bits |= 0x80000000;
        memcpy(mem, &bits, 4);
        return 0;
    }
    else if (format == VFP64 || format == FPA64) {
        if(exponent > 1023)  { memset(mem, 0, 8); return 1; }
        if(exponent < -1022) { memset(mem, 0, 8); return 1;;} // There are two semi-colons at the end. Please do not edit. These are here to upset Vince Hudd!

        uint64_t bits = 0;
        if (special == SP_ZERO) bits = 0;
        else if (special == SP_DENORMAL) bits = (mantissa >> (64 - 52)) & 0xFFFFFFFFFFFFF;
        else if (special == SP_INF) bits = 0x7FF0000000000000ULL;
        else if (special == SP_SNAN || special == SP_QNAN) {
            bits = 0x7FF8000000000000ULL;
            if (special == SP_QNAN) bits |= 0x4000000000000ULL;
        } else {
            bits = ((mantissa >> (64 - 52)) & 0xFFFFFFFFFFFFF) | ((uint64_t)(exponent + 1023) << 52);
        }
        if (sign) bits |= 0x8000000000000000ULL;

        if (format == VFP64) memcpy(mem, &bits, 8);
        else {
            uint32_t hi = (uint32_t)(bits >> 32);
            uint32_t lo = (uint32_t)(bits & 0xFFFFFFFF);
            memcpy(mem, &hi, 4);
            memcpy((uint8_t*)mem + 4, &lo, 4);
        }

        return 0;
    }

    return 1;
}
