#ifndef _LIBRARY_LONGINT_HPP_
#define _LIBRARY_LONGINT_HPP_

// ------------------------------------------------------------------------------------------------
#include "Common.hpp"

// ------------------------------------------------------------------------------------------------
#include <stdexcept>

// ------------------------------------------------------------------------------------------------
namespace SqMod {

// ------------------------------------------------------------------------------------------------
template < typename T > class LongInt
{
public:
    // --------------------------------------------------------------------------------------------
    static_assert(std::is_integral< T >::value, "LongInt type is not an integral type");

	// --------------------------------------------------------------------------------------------
	typedef T Type;

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	LongInt()
		: m_Data(0), m_Text()
	{

	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	template <typename U> LongInt(U data)
	{
		*this = data;
	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    LongInt(const SQChar * text)
    {
        *this = text;
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    LongInt(const SQChar * text, SQInteger overload)
    {
        SQMOD_UNUSED_VAR(overload);
        *this = text;
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	LongInt(const LongInt<T> & o)
        : m_Data(o.m_Data), m_Text(o.m_Text)
    {

    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    LongInt(LongInt<T> && o)
        : m_Data(o.m_Data), m_Text(std::move(o.m_Text))
    {

    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	~LongInt()
    {

    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	LongInt & operator = (const LongInt<T> & o)
    {
        m_Data = o.m_Data;
        m_Text = o.m_Text;

        return *this;
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    LongInt & operator = (LongInt<T> && o)
    {
        m_Data = o.m_Data;
        m_Text = std::move(o.m_Text);

        return *this;
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	template <typename U, typename std::enable_if<std::is_integral<U>::value>::type* = nullptr>
	LongInt & operator = (U data)
	{
		m_Data = static_cast< Type >(data);
		m_Text = std::to_string(m_Data);
		return *this;
	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	LongInt & operator = (const SQChar * text)
	{
		m_Text = text;
        try
        {
            m_Data = SToI< T >::Fn(text, 0, 10);
        }
        catch (const std::invalid_argument & e)
        {
            LogErr("Unable to extract number: %s", e.what());
        }
        return *this;
	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	bool operator == (const LongInt<T> & o) const
	{
		return (m_Data == o.m_Data);
	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	bool operator != (const LongInt<T> & o) const
	{
		return (m_Data != o.m_Data);
	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	bool operator < (const LongInt<T> & o) const
	{
		return (m_Data < o.m_Data);
	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	bool operator > (const LongInt<T> & o) const
	{
		return (m_Data > o.m_Data);
	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	bool operator <= (const LongInt<T> & o) const
	{
		return (m_Data <= o.m_Data);
	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	bool operator >= (const LongInt<T> & o) const
	{
		return (m_Data >= o.m_Data);
	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	inline operator T () const
    {
        return m_Data;
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    LongInt<T> operator + (const LongInt<T> & o) const
    {
        return LongInt<T>(m_Data + o.m_Data);
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    LongInt<T> operator - (const LongInt<T> & o) const
    {
        return LongInt<T>(m_Data - o.m_Data);
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    LongInt<T> operator * (const LongInt<T> & o) const
    {
        return LongInt<T>(m_Data * o.m_Data);
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    LongInt<T> operator / (const LongInt<T> & o) const
    {
        return LongInt<T>(m_Data / o.m_Data);
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    LongInt<T> operator % (const LongInt<T> & o) const
    {
        return LongInt<T>(m_Data % o.m_Data);
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    LongInt<T> operator - () const
    {
        return LongInt<T>(-m_Data);
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    SQInteger Cmp(const LongInt<T> & o) const
    {
        if (m_Data == o.m_Data)
        {
            return 0;
        }
        else if (m_Data > o.m_Data)
        {
            return 1;
        }
        else
        {
            return -1;
        }
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    const SQChar * ToString() const
    {
        return m_Text.c_str();
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	void SetNum(T data)
	{
		*this = data;
	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    T GetNum() const
    {
        return m_Data;
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    SQInteger GetSNum() const
    {
        return static_cast< SQInteger >(m_Data);
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	void SetStr(const SQChar * text)
	{
		*this = text;
	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	const String & GetStr() const
	{
		return m_Text;
	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	const SQChar * GetCStr() const
	{
		return m_Text.c_str();
	}

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    void Random()
    {
        m_Data = RandomVal<T>::Get();
        m_Text = std::to_string(m_Data);
    }

    /* --------------------------------------------------------------------------------------------
     * ...
    */
    void Random(T min, T max)
    {
        m_Data = RandomVal<T>::Get(min, max);
        m_Text = std::to_string(m_Data);
    }

private:

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	T      m_Data;

    /* --------------------------------------------------------------------------------------------
     * ...
    */
	String m_Text;
};

// ------------------------------------------------------------------------------------------------
typedef LongInt< Int64 > SLongInt;
typedef LongInt< Uint64 > ULongInt;

} // Namespace:: SqMod

#endif // _LIBRARY_LONGINT_HPP_