/*
 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.

 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef VGLOBAL_H
#define VGLOBAL_H

#include <cmath>
#include <cstdint>
#include <iostream>
#include <type_traits>
#include <utility>

using uint   = uint32_t;
using ushort = uint16_t;
using uchar  = uint8_t;

#if !defined(V_NAMESPACE)

#define V_USE_NAMESPACE
#define V_BEGIN_NAMESPACE
#define V_END_NAMESPACE

#else /* user namespace */

#define V_USE_NAMESPACE using namespace ::V_NAMESPACE;
#define V_BEGIN_NAMESPACE namespace V_NAMESPACE {
#define V_END_NAMESPACE }

#endif

#ifndef __has_attribute
# define __has_attribute(x) 0
#endif /* !__has_attribute */

#if __has_attribute(unused)
# define V_UNUSED __attribute__((__unused__))
#else
# define V_UNUSED
#endif /* V_UNUSED */

#if __has_attribute(warn_unused_result)
# define V_REQUIRED_RESULT __attribute__((__warn_unused_result__))
#else
# define V_REQUIRED_RESULT
#endif /* V_REQUIRED_RESULT */

#define V_CONSTEXPR constexpr
#define V_NOTHROW noexcept

#include "vector_vdebug.h"

#if __GNUC__ >= 7
#define VECTOR_FALLTHROUGH __attribute__ ((fallthrough));
#else
#define VECTOR_FALLTHROUGH
#endif

#ifdef LOTTIE_THREAD_SUPPORT
#define vthread_local thread_local
#else
#define vthread_local
#endif

#if defined(_MSC_VER)
    #define V_ALWAYS_INLINE __forceinline
#else
    #define V_ALWAYS_INLINE __attribute__((always_inline))
#endif

template <typename T>
V_CONSTEXPR inline const T &vMin(const T &a, const T &b)
{
    return (a < b) ? a : b;
}
template <typename T>
V_CONSTEXPR inline const T &vMax(const T &a, const T &b)
{
    return (a < b) ? b : a;
}

static const double EPSILON_DOUBLE = 0.000000000001f;
static const float  EPSILON_FLOAT = 0.000001f;

static inline bool vCompare(float p1, float p2)
{
    return (std::abs(p1 - p2) < EPSILON_FLOAT);
}

static inline bool vIsZero(float f)
{
    return (std::abs(f) <= EPSILON_FLOAT);
}

static inline bool vIsZero(double f)
{
    return (std::abs(f) <= EPSILON_DOUBLE);
}

class vFlagHelper {
    int i;

public:
    explicit constexpr inline vFlagHelper(int ai) noexcept : i(ai) {}
    constexpr inline operator int() const noexcept { return i; }

    explicit constexpr inline vFlagHelper(uint ai) noexcept : i(int(ai)) {}
    explicit constexpr inline vFlagHelper(short ai) noexcept : i(int(ai)) {}
    explicit constexpr inline vFlagHelper(ushort ai) noexcept : i(int(uint(ai))) {}
    constexpr inline operator uint() const noexcept { return uint(i); }
};

template <typename Enum>
class vFlag {
public:
    static_assert(
        (sizeof(Enum) <= sizeof(int)),
        "vFlag only supports int as storage so bigger type will overflow");
    static_assert((std::is_enum<Enum>::value),
                  "vFlag is only usable on enumeration types.");

    using Int = typename std::conditional<
        std::is_unsigned<typename std::underlying_type<Enum>::type>::value,
        unsigned int, signed int>::type;

    using  enum_type = Enum;
    // compiler-generated copy/move ctor/assignment operators are fine!

    vFlag() = default;
    constexpr vFlag(Enum f) noexcept : i(Int(f)) {}
    explicit constexpr vFlag(vFlagHelper f) noexcept : i(f) {}

    inline vFlag &operator&=(int mask) noexcept
    {
        i &= mask;
        return *this;
    }
    inline vFlag &operator&=(uint mask) noexcept
    {
        i &= mask;
        return *this;
    }
    inline vFlag &operator&=(Enum mask) noexcept
    {
        i &= Int(mask);
        return *this;
    }
    inline vFlag &operator|=(vFlag f) noexcept
    {
        i |= f.i;
        return *this;
    }
    inline vFlag &operator|=(Enum f) noexcept
    {
        i |= Int(f);
        return *this;
    }
    inline vFlag &operator^=(vFlag f) noexcept
    {
        i ^= f.i;
        return *this;
    }
    inline vFlag &operator^=(Enum f) noexcept
    {
        i ^= Int(f);
        return *this;
    }

