#pragma once

#include <algorithm>
#include <numeric>
#include <stack>
#include <utility>
#include <vector>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"

#include "include/core/SkRefCnt.h"

#pragma clang diagnostic pop

namespace RNSkia {

/**
 Small container for shaders, filters, masks and effects
 */
template <typename T> class Declaration {
public:
  // Pushes to the stack
  void push(T el) { _elements.push(el); }

  // Clears and returns all elements
  std::vector<T> popAll() {
    auto size = _elements.size();
    std::vector<T> tmp;
    tmp.reserve(size);
    for (size_t i = 0; i < size; ++i) {
      tmp.push_back(_elements.top());
      _elements.pop();
    }
    std::reverse(std::begin(tmp), std::end(tmp));
    return tmp;
  }

  T pop() {
    if (_elements.size() == 0) {
      return nullptr;
    }
    auto tmp = _elements.top();
    _elements.pop();
    return tmp;
  }

  // Clears and returns through reducer function in reversed order
  T popAsOne(std::function<T(T inner, T outer)> composer) {
    auto tmp = popAll();
    std::reverse(std::begin(tmp), std::end(tmp));
    return std::accumulate(std::begin(tmp), std::end(tmp),
                           static_cast<T>(nullptr), [=](T inner, T outer) {
                             if (inner == nullptr) {
                               return outer;
                             }
                             return composer(inner, outer);
                           });
  }

  // Returns the size of the elements
  size_t size() { return _elements.size(); }

private:
  std::stack<T> _elements;
};

/**
 Small container for shaders, filters, masks and effects
 */
template <typename T> class ComposableDeclaration : public Declaration<T> {
public:
  /**
   Constructor
   */
  explicit ComposableDeclaration(std::function<T(T inner, T outer)> composer)
      : Declaration<T>(), _composer(composer) {}

  // Clears and returns through reducer function in reversed order
  T popAsOne() { return Declaration<T>::popAsOne(_composer); }

private:
  std::function<T(T inner, T outer)> _composer;
};

} // namespace RNSkia
