/*******************************************************************************
 * Copyright (c) 2012 AIT
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *   Werner Tremmel - initial API and implementation and/or initial documentation
 *******************************************************************************/
#ifndef VARIANTNEW_H_
#define VARIANTNEW_H_

#include <boost/lexical_cast.hpp>

/*! \brief structure for simplified handling of the VARIANT
 *
 *  The structure simplifies the handling of the VARIANT with numeric types.
 */

#include <OleAuto.h>
#include <comutil.h>

struct Variant : public VARIANT{
  public:
    class VariantException : public std::exception{
      public:
        VariantException(){
        }
        virtual const char* what() throw (){
          return "VariantException: unknown variant type";
        }
    };

    ~Variant(){
    }
    Variant(){
    }

    explicit Variant(const _variant_t &value) :
        VARIANT(value){
    }
    explicit Variant(const VARIANT &value) :
        VARIANT(value){
    }
    explicit Variant(bool value){
      setbool(value);
    }
    explicit Variant(BOOL value){
      setbool(value != 0);
    }
    explicit Variant(VARIANT_BOOL value){
      setbool(value != 0);
    }

    template<typename T> explicit Variant(T value){
      set<T>(value);
    }

    template<typename T> T get() const{
      switch (vt){
        case VT_I8:
          return static_cast < T > (llVal);
        case VT_I4:
          return static_cast < T > (lVal);
        case VT_I2:
          return static_cast < T > (iVal);
        case VT_I1:
          return static_cast < T > (cVal);
        case VT_UI8:
          return static_cast < T > (ullVal);
        case VT_UI4:
          return static_cast < T > (ulVal);
        case VT_UI2:
          return static_cast < T > (uiVal);
        case VT_UI1:
          return static_cast < T > (bVal);
        case VT_R8:
          return static_cast < T > (dblVal);
        case VT_R4:
          return static_cast < T > (fltVal);
        case VT_INT:
          return static_cast < T > (intVal);
        case VT_UINT:
          return static_cast < T > (uintVal);
        case VT_BOOL:
          return boolVal != 0 ? static_cast < T > (1) : static_cast < T > (0);

        default:
          throw VariantException();
      }
    }
    template<> std::string get<std::string>() const{
      switch (vt){
        case VT_I8:
          return boost::lexical_cast < std::string > (llVal);
        case VT_I4:
          return boost::lexical_cast < std::string > (lVal);
        case VT_I2:
          return boost::lexical_cast < std::string > (iVal);
        case VT_I1:
          return boost::lexical_cast < std::string > (cVal);
        case VT_UI8:
          return boost::lexical_cast < std::string > (ullVal);
        case VT_UI4:
          return boost::lexical_cast < std::string > (ulVal);
        case VT_UI2:
          return boost::lexical_cast < std::string > (uiVal);
        case VT_UI1:
          return boost::lexical_cast < std::string > (bVal);
        case VT_R8:
          return boost::lexical_cast < std::string > (dblVal);
        case VT_R4:
          return boost::lexical_cast < std::string > (fltVal);
        case VT_INT:
          return boost::lexical_cast < std::string > (intVal);
        case VT_UINT:
          return boost::lexical_cast < std::string > (uintVal);
        case VT_BOOL:
          return boolVal != 0 ? boost::lexical_cast < std::string > (true) : boost::lexical_cast < std::string > (false);

        default:
          throw VariantException();
      }
    }
    template<> bool get<bool>() const{
      switch (vt){
        case VT_I8:
          return llVal != 0 ? true : false;
        case VT_I4:
          return lVal != 0 ? true : false;
        case VT_I2:
          return iVal != 0 ? true : false;
        case VT_I1:
          return cVal != 0 ? true : false;
        case VT_UI8:
          return ullVal != 0 ? true : false;
        case VT_UI4:
         return ulVal != 0 ? true : false;
        case VT_UI2:
         return uiVal != 0 ? true : false;
        case VT_UI1:
        return bVal != 0 ? true : false;
        case VT_R8:
        return dblVal != 0 ? true : false;
        case VT_R4:
         return fltVal != 0 ? true : false;
        case VT_INT:
         return intVal != 0 ? true : false;
        case VT_UINT:
          return uintVal != 0 ? true : false;
        case VT_BOOL:
          return boolVal != 0 ? true : false;

        default:
          throw VariantException();
      }
    }
    void setbool(bool value){
      boolVal = value ? 0xffff : 0;
      vt = VT_BOOL;
    }
    template<typename T> void set(T value){
      getField<T>() = value;
      vt = getType<T>();
    }
    template<> void set<bool>(bool value){
      setbool(value);
    }
    void setBOOL(BOOL value){
      setbool(value != 0);
    }
    void setVARIANT_BOOL(VARIANT_BOOL value){
      setbool(value != 0);
    }