    constexpr inline operator Int() const noexcept { return i; }

    constexpr inline vFlag operator|(vFlag f) const
    {
        return vFlag(vFlagHelper(i | f.i));
    }
    constexpr inline vFlag operator|(Enum f) const noexcept
    {
        return vFlag(vFlagHelper(i | Int(f)));
    }
    constexpr inline vFlag operator^(vFlag f) const noexcept
    {
        return vFlag(vFlagHelper(i ^ f.i));
    }
    constexpr inline vFlag operator^(Enum f) const noexcept
    {
        return vFlag(vFlagHelper(i ^ Int(f)));
    }
    constexpr inline vFlag operator&(int mask) const noexcept
    {
        return vFlag(vFlagHelper(i & mask));
    }
    constexpr inline vFlag operator&(uint mask) const noexcept
    {
        return vFlag(vFlagHelper(i & mask));
    }
    constexpr inline vFlag operator&(Enum f) const noexcept
    {
        return vFlag(vFlagHelper(i & Int(f)));
    }
    constexpr inline vFlag operator~() const noexcept
    {
        return vFlag(vFlagHelper(~i));
    }

    constexpr inline bool operator!() const noexcept { return !i; }

    constexpr inline bool testFlag(Enum f) const noexcept
    {
        return (i & Int(f)) == Int(f) && (Int(f) != 0 || i == Int(f));
    }
    inline vFlag &setFlag(Enum f, bool on = true) noexcept
    {
        return on ? (*this |= f) : (*this &= ~f);
    }

    Int i{0};
};

class VColor {
public:
    VColor() = default;
    explicit VColor(uchar red, uchar green, uchar blue, uchar alpha = 255) noexcept
        :a(alpha), r(red), g(green), b(blue){}
    inline uchar  red() const noexcept { return r; }
    inline uchar  green() const noexcept { return g; }
    inline uchar  blue() const noexcept { return b; }
    inline uchar  alpha() const noexcept { return a; }
    inline void setRed(uchar red) noexcept { r = red; }
    inline void setGreen(uchar green) noexcept { g = green; }
    inline void setBlue(uchar blue) noexcept { b = blue; }
    inline void setAlpha(uchar alpha) noexcept { a = alpha; }
    inline bool isOpaque() const { return a == 255; }
    inline bool isTransparent() const { return a == 0; }
    inline bool operator==(const VColor &o) const
    {
        return ((a == o.a) && (r == o.r) && (g == o.g) && (b == o.b));
    }
    uint premulARGB() const
    {
        int pr = (r * a) / 255;
        int pg = (g * a) / 255;
        int pb = (b * a) / 255;
        return uint((a << 24) | (pr << 16) | (pg << 8) | (pb));
    }

    uint premulARGB(float opacity) const
    {
        int alpha = int(a * opacity);
        int pr = (r * alpha) / 255;
        int pg = (g * alpha) / 255;
        int pb = (b * alpha) / 255;
        return uint((alpha << 24) | (pr << 16) | (pg << 8) | (pb));
    }

public:
    uchar a{0};
    uchar r{0};
    uchar g{0};
    uchar b{0};
};

enum class FillRule: unsigned char { EvenOdd, Winding };
enum class JoinStyle: unsigned char { Miter, Bevel, Round };
enum class CapStyle: unsigned char { Flat, Square, Round };

enum class BlendMode {
    Src,
    SrcOver,
    DestIn,
    DestOut,
    Last,
};

#ifndef V_CONSTRUCTOR_FUNCTION
#define V_CONSTRUCTOR_FUNCTION0(AFUNC)            \
    namespace {                                   \
    static const struct AFUNC##_ctor_class_ {     \
        inline AFUNC##_ctor_class_() { AFUNC(); } \
    } AFUNC##_ctor_instance_;                     \
    }

#define V_CONSTRUCTOR_FUNCTION(AFUNC) V_CONSTRUCTOR_FUNCTION0(AFUNC)
#endif

#endif  // VGLOBAL_H
