/* ===========================================================================
 *
 *                            PUBLIC DOMAIN NOTICE
 *               National Center for Biotechnology Information
 *
 *  This software/database is a "United States Government Work" under the
 *  terms of the United States Copyright Act.  It was written as part of
 *  the author's official duties as a United States Government employee and
 *  thus cannot be copyrighted.  This software/database is freely available
 *  to the public for use. The National Library of Medicine and the U.S.
 *  Government have not placed any restriction on its use or reproduction.
 *
 *  Although all reasonable efforts have been taken to ensure the accuracy
 *  and reliability of the software and data, the NLM and the U.S.
 *  Government do not and cannot warrant the performance or results that
 *  may be obtained by using this software or data. The NLM and the U.S.
 *  Government disclaim all warranties, express or implied, including
 *  warranties of performance, merchantability or fitness for any particular
 *  purpose.
 *
 *  Please cite the author in any work or product based on this material.
 *
 * ===========================================================================
 *
 * Project:
 *  sratools command line tool
 *
 * Purpose:
 *  locate executable
 *
 */

#pragma once
#include <string>
#include <iosfwd>
#include <cstdint>
#include <cstring>

namespace sratools {

struct Version {
    uint32_t packed;
    operator std::string() const noexcept;
    operator bool() const noexcept { return packed != 0; }

    std::ostream &write(std::ostream &os) const noexcept;

    bool operator <(Version const &rhs) const { return packed < rhs.packed; }
    bool operator >(Version const &rhs) const { return packed > rhs.packed; }
    bool operator <=(Version const &rhs) const { return packed <= rhs.packed; }
    bool operator >=(Version const &rhs) const { return packed >= rhs.packed; }
    bool operator ==(Version const &other) const { return packed == other.packed; }
    bool operator !=(Version const &other) const { return packed != other.packed; }

    /// The current build version.
    static Version const &current;
    /// The current build version, as a string.
    static std::string const &currentString;

    Version() noexcept : packed(0) {}

    explicit Version(uint32_t const pckd) noexcept
    : packed(pckd)
    {}

    Version(char const *string, char const *strEnd);

    explicit Version(char const *string)
    : Version(string, string + strlen(string))
    {}

    explicit Version(std::string const &string)
    : Version(string.data(), string.data() + string.size())
    {}

    Version(uint8_t const major, uint8_t const minor, uint16_t const revision) noexcept
    : packed((((uint32_t)major) << 24) | (((uint32_t)minor) << 16) | ((uint32_t)revision))
    {}

    unsigned ncbi_major() const noexcept { return packed >> 24; }
    unsigned ncbi_minor() const noexcept { return (packed >> 16) & 0xFF; }
    unsigned revision() const noexcept { return packed & 0xFFFF; }

    Version nextMinor() const noexcept { return Version(uint8_t(ncbi_major()), uint8_t(ncbi_minor() + 1), 0); }
    Version nextMajor() const noexcept { return Version(uint8_t(ncbi_major() + 1), 0, 0); }

    /// \brief Scan the name for a version string; if found, remove it.
    /// \Returns The found version or `Version()`.
    static Version removeVersion(std::string &name) noexcept;
    /// \brief Scan the name for a version string; if found, return it.
    /// \Returns The found version or `Version()`.

    static Version fromName(std::string const &name) noexcept {
        auto tmp = std::string(name);
        return removeVersion(tmp);
    }

    template< typename T >
    static std::pair< Version, T > make(T begin, T end, unsigned *parts) noexcept;
};

} // namespace sratools

static inline std::ostream &operator <<(std::ostream &os, sratools::Version const &vers) {
    return vers.write(os);
}
