// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.

#include "Half.h"
#include "Vector4.h"
#include "Rectangle.h"
#include "Color.h"

static_assert(sizeof(Half) == 2, "Invalid Half type size.");
static_assert(sizeof(Half2) == 4, "Invalid Half2 type size.");
static_assert(sizeof(Half3) == 6, "Invalid Half3 type size.");
static_assert(sizeof(Half4) == 8, "Invalid Half4 type size.");

Half2 Half2::Zero(0.0f, 0.0f);
Half3 Half3::Zero(0.0f, 0.0f, 0.0f);
Half4 Half4::Zero(0.0f, 0.0f, 0.0f, 0.0f);

#if !USE_SSE_HALF_CONVERSION

Half Float16Compressor::Compress(float value)
{
    Bits v, s;
    v.f = value;
    uint32 sign = v.si & signN;
    v.si ^= sign;
    sign >>= shiftSign; // logical shift
    s.si = mulN;
    s.si = static_cast<int32>(s.f * v.f); // correct subnormals
    v.si ^= (s.si ^ v.si) & -(minN > v.si);
    v.si ^= (infN ^ v.si) & -((infN > v.si) & (v.si > maxN));
    v.si ^= (nanN ^ v.si) & -((nanN > v.si) & (v.si > infN));
    v.ui >>= shift; // logical shift
    v.si ^= ((v.si - maxD) ^ v.si) & -(v.si > maxC);
    v.si ^= ((v.si - minD) ^ v.si) & -(v.si > subC);
    return v.ui | sign;
}

float Float16Compressor::Decompress(Half value)
{
    Bits v;
    v.ui = value;
    int32 sign = v.si & signC;
    v.si ^= sign;
    sign <<= shiftSign;
    v.si ^= ((v.si + minD) ^ v.si) & -(v.si > subC);
    v.si ^= ((v.si + maxD) ^ v.si) & -(v.si > maxC);
    Bits s;
    s.si = mulC;
    s.f *= v.si;
    const int32 mask = -(norC > v.si);
    v.si <<= shift;
    v.si ^= (s.si ^ v.si) & mask;
    v.si |= sign;
    return v.f;
}

#endif

Float2 Half2::ToFloat2() const
{
    return Float2(
        Float16Compressor::Decompress(X),
        Float16Compressor::Decompress(Y)
    );
}

Float3 Half3::ToFloat3() const
{
    return Float3(
        Float16Compressor::Decompress(X),
        Float16Compressor::Decompress(Y),
        Float16Compressor::Decompress(Z)
    );
}

Half4::Half4(const Float4& v)
{
    X = Float16Compressor::Compress(v.X);
    Y = Float16Compressor::Compress(v.Y);
    Z = Float16Compressor::Compress(v.Z);
    W = Float16Compressor::Compress(v.W);
}

Half4::Half4(const Color& c)
{
    X = Float16Compressor::Compress(c.R);
    Y = Float16Compressor::Compress(c.G);
    Z = Float16Compressor::Compress(c.B);
    W = Float16Compressor::Compress(c.A);
}

Half4::Half4(const Rectangle& rect)
{
    X = Float16Compressor::Compress(rect.Location.X);
    Y = Float16Compressor::Compress(rect.Location.Y);
    Z = Float16Compressor::Compress(rect.Size.X);
    W = Float16Compressor::Compress(rect.Size.Y);
}

Float2 Half4::ToFloat2() const
{
    return Float2(
        Float16Compressor::Decompress(X),
        Float16Compressor::Decompress(Y)
    );
}

Float3 Half4::ToFloat3() const
{
    return Float3(
        Float16Compressor::Decompress(X),
        Float16Compressor::Decompress(Y),
        Float16Compressor::Decompress(Z)
    );
}

Float4 Half4::ToFloat4() const
{
    return Float4(
        Float16Compressor::Decompress(X),
        Float16Compressor::Decompress(Y),
        Float16Compressor::Decompress(Z),
        Float16Compressor::Decompress(W)
    );
}
