/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
 * Use of this file is governed by the BSD 3-clause license that
 * can be found in the LICENSE.txt file in the project root.
 */

// A standard C++ class loosely modeled after boost::Any.

#pragma once

#include "antlr4-common.h"

#ifdef _MSC_VER
  #pragma warning(push)
  #pragma warning(disable: 4521) // 'antlrcpp::Any': multiple copy constructors specified
#endif

namespace antlrcpp {

template<class T>
  using StorageType = typename std::decay<T>::type;

struct ANTLR4CPP_PUBLIC Any
{
  bool isNull() const { return _ptr == nullptr; }
  bool isNotNull() const { return _ptr != nullptr; }

  Any() : _ptr(nullptr) {
  }

  Any(Any& that) : _ptr(that.clone()) {
  }

  Any(Any&& that) : _ptr(that._ptr) {
    that._ptr = nullptr;
  }

  Any(const Any& that) : _ptr(that.clone()) {
  }

  Any(const Any&& that) : _ptr(that.clone()) {
  }

  template<typename U>
  Any(U&& value) : _ptr(new Derived<StorageType<U>>(std::forward<U>(value))) {
  }

  template<class U>
  bool is() const {
    auto derived = getDerived<U>(false);

    return derived != nullptr;
  }

  template<class U>
  StorageType<U>& as() {
    auto derived = getDerived<U>(true);

    return derived->value;
  }

  template<class U>
  const StorageType<U>& as() const {
    auto derived = getDerived<U>(true);

    return derived->value;
  }

  template<class U, typename std::enable_if<std::is_copy_constructible<U>::value || std::is_copy_assignable<U>::value>::value>
  operator U() {
    return as<StorageType<U>>();
  }

  template<class U, typename std::enable_if<(!std::is_copy_constructible<U>::value && !std::is_copy_assignable<U>::value) && (std::is_move_constructible<U>::value || std::is_move_assignable<U>::value)>::value>
  operator U() {
    return std::move(as<StorageType<U>>());
  }

  template<class U, typename std::enable_if<std::is_copy_constructible<U>::value || std::is_copy_assignable<U>::value>::value>
  operator const U() const {
    return as<const StorageType<U>>();
  }

  template<class U, typename std::enable_if<!(!std::is_copy_constructible<U>::value && !std::is_copy_assignable<U>::value) && (std::is_move_constructible<U>::value || std::is_move_assignable<U>::value)>::value>
  operator const U() const {
    return std::move(as<const StorageType<U>>());
  }

  Any& operator = (const Any& a) {
    if (_ptr == a._ptr)
      return *this;

    auto * old_ptr = _ptr;
    _ptr = a.clone();

    if (old_ptr)
      delete old_ptr;

    return *this;
  }

  Any& operator = (Any&& a) {
    if (_ptr == a._ptr)
      return *this;

    std::swap(_ptr, a._ptr);

    return *this;
  }

  virtual ~Any();

  virtual bool equals(const Any& other) const {
    return _ptr == other._ptr;
  }

private:
  struct Base {
    virtual ~Base() {};
    virtual Base* clone() const = 0;
  };

  template<typename T>
  struct Derived : Base
  {
    template<typename U> Derived(U&& value_) : value(std::forward<U>(value_)) {
    }

    T value;

    Base* clone() const {
      return clone<>();
    }

  private:
    template<typename U = T, typename std::enable_if<std::is_nothrow_copy_constructible<U>::value, int>::type = 0>
    Base* clone() const {
      return new Derived<T>(value);
    }

    template<typename U = T, typename std::enable_if<!std::is_nothrow_copy_constructible<U>::value, int>::type = 0>
    Base* clone() const {
      return nullptr;
    }

  };

  Base* clone() const
  {
    if (_ptr)
      return _ptr->clone();
    else
      return nullptr;
  }

  template<class U>
  Derived<StorageType<U>>* getDerived(bool checkCast) const {
    typedef StorageType<U> T;

    auto derived = dynamic_cast<Derived<T>*>(_ptr);

    if (checkCast && !derived)
      throw std::bad_cast();

    return derived;
  }

  Base *_ptr;

};

  template<> inline
  Any::Any(std::nullptr_t&& ) : _ptr(nullptr) {
  }


} // namespace antlrcpp

#ifdef _MSC_VER
#pragma warning(pop)
#endif
