#include <Version/Version.hpp>
#include <Log/Log.hpp>

#ifndef BUILD_VERSION
#   define  BUILD_VERSION {0, 0, 0}
#endif

Version Version::ofThisBuild BUILD_VERSION;

std::optional<Version> Version::fromString(const std::string &versionString)
{
    Version result;

    char dot1, dot2;
    std::istringstream stream(versionString);

    int major, minor, patchLevel;
    if (!(stream >> major >> dot1 >> minor >> dot2 >> patchLevel) || dot1 != '.' || dot2 != '.' || !stream.eof())
    {
        return std::nullopt;
    }

    if (major < 0 || major >= 256 || minor < 0 || minor >= 256 || patchLevel < 0 || patchLevel >= 256)
    {
        return std::nullopt;
    }

    result.major = major;
    result.minor = minor;
    result.patchLevel = patchLevel;

    return result;
}

Version::operator bool()
{
    return major != 0 || minor != 0 || patchLevel != 0;
}

bool Version::operator!()
{
    return !((bool) *this);
}

bool Version::operator==(const Version &other) const
{
    return major == other.major && minor == other.minor && patchLevel == other.patchLevel;
}

bool Version::operator<(const Version &other) const
{
    if (major == other.major)
    {
        if (minor == other.minor)
        {
            return patchLevel < other.patchLevel;
        }

        return minor < other.minor;
    }

    return major < other.major;
}

bool Version::operator>(const Version &other) const
{
    if (major == other.major)
    {
        if (minor == other.minor)
        {
            return patchLevel > other.patchLevel;
        }

        return minor > other.minor;
    }

    return major > other.major;
}

bool Version::operator<=(const Version &other) const
{
    if (major == other.major)
    {
        if (minor == other.minor)
        {
            return patchLevel <= other.patchLevel;
        }

        return minor <= other.minor;
    }

    return major <= other.major;
}

bool Version::operator>=(const Version &other) const
{
    if (major == other.major)
    {
        if (minor == other.minor)
        {
            return patchLevel >= other.patchLevel;
        }

        return minor >= other.minor;
    }

    return major >= other.major;
}

std::ostream& operator<<(std::ostream &os, const Version &version)
{
    os << (int) version.major << "." << (int) version.minor << "." << (int) version.patchLevel;
    return os;
}