    template<typename T> bool isType() const{
      return vt == getType<T>();
    }

    bool isInteger() const{
      switch (vt){
        case VT_I8:
        case VT_I4:
        case VT_I2:
        case VT_I1:
        case VT_INT:
          return true;
        default:
          return false;
      }
    }
    bool isUnsignedInteger() const{
      switch (vt){
        case VT_UI8:
        case VT_UI4:
        case VT_UI2:
        case VT_UI1:
        case VT_UINT:
          return true;
        default:
          return false;
      }
    }
    bool isFloatingPoint() const{
      switch (vt){
        case VT_R8:
        case VT_R4:
          return true;
        default:
          return false;
      }
    }

    template<typename T> VARTYPE getType() const{
      constraint<T>();
    }
    template<> VARTYPE getType<LONGLONG>() const{
      return VT_I8;
    }
    template<> VARTYPE getType<LONG>() const{
      return VT_I4;
    }
    template<> VARTYPE getType<SHORT>() const{
      return VT_I2;
    }
    template<> VARTYPE getType<CHAR>() const{
      return VT_I1;
    }
    template<> VARTYPE getType<signed char>() const{
      return VT_I1;
    }
    template<> VARTYPE getType<ULONGLONG>() const{
      return VT_UI8;
    }
    template<> VARTYPE getType<ULONG>() const{
      return VT_UI4;
    }
    template<> VARTYPE getType<USHORT>() const{
      return VT_UI2;
    }
    template<> VARTYPE getType<BYTE>() const{
      return VT_UI1;
    }
    template<> VARTYPE getType<float>() const{
      return VT_R4;
    }
    template<> VARTYPE getType<double>() const{
      return VT_R8;
    }
    //template<> VARTYPE getType<BOOL>() const{
    //  return VT_BOOL;
    //}
    template<> VARTYPE getType<INT>() const{
      return VT_INT;
    }
    template<> VARTYPE getType<UINT>() const{
      return VT_UINT;
    }

    template<typename T> T &getField(){
      constraint<T>();
    }
    template<> LONGLONG &getField<LONGLONG>(){
      return llVal;
    }
    template<> LONG &getField<LONG>(){
      return lVal;
    }
    template<> SHORT &getField<SHORT>(){
      return iVal;
    }
    template<> CHAR &getField<CHAR>(){
      return cVal;
    }
    template<> signed char &getField<signed char>(){
      return (signed char&)cVal;
    }
    template<> ULONGLONG &getField<ULONGLONG>(){
      return ullVal;
    }
    template<> ULONG &getField<ULONG>(){
      return ulVal;
    }
    template<> USHORT &getField<USHORT>(){
      return uiVal;
    }
    template<> BYTE &getField<BYTE>(){
      return bVal;
    }
    template<> float &getField<float>(){
      return fltVal;
    }
    template<> double &getField<double>(){
      return dblVal;
    }
    template<> INT &getField<INT>(){
      return intVal;
    }
    template<> UINT &getField<UINT>(){
      return uintVal;
    }

    bool operator==(const Variant &other) const{
      if(vt != other.vt) {
        return false;
      }
      ULONGLONG mask = ~0ul;
      switch (vt){
        case VT_I4:
        case VT_UI4:
        case VT_INT:
        case VT_UINT:
        case VT_R4:
          mask = 0xffffffffull;
          break;
        case VT_I2:
        case VT_UI2:
        case VT_BOOL:
          mask = 0xffffull;
          break;
        case VT_I1:
        case VT_UI1:
          mask = 0xffull;
          break;
      }
      return ((ullVal ^ other.ullVal) & mask) == 0;
    }

    /*!  \brief Compiler error, when used
     *
     *  This is only here to throw a compiler error when used. So the unsupported types are caught by the compiler
     *  and not by the runtime system
     */
    template<typename T> static void constraint(){
      char a[2];
      a[1.2] = 0;
    }
};

#endif //VARIANTNEW_H_
