#pragma once

#include <type_traits>
#include <utility>

namespace hr {

template<class Sig>
class function;

template<class R, class... Args>
struct function_state_base {
    virtual R call(Args...) const = 0;
    virtual function_state_base *clone() const = 0;
    virtual ~function_state_base() {}
};

template<class T, class R, class... Args>
struct function_state : function_state_base<R, Args...> {
    T t_;
    explicit function_state(T t) : t_(std::move(t)) {}
    R call(Args... args) const override {
        return const_cast<T&>(t_)(static_cast<Args&&>(args)...);
    }
    function_state_base<R, Args...> *clone() const override {
        return new function_state(*this);
    }
};

template<class R, class... Args>
class function<R(Args...)>
{
    function_state_base<R, Args...> *ptr_;
public:
    function() : ptr_(nullptr) {}

    template<class Callable, class = decltype(R(std::declval<typename std::decay<Callable>::type>()(std::declval<Args>()...)))>
    function(Callable&& t) :
        ptr_(new function_state<typename std::decay<Callable>::type, R, Args...>(static_cast<Callable&&>(t)))
    {}

    ~function() {
        delete ptr_;
    }

    function(function& rhs) : ptr_(rhs.ptr_ ? rhs.ptr_->clone() : nullptr) {}
    function(const function& rhs) : ptr_(rhs.ptr_ ? rhs.ptr_->clone() : nullptr) {}
    function(function&& rhs) noexcept : ptr_(rhs.ptr_) { rhs.ptr_ = nullptr; }
    function(const function&& rhs) = delete;

    void operator=(function rhs) noexcept {
        std::swap(ptr_, rhs.ptr_);
    }

    R operator()(Args... args) const {
        return ptr_->call(static_cast<Args&&>(args)...);
    }

    explicit operator bool() const noexcept {
        return ptr_ != nullptr;
    }

    template<class T> T* target() { 
      auto ptr = dynamic_cast<function_state<T, R, Args...>*> (ptr_);
      if(!ptr) return nullptr;
      return &ptr->t_;
      }
};

} // namespace hr
