#include <reanimated/CSS/common/values/CSSNumber.h>

#include <algorithm>
#include <string>

namespace reanimated::css {

template <typename TDerived, typename TValue>
CSSNumberBase<TDerived, TValue>::CSSNumberBase() : value(0) {}

template <typename TDerived, typename TValue>
CSSNumberBase<TDerived, TValue>::CSSNumberBase(TValue value) : value(value) {}

template <typename TDerived, typename TValue>
CSSNumberBase<TDerived, TValue>::CSSNumberBase(jsi::Runtime &rt, const jsi::Value &jsiValue) {
  value = static_cast<TValue>(jsiValue.asNumber());
}

template <typename TDerived, typename TValue>
CSSNumberBase<TDerived, TValue>::CSSNumberBase(const folly::dynamic &value) {
  this->value = static_cast<TValue>(value.asDouble());
}

template <typename TDerived, typename TValue>
bool CSSNumberBase<TDerived, TValue>::canConstruct(jsi::Runtime &rt, const jsi::Value &jsiValue) {
  return jsiValue.isNumber();
}

template <typename TDerived, typename TValue>
bool CSSNumberBase<TDerived, TValue>::canConstruct(const folly::dynamic &value) {
  return value.isInt() || value.isDouble();
}

template <typename TDerived, typename TValue>
folly::dynamic CSSNumberBase<TDerived, TValue>::toDynamic() const {
  return value;
}

template <typename TDerived, typename TValue>
std::string CSSNumberBase<TDerived, TValue>::toString() const {
  return std::to_string(value);
}

template <typename TDerived, typename TValue>
TDerived CSSNumberBase<TDerived, TValue>::interpolate(double progress, const TDerived &other) const {
  return TDerived(value + progress * (other.value - value));
}

template <typename TDerived, typename TValue>
bool CSSNumberBase<TDerived, TValue>::operator==(const CSSNumberBase<TDerived, TValue> &other) const {
  return value == other.value;
}

CSSInteger CSSInteger::interpolate(double progress, const CSSInteger &other) const {
  return CSSInteger(static_cast<int>(std::round(value + progress * (other.value - value))));
}

CSSIndex CSSIndex::interpolate(double progress, const CSSIndex &other) const {
  return CSSIndex(progress < 0.5 ? value : other.value);
}

template struct CSSNumberBase<CSSDouble, double>;
template struct CSSNumberBase<CSSInteger, int>;
template struct CSSNumberBase<CSSIndex, int>;

#ifdef ANDROID

CSSShadowRadiusAndroid::CSSShadowRadiusAndroid() : CSSNumberBase<CSSShadowRadiusAndroid, double>(1.0) {}

CSSShadowRadiusAndroid::CSSShadowRadiusAndroid(const double value)
    : CSSNumberBase<CSSShadowRadiusAndroid, double>(std::max(1.0, value)) {}

CSSShadowRadiusAndroid::CSSShadowRadiusAndroid(jsi::Runtime &rt, const jsi::Value &jsiValue)
    : CSSNumberBase<CSSShadowRadiusAndroid, double>(rt, jsiValue) {
  value = std::max(1.0, value);
}

CSSShadowRadiusAndroid::CSSShadowRadiusAndroid(const folly::dynamic &value)
    : CSSNumberBase<CSSShadowRadiusAndroid, double>(value) {
  this->value = std::max(1.0, value.getDouble());
}

template struct CSSNumberBase<CSSShadowRadiusAndroid, double>;

#endif

} // namespace reanimated::css
