////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2013 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

#include <cmake.h>
// cmake.h include header must come first

#include <Datetime.h>
#include <Duration.h>
#include <Lexer.h>
#include <RX.h>
#include <Variant.h>
#include <math.h>
#include <shared.h>
#include <stdio.h>
#include <stdlib.h>

#include <algorithm>
#include <sstream>

// These are all error messages generated by the expression evaluator, and are
// mostly concerned with how various operators interact with the different
// data types.
#define STRING_VARIANT_TIME_T "Cannot instantiate this type with a time_t value."
#define STRING_VARIANT_EXP_BOOL "Cannot exponentiate Booleans"
#define STRING_VARIANT_EXP_NON_INT "Cannot exponentiate to a non-integer power"
#define STRING_VARIANT_EXP_STRING "Cannot exponentiate strings"
#define STRING_VARIANT_EXP_DATE "Cannot exponentiate dates"
#define STRING_VARIANT_EXP_DURATION "Cannot exponentiate durations"
#define STRING_VARIANT_SUB_BOOL "Cannot subtract from a Boolean value"
#define STRING_VARIANT_SUB_STRING "Cannot subtract strings"
#define STRING_VARIANT_SUB_DATE "Cannot subtract a date"
#define STRING_VARIANT_ADD_BOOL "Cannot add two Boolean values"
#define STRING_VARIANT_ADD_DATE "Cannot add two date values"
#define STRING_VARIANT_MUL_BOOL "Cannot multiply Boolean values"
#define STRING_VARIANT_MUL_DATE "Cannot multiply date values"
#define STRING_VARIANT_MUL_REAL_STR "Cannot multiply real numbers by strings"
#define STRING_VARIANT_MUL_STR_REAL "Cannot multiply strings by real numbers"
#define STRING_VARIANT_MUL_STR_STR "Cannot multiply strings by strings"
#define STRING_VARIANT_MUL_STR_DATE "Cannot multiply strings by dates"
#define STRING_VARIANT_MUL_STR_DUR "Cannot multiply strings by durations"
#define STRING_VARIANT_MUL_DUR_STR "Cannot multiply durations by strings"
#define STRING_VARIANT_MUL_DUR_DATE "Cannot multiply durations by dates"
#define STRING_VARIANT_MUL_DUR_DUR "Cannot multiply durations by durations"
#define STRING_VARIANT_DIV_BOOL "Cannot divide Boolean values"
#define STRING_VARIANT_DIV_INT_BOOL "Cannot divide integers by Boolean values"
#define STRING_VARIANT_DIV_ZERO "Cannot divide by zero"
#define STRING_VARIANT_DIV_INT_STR "Cannot divide integer by string"
#define STRING_VARIANT_DIV_INT_DATE "Cannot divide integer by date values"
#define STRING_VARIANT_DIV_REAL_BOOL "Cannot divide real by Boolean"
#define STRING_VARIANT_DIV_REAL_STR "Cannot divide real numbers by strings"
#define STRING_VARIANT_DIV_REAL_DATE "Cannot divide real numbers by dates"
#define STRING_VARIANT_DIV_DUR_BOOL "Cannot divide duration by Boolean"
#define STRING_VARIANT_DIV_DUR_STR "Cannot divide durations by strings"
#define STRING_VARIANT_DIV_DUR_DATE "Cannot divide durations by dates"
#define STRING_VARIANT_MOD_BOOL "Cannot modulo Booleans"
#define STRING_VARIANT_MOD_DATE "Cannot modulo date values"
#define STRING_VARIANT_MOD_DUR "Cannot modulo duration values"
#define STRING_VARIANT_MOD_INT_BOOL "Cannot modulo integer by Boolean"
#define STRING_VARIANT_MOD_INT_DATE "Cannot modulo integer by date values"
#define STRING_VARIANT_MOD_INT_DUR "Cannot modulo integer by duration values"
#define STRING_VARIANT_MOD_INT_STR "Cannot modulo integer by string"
#define STRING_VARIANT_MOD_REAL_BOOL "Cannot modulo real by Boolean"
#define STRING_VARIANT_MOD_REAL_DUR "Cannot modulo real by duration values"
#define STRING_VARIANT_MOD_REAL_DATE "Cannot modulo real numbers by dates"
#define STRING_VARIANT_MOD_REAL_STR "Cannot modulo real numbers by strings"
#define STRING_VARIANT_MOD_STR "Cannot modulo string values"
#define STRING_VARIANT_MOD_ZERO "Cannot modulo zero"
#define STRING_VARIANT_SQRT_NEG "Cannot take the square root of a negative number."

std::string Variant::dateFormat = "";
bool Variant::searchCaseSensitive = true;
bool Variant::searchUsingRegex = true;

////////////////////////////////////////////////////////////////////////////////
Variant::Variant(const Variant& other) {
  _type = other._type;
  _bool = other._bool;
  _integer = other._integer;
  _real = other._real;
  _string = other._string;
  _date = other._date;
  _duration = other._duration;
  _source = other._source;
}

////////////////////////////////////////////////////////////////////////////////
Variant::Variant(const bool value) : _type(Variant::type_boolean), _bool(value) {}

////////////////////////////////////////////////////////////////////////////////
Variant::Variant(const int value) : _type(Variant::type_integer), _integer(value) {}

////////////////////////////////////////////////////////////////////////////////
Variant::Variant(const double value) : _type(Variant::type_real), _real(value) {}

////////////////////////////////////////////////////////////////////////////////
Variant::Variant(const std::string& value) : _type(Variant::type_string), _string(value) {}

////////////////////////////////////////////////////////////////////////////////
Variant::Variant(const char* value) : _type(Variant::type_string), _string(value) {}

////////////////////////////////////////////////////////////////////////////////
Variant::Variant(const time_t value, const enum type new_type) : _type(new_type) {
  switch (new_type) {
    case type_date:
      _date = value;
      break;
    case type_duration:
      _duration = value;
      break;
    default:
      throw std::string(STRING_VARIANT_TIME_T);
  }
}

////////////////////////////////////////////////////////////////////////////////
void Variant::source(const std::string& input) { _source = input; }

////////////////////////////////////////////////////////////////////////////////
const std::string& Variant::source() const { return _source; }

////////////////////////////////////////////////////////////////////////////////
Variant& Variant::operator=(const Variant& other) = default;

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator&&(const Variant& other) const {
  Variant left(*this);
  Variant right(other);

  if (left._type == type_string) Lexer::dequote(left._string);

  if (right._type == type_string) Lexer::dequote(right._string);

  left.cast(type_boolean);
  right.cast(type_boolean);

  return left._bool && right._bool;
}

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator||(const Variant& other) const {
  Variant left(*this);
  Variant right(other);

  if (left._type == type_string) Lexer::dequote(left._string);

  if (right._type == type_string) Lexer::dequote(right._string);

  left.cast(type_boolean);
  right.cast(type_boolean);

  return left._bool || right._bool;
}

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator_xor(const Variant& other) const {
  Variant left(*this);
  Variant right(other);

  if (left._type == type_string) Lexer::dequote(left._string);

  if (right._type == type_string) Lexer::dequote(right._string);

  left.cast(type_boolean);
  right.cast(type_boolean);

  return (left._bool && !right._bool) || (!left._bool && right._bool);
}

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator<(const Variant& other) const {
  Variant left(*this);
  Variant right(other);

  if (left._type == type_string) Lexer::dequote(left._string);

  if (right._type == type_string) Lexer::dequote(right._string);

  switch (left._type) {
    case type_boolean:
      switch (right._type) {
        case type_boolean:
          return !left._bool && right._bool;
        case type_integer:
          left.cast(type_integer);
          return left._integer < right._integer;
        case type_real:
          left.cast(type_real);
          return left._real < right._real;
        case type_string:
          left.cast(type_string);
          return left._string < right._string;
        case type_date:
          left.cast(type_date);
          return left._date < right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration < right._duration;
      }
      break;

    case type_integer:
      switch (right._type) {
        case type_boolean:
          right.cast(type_integer);
          return left._integer < right._integer;
        case type_integer:
          return left._integer < right._integer;
        case type_real:
          left.cast(type_real);
          return left._real < right._real;
        case type_string:
          left.cast(type_string);
          return left._string < right._string;
        case type_date:
          left.cast(type_date);
          return left._date < right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration < right._duration;
      }
      break;

    case type_real:
      switch (right._type) {
        case type_boolean:
          right.cast(type_real);
          return left._real < right._real;
        case type_integer:
          right.cast(type_real);
          return left._real < right._real;
        case type_real:
          return left._real < right._real;
        case type_string:
          left.cast(type_string);
          return left._string < right._string;
        case type_date:
          left.cast(type_date);
          return left._date < right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration < right._duration;
      }
      break;

    case type_string:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
          right.cast(type_string);
          return left._string < right._string;

        case type_string: {
          if (left._string == right._string) return false;

          auto order = Task::customOrder.find(left.source());
          if (order != Task::customOrder.end()) {
            // Guaranteed to be found, because of ColUDA::validate ().
            auto posLeft = std::find(order->second.begin(), order->second.end(), left._string);
            auto posRight = std::find(order->second.begin(), order->second.end(), right._string);
            return posLeft < posRight;
          } else {
            if (left.trivial() || right.trivial()) return false;

            return left._string < right._string;
          }
        }

        case type_date:
          if (left.trivial() || right.trivial()) return false;

          left.cast(type_date);
          return left._date < right._date;

        case type_duration:
          if (left.trivial() || right.trivial()) return false;

          left.cast(type_duration);
          return left._duration < right._duration;
      }
      break;

    case type_date:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
        case type_string:
        case type_date:
        case type_duration:
          if (left.trivial() || right.trivial()) return false;

          right.cast(type_date);
          return left._date < right._date;
      }
      break;

    case type_duration:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
        case type_string:
        case type_date:
        case type_duration:
          if (left.trivial() || right.trivial()) return false;

          right.cast(type_duration);
          return left._duration < right._duration;
      }
      break;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator<=(const Variant& other) const {
  Variant left(*this);
  Variant right(other);

  if (left._type == type_string) Lexer::dequote(left._string);

  if (right._type == type_string) Lexer::dequote(right._string);

  switch (left._type) {
    case type_boolean:
      switch (right._type) {
        case type_boolean:
          return !left._bool || right._bool;
        case type_integer:
          left.cast(type_integer);
          return left._integer <= right._integer;
        case type_real:
          left.cast(type_real);
          return left._real <= right._real;
        case type_string:
          left.cast(type_string);
          return left._string <= right._string;
        case type_date:
          left.cast(type_date);
          return left._date <= right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration <= right._duration;
      }
      break;

    case type_integer:
      switch (right._type) {
        case type_boolean:
          right.cast(type_integer);
          return left._integer <= right._integer;
        case type_integer:
          return left._integer <= right._integer;
        case type_real:
          left.cast(type_real);
          return left._real <= right._real;
        case type_string:
          left.cast(type_string);
          return left._string <= right._string;
        case type_date:
          left.cast(type_date);
          return left._date <= right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration <= right._duration;
      }
      break;

    case type_real:
      switch (right._type) {
        case type_boolean:
          right.cast(type_real);
          return left._real <= right._real;
        case type_integer:
          right.cast(type_real);
          return left._real <= right._real;
        case type_real:
          return left._real <= right._real;
        case type_string:
          left.cast(type_string);
          return left._string <= right._string;
        case type_date:
          left.cast(type_date);
          return left._date <= right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration <= right._duration;
      }
      break;

    case type_string:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
          right.cast(type_string);
          return left._string <= right._string;

        case type_string: {
          if (left._string == right._string) return true;

          auto order = Task::customOrder.find(left.source());
          if (order != Task::customOrder.end()) {
            // Guaranteed to be found, because of ColUDA::validate ().
            auto posLeft = std::find(order->second.begin(), order->second.end(), left._string);
            auto posRight = std::find(order->second.begin(), order->second.end(), right._string);
            return posLeft <= posRight;
          } else {
            if (left.trivial() || right.trivial()) return false;

            return left._string <= right._string;
          }
        }

        case type_date:
          if (left.trivial() || right.trivial()) return false;

          left.cast(type_date);
          return left._date <= right._date;

        case type_duration:
          if (left.trivial() || right.trivial()) return false;

          left.cast(type_duration);
          return left._duration <= right._duration;
      }
      break;

    case type_date:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
        case type_string:
        case type_date:
        case type_duration:
          if (left.trivial() || right.trivial()) return false;

          right.cast(type_date);
          return left._date <= right._date;
      }
      break;

    case type_duration:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
        case type_string:
        case type_date:
        case type_duration:
          if (left.trivial() || right.trivial()) return false;

          right.cast(type_duration);
          return left._duration <= right._duration;
      }
      break;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator>(const Variant& other) const {
  Variant left(*this);
  Variant right(other);

  if (left._type == type_string) Lexer::dequote(left._string);

  if (right._type == type_string) Lexer::dequote(right._string);

  switch (left._type) {
    case type_boolean:
      switch (right._type) {
        case type_boolean:
          return !left._bool && right._bool;
        case type_integer:
          left.cast(type_integer);
          return left._integer > right._integer;
        case type_real:
          left.cast(type_real);
          return left._real > right._real;
        case type_string:
          left.cast(type_string);
          return left._string > right._string;
        case type_date:
          left.cast(type_date);
          return left._date > right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration > right._duration;
      }
      break;

    case type_integer:
      switch (right._type) {
        case type_boolean:
          right.cast(type_integer);
          return left._integer > right._integer;
        case type_integer:
          return left._integer > right._integer;
        case type_real:
          left.cast(type_real);
          return left._real > right._real;
        case type_string:
          left.cast(type_string);
          return left._string > right._string;
        case type_date:
          left.cast(type_date);
          return left._date > right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration > right._duration;
      }
      break;

    case type_real:
      switch (right._type) {
        case type_boolean:
          right.cast(type_real);
          return left._real > right._real;
        case type_integer:
          right.cast(type_real);
          return left._real > right._real;
        case type_real:
          return left._real > right._real;
        case type_string:
          left.cast(type_string);
          return left._string > right._string;
        case type_date:
          left.cast(type_date);
          return left._date > right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration > right._duration;
      }
      break;

    case type_string:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
          right.cast(type_string);
          return left._string > right._string;

        case type_string: {
          if (left._string == right._string) return false;

          auto order = Task::customOrder.find(left.source());
          if (order != Task::customOrder.end()) {
            // Guaranteed to be found, because of ColUDA::validate ().
            auto posLeft = std::find(order->second.begin(), order->second.end(), left._string);
            auto posRight = std::find(order->second.begin(), order->second.end(), right._string);
            return posLeft > posRight;
          } else {
            if (left.trivial() || right.trivial()) return false;

            return left._string > right._string;
          }
        }

        case type_date:
          if (left.trivial() || right.trivial()) return false;

          left.cast(type_date);
          return left._date > right._date;

        case type_duration:
          if (left.trivial() || right.trivial()) return false;

          left.cast(type_duration);
          return left._duration > right._duration;
      }
      break;

    case type_date:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
        case type_string:
        case type_date:
        case type_duration:
          if (left.trivial() || right.trivial()) return false;

          right.cast(type_date);
          return left._date > right._date;
      }
      break;

    case type_duration:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
        case type_string:
        case type_date:
        case type_duration:
          if (left.trivial() || right.trivial()) return false;

          right.cast(type_duration);
          return left._duration > right._duration;
      }
      break;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator>=(const Variant& other) const {
  Variant left(*this);
  Variant right(other);

  if (left._type == type_string) Lexer::dequote(left._string);

  if (right._type == type_string) Lexer::dequote(right._string);

  switch (left._type) {
    case type_boolean:
      switch (right._type) {
        case type_boolean:
          return left._bool || !right._bool;
        case type_integer:
          left.cast(type_integer);
          return left._integer >= right._integer;
        case type_real:
          left.cast(type_real);
          return left._real >= right._real;
        case type_string:
          left.cast(type_string);
          return left._string >= right._string;
        case type_date:
          left.cast(type_date);
          return left._date >= right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration >= right._duration;
      }
      break;

    case type_integer:
      switch (right._type) {
        case type_boolean:
          right.cast(type_integer);
          return left._integer >= right._integer;
        case type_integer:
          return left._integer >= right._integer;
        case type_real:
          left.cast(type_real);
          return left._real >= right._real;
        case type_string:
          left.cast(type_string);
          return left._string >= right._string;
        case type_date:
          left.cast(type_date);
          return left._date >= right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration >= right._duration;
      }
      break;

    case type_real:
      switch (right._type) {
        case type_boolean:
          right.cast(type_real);
          return left._real >= right._real;
        case type_integer:
          right.cast(type_real);
          return left._real >= right._real;
        case type_real:
          return left._real >= right._real;
        case type_string:
          left.cast(type_string);
          return left._string >= right._string;
        case type_date:
          left.cast(type_date);
          return left._date >= right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration >= right._duration;
      }
      break;

    case type_string:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
          right.cast(type_string);
          return left._string >= right._string;

        case type_string: {
          if (left._string == right._string) return true;

          auto order = Task::customOrder.find(left.source());
          if (order != Task::customOrder.end()) {
            // Guaranteed to be found, because of ColUDA::validate ().
            auto posLeft = std::find(order->second.begin(), order->second.end(), left._string);
            auto posRight = std::find(order->second.begin(), order->second.end(), right._string);
            return posLeft >= posRight;
          } else {
            if (left.trivial() || right.trivial()) return false;

            return left._string >= right._string;
          }
        }

        case type_date:
          if (left.trivial() || right.trivial()) return false;

          left.cast(type_date);
          return left._date >= right._date;

        case type_duration:
          if (left.trivial() || right.trivial()) return false;

          left.cast(type_duration);
          return left._duration >= right._duration;
      }
      break;

    case type_date:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
        case type_string:
        case type_date:
        case type_duration:
          if (left.trivial() || right.trivial()) return false;

          right.cast(type_date);
          return left._date >= right._date;
      }
      break;

    case type_duration:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
        case type_string:
        case type_date:
        case type_duration:
          if (left.trivial() || right.trivial()) return false;

          right.cast(type_duration);
          return left._duration >= right._duration;
      }
      break;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator==(const Variant& other) const {
  Variant left(*this);
  Variant right(other);

  if (left._type == type_string) Lexer::dequote(left._string);

  if (right._type == type_string) Lexer::dequote(right._string);

  switch (left._type) {
    case type_boolean:
      switch (right._type) {
        case type_boolean:
          return left._bool == right._bool;
        case type_integer:
          left.cast(type_integer);
          return left._integer == right._integer;
        case type_real:
          left.cast(type_real);
          return left._real == right._real;
        case type_string:
          left.cast(type_string);
          return left._string == right._string;
        case type_date:
          left.cast(type_date);
          return left._date == right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration == right._duration;
      }
      break;

    case type_integer:
      switch (right._type) {
        case type_boolean:
          right.cast(type_integer);
          return left._integer == right._integer;
        case type_integer:
          return left._integer == right._integer;
        case type_real:
          left.cast(type_real);
          return left._real == right._real;
        case type_string:
          left.cast(type_string);
          return left._string == right._string;
        case type_date:
          left.cast(type_date);
          return left._date == right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration == right._duration;
      }
      break;

    case type_real:
      switch (right._type) {
        case type_boolean:
          right.cast(type_real);
          return left._real == right._real;
        case type_integer:
          right.cast(type_real);
          return left._real == right._real;
        case type_real:
          return left._real == right._real;
        case type_string:
          left.cast(type_string);
          return left._string == right._string;
        case type_date:
          left.cast(type_date);
          return left._date == right._date;
        case type_duration:
          left.cast(type_duration);
          return left._duration == right._duration;
      }
      break;

    case type_string:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
        case type_string:
          right.cast(type_string);

          // Status is always compared caseless.
          if (left.source() == "status") return compare(left._string, right._string, false);

          return left._string == right._string;

        case type_date:
          if (left.trivial() || right.trivial()) return false;

          left.cast(type_date);
          return left._date == right._date;

        case type_duration:
          left.cast(type_duration);
          return left._duration == right._duration;
      }
      break;

    case type_date:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
        case type_string:
        case type_date:
        case type_duration:
          if (left.trivial() || right.trivial()) return false;

          right.cast(type_date);
          return left._date == right._date;
      }
      break;

    case type_duration:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
        case type_string:
        case type_date:
        case type_duration:
          right.cast(type_duration);
          return left._duration == right._duration;
      }
      break;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator!=(const Variant& other) const { return !(*this == other); }

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator_match(const Variant& other, const Task& task) const {
  // Simple matching case first.
  Variant left(*this);
  Variant right(other);

  if (left._type == type_string) Lexer::dequote(left._string);

  if (right._type == type_string) Lexer::dequote(right._string);

  left.cast(type_string);
  right.cast(type_string);

  std::string pattern = right._string;
  Lexer::dequote(pattern);

  if (searchUsingRegex) {
    RX r(pattern, searchCaseSensitive);
    if (r.match(left._string)) return true;

    // If the above did not match, and the left source is "description", then
    // in the annotations.
    if (left.source() == "description") {
      for (auto& a : task.getAnnotations())
        if (r.match(a.second)) return true;
    }
  } else {
    // If pattern starts with '^', look for a leftmost compare only.
    if (pattern[0] == '^' && find(left._string, pattern.substr(1), searchCaseSensitive) == 0) {
      return true;
    }

    // If pattern ends with '$', look for a rightmost compare only.
    else if (pattern[pattern.length() - 1] == '$' &&
             find(left._string, pattern.substr(0, pattern.length() - 1), searchCaseSensitive) ==
                 (left._string.length() - pattern.length() + 1)) {
      return true;
    }

    else if (find(left._string, pattern, searchCaseSensitive) != std::string::npos)
      return true;

    // If the above did not match, and the left source is "description", then
    // in the annotations.
    if (left.source() == "description") {
      for (auto& a : task.getAnnotations())
        if (find(a.second, pattern, searchCaseSensitive) != std::string::npos) return true;
    }
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator_nomatch(const Variant& other, const Task& task) const {
  return !operator_match(other, task);
}

////////////////////////////////////////////////////////////////////////////////
// Partial match is mostly a clone of operator==, but with some overrides:
//
//   date <partial> date     --> same day check
//   string <partial> string --> leftmost
//
bool Variant::operator_partial(const Variant& other) const {
  Variant left(*this);
  Variant right(other);

  if (left._type == type_string) Lexer::dequote(left._string);

  if (right._type == type_string) Lexer::dequote(right._string);

  switch (left._type) {
    case type_boolean:
      switch (right._type) {
        case type_boolean:
          return left._bool == right._bool;
        case type_integer:
          left.cast(type_integer);
          return left._integer == right._integer;
        case type_real:
          left.cast(type_real);
          return left._real == right._real;
        case type_string:
          left.cast(type_string);
          return left._string == right._string;

        // Same-day comparison.
        case type_date: {
          left.cast(type_date);
          Datetime left_date(left._date);
          Datetime right_date(right._date);
          return left_date.sameDay(right_date);
        }

        case type_duration:
          left.cast(type_duration);
          return left._duration == right._duration;
      }
      break;

    case type_integer:
      switch (right._type) {
        case type_boolean:
          right.cast(type_integer);
          return left._integer == right._integer;
        case type_integer:
          return left._integer == right._integer;
        case type_real:
          left.cast(type_real);
          return left._real == right._real;
        case type_string:
          left.cast(type_string);
          return left._string == right._string;

        // Same-day comparison.
        case type_date: {
          left.cast(type_date);
          Datetime left_date(left._date);
          Datetime right_date(right._date);
          return left_date.sameDay(right_date);
        }

        case type_duration:
          left.cast(type_duration);
          return left._duration == right._duration;
      }
      break;

    case type_real:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
          right.cast(type_real);
          return left._real == right._real;

        case type_string:
          left.cast(type_string);
          return left._string == right._string;

        // Same-day comparison.
        case type_date: {
          left.cast(type_date);
          Datetime left_date(left._date);
          Datetime right_date(right._date);
          return left_date.sameDay(right_date);
        }

        case type_duration:
          left.cast(type_duration);
          return left._duration == right._duration;
      }
      break;

    case type_string:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
        case type_string: {
          right.cast(type_string);

          // Status is always compared caseless.
          if (left.source() == "status") return compare(left._string, right._string, false);

          int left_len = left._string.length();
          int right_len = right._string.length();

          if ((left_len == 0 && right_len != 0) || (left_len != 0 && right_len == 0)) return false;

          // Dodgy.
          if (left._string.length() < right._string.length()) return false;

          return left._string.substr(0, right._string.length()) == right._string;
        }

        // Same-day comparison.
        case type_date: {
          if (left.trivial() || right.trivial()) return false;

          left.cast(type_date);
          Datetime left_date(left._date);
          Datetime right_date(right._date);
          return left_date.sameDay(right_date);
        }

        case type_duration:
          left.cast(type_duration);
          return left._duration == right._duration;
      }
      break;

    case type_date:
      switch (right._type) {
        // Same-day comparison.
        case type_string: {
          if (left.trivial() || right.trivial()) return false;

          right.cast(type_date);
          Datetime left_date(left._date);
          Datetime right_date(right._date);
          return left_date.sameDay(right_date);
        }

        case type_boolean:
        case type_integer:
        case type_real:
        case type_date:
        case type_duration: {
          right.cast(type_date);
          Datetime left_date(left._date);
          Datetime right_date(right._date);
          return left_date.sameDay(right_date);
        }
      }
      break;

    case type_duration:
      switch (right._type) {
        // Same-day comparison.
        case type_boolean:
        case type_integer:
        case type_real:
        case type_string:
        case type_date:
        case type_duration:
          right.cast(type_duration);
          return left._duration == right._duration;
      }
      break;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
// Inverse of operator_partial.
bool Variant::operator_nopartial(const Variant& other) const { return !operator_partial(other); }

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator_hastag(const Variant& other, const Task& task) const {
  Variant right(other);
  right.cast(type_string);
  Lexer::dequote(right._string);
  return task.hasTag(right._string);
}

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator_notag(const Variant& other, const Task& task) const {
  return !operator_hastag(other, task);
}

////////////////////////////////////////////////////////////////////////////////
bool Variant::operator!() const {
  Variant left(*this);

  if (left._type == type_string) Lexer::dequote(left._string);

  left.cast(type_boolean);
  return !left._bool;
}

////////////////////////////////////////////////////////////////////////////////
Variant& Variant::operator^=(const Variant& other) {
  switch (_type) {
    case type_boolean:
      throw std::string(STRING_VARIANT_EXP_BOOL);
      break;

    case type_integer:
      switch (other._type) {
        case type_boolean:
          throw std::string(STRING_VARIANT_EXP_BOOL);
        case type_integer:
          _integer = (int)pow(static_cast<double>(_integer), static_cast<double>(other._integer));
          break;
        case type_real:
          throw std::string(STRING_VARIANT_EXP_NON_INT);
        case type_string:
          throw std::string(STRING_VARIANT_EXP_STRING);
        case type_date:
          throw std::string(STRING_VARIANT_EXP_DATE);
        case type_duration:
          throw std::string(STRING_VARIANT_EXP_DURATION);
      }
      break;

    case type_real:
      switch (other._type) {
        case type_boolean:
          throw std::string(STRING_VARIANT_EXP_BOOL);
        case type_integer:
          _real = pow(_real, static_cast<double>(other._integer));
          break;
        case type_real:
          throw std::string(STRING_VARIANT_EXP_NON_INT);
        case type_string:
          throw std::string(STRING_VARIANT_EXP_STRING);
        case type_date:
          throw std::string(STRING_VARIANT_EXP_DATE);
        case type_duration:
          throw std::string(STRING_VARIANT_EXP_DURATION);
      }
      break;

    case type_string:
      throw std::string(STRING_VARIANT_EXP_STRING);
      break;

    case type_date:
      throw std::string(STRING_VARIANT_EXP_DATE);
      break;

    case type_duration:
      throw std::string(STRING_VARIANT_EXP_DURATION);
      break;
  }

  return *this;
}

////////////////////////////////////////////////////////////////////////////////
Variant Variant::operator^(const Variant& other) const {
  Variant left(*this);
  left ^= other;
  return left;
}

////////////////////////////////////////////////////////////////////////////////
Variant& Variant::operator-=(const Variant& other) {
  Variant right(other);

  switch (_type) {
    case type_boolean:
      throw std::string(STRING_VARIANT_SUB_BOOL);
      break;

    case type_integer:
      switch (right._type) {
        case type_boolean:
          right.cast(type_integer);
          _integer -= right._integer;
          break;
        case type_integer:
          _integer -= right._integer;
          break;
        case type_real:
          cast(type_real);
          _real -= right._real;
          break;
        case type_string:
          throw std::string(STRING_VARIANT_SUB_STRING);
        case type_date:
          cast(type_date);
          _date -= right._date;
          break;
        case type_duration:
          cast(type_duration);
          _duration -= right._duration;
          break;
      }
      break;

    case type_real:
      switch (right._type) {
        case type_string:
          throw std::string(STRING_VARIANT_SUB_STRING);

        case type_boolean:
        case type_integer:
        case type_real:
        case type_date:
        case type_duration:
          right.cast(type_real);
          _real -= right._real;
          break;
      }
      break;

    case type_string:
      switch (right._type) {
        case type_string:
          cast(type_string);
          _string += '-' + right._string;
          break;
        case type_boolean:
        case type_integer:
        case type_real:
        case type_date:
        case type_duration:
          throw std::string(STRING_VARIANT_SUB_STRING);
          break;
      }
      break;

    case type_date:
      switch (right._type) {
        case type_boolean:
          right.cast(type_integer);
          _date -= right._integer;
          break;
        case type_integer:
          _date -= right._integer;
          break;
        case type_real:
          _date -= (int)right._real;
          break;
        case type_string:
          throw std::string(STRING_VARIANT_SUB_STRING);
        case type_date:
          _type = Variant::type_duration;
          _duration = _date - right._date;
          break;
        case type_duration:
          _date -= right._duration;
          break;
      }
      break;

    case type_duration:
      switch (right._type) {
        case type_boolean:
          right.cast(type_integer);
          _duration -= right._integer;
          break;
        case type_integer:
          _duration -= right._integer;
          break;
        case type_real:
          _duration -= (int)right._real;
          break;
        case type_string:
          throw std::string(STRING_VARIANT_SUB_STRING);
        case type_date:
          throw std::string(STRING_VARIANT_SUB_DATE);
        case type_duration:
          _duration -= right._duration;
          break;
      }
      break;
  }

  return *this;
}

////////////////////////////////////////////////////////////////////////////////
Variant Variant::operator-(const Variant& other) const {
  Variant left(*this);
  left -= other;
  return left;
}

////////////////////////////////////////////////////////////////////////////////
Variant& Variant::operator+=(const Variant& other) {
  Variant right(other);

  if (right._type == type_string) Lexer::dequote(right._string);

  switch (_type) {
    case type_boolean:
      switch (right._type) {
        case type_boolean:
          throw std::string(STRING_VARIANT_ADD_BOOL);
        case type_integer:
          cast(type_integer);
          _integer += right._integer;
          break;
        case type_real:
          cast(type_real);
          _real += right._real;
          break;
        case type_string:
          cast(type_string);
          _string += right._string;
          break;
        case type_date:
          cast(type_date);
          _date += right._date;
          break;
        case type_duration:
          cast(type_duration);
          _duration += right._duration;
          break;
      }
      break;

    case type_integer:
      switch (right._type) {
        case type_boolean:
          right.cast(type_integer);
          _integer += right._integer;
          break;
        case type_integer:
          _integer += right._integer;
          break;
        case type_real:
          cast(type_real);
          _real += right._real;
          break;
        case type_string:
          cast(type_string);
          _string += right._string;
          break;
        case type_date:
          cast(type_date);
          _date += right._date;
          break;
        case type_duration:
          cast(type_duration);
          _duration += right._duration;
          break;
      }
      break;

    case type_real:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
          right.cast(type_real);
          _real += right._real;
          break;

        case type_string:
          cast(type_string);
          _string += right._string;
          break;

        case type_date:
          _type = type_date;
          _date = (unsigned)(int)_real + right._date;
          break;

        case type_duration:
          _type = type_duration;
          _duration = (unsigned)(int)_real + right._duration;
          break;
      }
      break;

    case type_string:
      _string += (std::string)right;
      break;

    case type_date:
      switch (right._type) {
        case type_boolean:
          right.cast(type_date);
          _date += right._date;
          break;
        case type_integer:
          _date += right._integer;
          break;
        case type_real:
          _date += (int)right._real;
          break;
        case type_string:
          cast(type_string);
          _string += right._string;
          break;
        case type_date:
          throw std::string(STRING_VARIANT_ADD_DATE);
        case type_duration:
          _date += right._duration;
          break;
      }
      break;

    case type_duration:
      switch (right._type) {
        case type_boolean:
          right.cast(type_duration);
          _duration += right._duration;
          break;
        case type_integer:
          _duration += right._integer;
          break;
        case type_real:
          _duration += (int)right._real;
          break;
        case type_string:
          cast(type_string);
          _string += right._string;
          break;
        case type_date:
          _type = Variant::type_date;
          _date += right._date + _duration;
          break;
        case type_duration:
          _duration += right._duration;
          break;
      }
      break;
  }

  return *this;
}

////////////////////////////////////////////////////////////////////////////////
Variant Variant::operator+(const Variant& other) const {
  Variant left(*this);
  left += other;
  return left;
}

////////////////////////////////////////////////////////////////////////////////
Variant& Variant::operator*=(const Variant& other) {
  Variant right(other);

  if (right._type == type_string) Lexer::dequote(right._string);

  switch (_type) {
    case type_boolean:
      switch (right._type) {
        case type_boolean:
          throw std::string(STRING_VARIANT_MUL_BOOL);
        case type_integer:
          cast(type_integer);
          _integer *= right._integer;
          break;
        case type_real:
          cast(type_real);
          _real *= right._real;
          break;
        case type_string:
          _string = (_bool ? right._string : "");
          _type = type_string;
          break;
        case type_date:
          throw std::string(STRING_VARIANT_MUL_DATE);
        case type_duration:
          cast(type_duration);
          _duration *= right._duration;
          break;
      }
      break;

    case type_integer:
      switch (right._type) {
        case type_boolean:
          right.cast(type_integer);
          _integer *= right._integer;
          break;
        case type_integer:
          _integer *= right._integer;
          break;
        case type_real:
          cast(type_real);
          _real *= right._real;
          break;
        case type_string: {
          int limit = _integer;
          // assert (limit < 128);
          _type = type_string;
          _string = "";
          while (limit--) _string += right._string;
        } break;
        case type_date:
          throw std::string(STRING_VARIANT_MUL_DATE);
        case type_duration:
          cast(type_duration);
          _duration *= right._duration;
          break;
      }
      break;

    case type_real:
      switch (right._type) {
        case type_boolean:
        case type_integer:
        case type_real:
          right.cast(type_real);
          _real *= right._real;
          break;

        case type_string:
          throw std::string(STRING_VARIANT_MUL_REAL_STR);

        case type_date:
          throw std::string(STRING_VARIANT_MUL_DATE);

        case type_duration:
          _type = type_duration;
          _duration = (time_t)(unsigned)(int)(_real * static_cast<double>(right._duration));
      }
      break;

    case type_string:
      switch (right._type) {
        case type_boolean:
          if (!right._bool) _string = "";
          break;
        case type_integer: {
          int limit = right._integer - 1;
          // assert (limit < 128);
          std::string fragment = _string;
          while (limit--) _string += fragment;
        } break;
        case type_real:
          throw std::string(STRING_VARIANT_MUL_STR_REAL);
        case type_string:
          throw std::string(STRING_VARIANT_MUL_STR_STR);
        case type_date:
          throw std::string(STRING_VARIANT_MUL_STR_DATE);
        case type_duration:
          throw std::string(STRING_VARIANT_MUL_STR_DUR);
      }
      break;

    case type_date:
      throw std::string(STRING_VARIANT_MUL_DATE);

    case type_duration:
      switch (right._type) {
        case type_boolean:
          right.cast(type_duration);
          _duration *= right._duration;
          break;
        case type_integer:
          _duration *= right._integer;
          break;
        case type_real:
          _duration = (time_t)(unsigned)(int)(static_cast<double>(_duration) * right._real);
          break;
        case type_string:
          throw std::string(STRING_VARIANT_MUL_DUR_STR);
        case type_date:
          throw std::string(STRING_VARIANT_MUL_DUR_DATE);
        case type_duration:
          throw std::string(STRING_VARIANT_MUL_DUR_DUR);
      }
      break;
  }

  return *this;
}

////////////////////////////////////////////////////////////////////////////////
Variant Variant::operator*(const Variant& other) const {
  Variant left(*this);
  left *= other;
  return left;
}

////////////////////////////////////////////////////////////////////////////////
Variant& Variant::operator/=(const Variant& other) {
  Variant right(other);

  switch (_type) {
    case type_boolean:
      throw std::string(STRING_VARIANT_DIV_BOOL);
      break;

    case type_integer:
      switch (right._type) {
        case type_boolean:
          throw std::string(STRING_VARIANT_DIV_INT_BOOL);

        case type_integer:
          if (right._integer == 0) throw std::string(STRING_VARIANT_DIV_ZERO);
          _integer /= right._integer;
          break;

        case type_real:
          if (right._real == 0.0) throw std::string(STRING_VARIANT_DIV_ZERO);
          cast(type_real);
          _real /= right._real;
          break;

        case type_string:
          throw std::string(STRING_VARIANT_DIV_INT_STR);

        case type_date:
          throw std::string(STRING_VARIANT_DIV_INT_DATE);

        case type_duration:
          if (right._duration == 0) throw std::string(STRING_VARIANT_DIV_ZERO);
          _type = type_duration;
          _duration = (time_t)(unsigned)(int)(_integer / right._duration);
          break;
      }
      break;

    case type_real:
      switch (right._type) {
        case type_boolean:
          throw std::string(STRING_VARIANT_DIV_REAL_BOOL);

        case type_integer:
          if (right._integer == 0) throw std::string(STRING_VARIANT_DIV_ZERO);
          _real /= static_cast<double>(right._integer);
          break;

        case type_real:
          if (right._real == 0) throw std::string(STRING_VARIANT_DIV_ZERO);
          _real /= right._real;
          break;

        case type_string:
          throw std::string(STRING_VARIANT_DIV_REAL_STR);

        case type_date:
          throw std::string(STRING_VARIANT_DIV_REAL_DATE);

        case type_duration:
          if (right._duration == 0) throw std::string(STRING_VARIANT_DIV_ZERO);
          _type = type_duration;
          _duration = (time_t)(unsigned)(int)(_real / right._duration);
          break;
      }
      break;

    case type_string:
      throw std::string(STRING_VARIANT_DIV_REAL_STR);
      break;

    case type_date:
      throw std::string(STRING_VARIANT_DIV_REAL_DATE);

    case type_duration:
      switch (right._type) {
        case type_boolean:
          throw std::string(STRING_VARIANT_DIV_DUR_BOOL);

        case type_integer:
          if (right._integer == 0) throw std::string(STRING_VARIANT_DIV_ZERO);
          _duration /= right._integer;
          break;

        case type_real:
          if (right._real == 0) throw std::string(STRING_VARIANT_DIV_ZERO);
          _duration = (time_t)(unsigned)(int)(static_cast<double>(_duration) / right._real);
          break;

        case type_string:
          throw std::string(STRING_VARIANT_DIV_DUR_STR);

        case type_date:
          throw std::string(STRING_VARIANT_DIV_DUR_DATE);

        case type_duration:
          _type = type_real;
          _real = static_cast<double>(_duration) / static_cast<double>(right._duration);
          break;
      }
      break;
  }

  return *this;
}

////////////////////////////////////////////////////////////////////////////////
Variant Variant::operator/(const Variant& other) const {
  Variant left(*this);
  left /= other;
  return left;
}

////////////////////////////////////////////////////////////////////////////////
Variant& Variant::operator%=(const Variant& other) {
  Variant right(other);

  switch (_type) {
    case type_boolean:
      throw std::string(STRING_VARIANT_MOD_BOOL);
      break;

    case type_integer:
      switch (right._type) {
        case type_boolean:
          throw std::string(STRING_VARIANT_MOD_INT_BOOL);

        case type_integer:
          if (right._integer == 0) throw std::string(STRING_VARIANT_MOD_ZERO);
          _integer %= right._integer;
          break;

        case type_real:
          if (right._real == 0.0) throw std::string(STRING_VARIANT_MOD_ZERO);
          cast(type_real);
          _real = fmod(_real, right._real);
          break;

        case type_string:
          throw std::string(STRING_VARIANT_MOD_INT_STR);

        case type_date:
          throw std::string(STRING_VARIANT_MOD_INT_DATE);

        case type_duration:
          throw std::string(STRING_VARIANT_MOD_INT_DUR);
      }
      break;

    case type_real:
      switch (right._type) {
        case type_boolean:
          throw std::string(STRING_VARIANT_MOD_REAL_BOOL);

        case type_integer:
          if (right._integer == 0) throw std::string(STRING_VARIANT_MOD_ZERO);
          _real = fmod(_real, static_cast<double>(right._integer));
          break;

        case type_real:
          if (right._real == 0) throw std::string(STRING_VARIANT_MOD_ZERO);
          _real = fmod(_real, right._real);
          break;

        case type_string:
          throw std::string(STRING_VARIANT_MOD_REAL_STR);

        case type_date:
          throw std::string(STRING_VARIANT_MOD_REAL_DATE);

        case type_duration:
          throw std::string(STRING_VARIANT_MOD_REAL_DUR);
      }
      break;

    case type_string:
      throw std::string(STRING_VARIANT_MOD_STR);

    case type_date:
      throw std::string(STRING_VARIANT_MOD_DATE);

    case type_duration:
      throw std::string(STRING_VARIANT_MOD_DUR);
  }

  return *this;
}

////////////////////////////////////////////////////////////////////////////////
Variant Variant::operator%(const Variant& other) const {
  Variant left(*this);
  left %= other;
  return left;
}

////////////////////////////////////////////////////////////////////////////////
Variant::operator std::string() const {
  switch (_type) {
    case type_boolean:
      return std::string(_bool ? "true" : "false");

    case type_integer: {
      std::stringstream s;
      s << _integer;
      return s.str();
    }

    case type_real: {
      std::stringstream s;
      s << _real;
      return s.str();
    }

    case type_string:
      return _string;

    case type_date:
      return Datetime(_date).toISOLocalExtended();

    case type_duration:
      return Duration(_duration).formatISO();
  }

  return "";
}

////////////////////////////////////////////////////////////////////////////////
void Variant::sqrt() {
  if (_type == type_string) Lexer::dequote(_string);

  cast(type_real);
  if (_real < 0.0) throw std::string(STRING_VARIANT_SQRT_NEG);
  _real = ::sqrt(_real);
}

////////////////////////////////////////////////////////////////////////////////
void Variant::cast(const enum type new_type) {
  // Short circuit.
  if (_type == new_type) return;

  // From type_boolean
  switch (_type) {
    case type_boolean:
      switch (new_type) {
        case type_boolean:
          break;
        case type_integer:
          _integer = _bool ? 1 : 0;
          break;
        case type_real:
          _real = _bool ? 1.0 : 0.0;
          break;
        case type_string:
          _string = _bool ? "true" : "false";
          break;
        case type_date:
          _date = _bool ? 1 : 0;
          break;
        case type_duration:
          _duration = _bool ? 1 : 0;
          break;
      }
      break;

    case type_integer:
      switch (new_type) {
        case type_boolean:
          _bool = _integer == 0 ? false : true;
          break;
        case type_integer:
          break;
        case type_real:
          _real = static_cast<double>(_integer);
          break;
        case type_string: {
          char temp[24];
          snprintf(temp, 24, "%lld", _integer);
          _string = temp;
        } break;
        case type_date:
          _date = (time_t)_integer;
          break;
        case type_duration:
          _duration = (time_t)_integer;
          break;
      }
      break;

    case type_real:
      switch (new_type) {
        case type_boolean:
          _bool = _real == 0.0 ? false : true;
          break;
        case type_integer:
          _integer = (long long)_real;
          break;
        case type_real:
          break;
        case type_string: {
          char temp[24];
          snprintf(temp, 24, "%g", _real);
          _string = temp;
        } break;
        case type_date:
          _date = (time_t)(int)_real;
          break;
        case type_duration:
          _duration = (time_t)(int)_real;
          break;
      }
      break;

    case type_string:
      Lexer::dequote(_string);
      switch (new_type) {
        case type_boolean:
          _bool = (_string.length() == 0 || _string == "0" || _string == "0.0") ? false : true;
          break;
        case type_integer:
          _integer =
              (long long)strtol(_string.c_str(), nullptr, (_string.substr(0, 2) == "0x" ? 16 : 10));
          break;
        case type_real:
          _real = strtod(_string.c_str(), nullptr);
          break;
        case type_string:
          break;
        case type_date: {
          _date = 0;
          Datetime iso;
          std::string::size_type pos = 0;
          if (iso.parse(_string, pos, dateFormat) && pos == _string.length()) {
            _date = iso.toEpoch();
            break;
          }

          pos = 0;
          Duration isop;
          if (isop.parse(_string, pos) && pos == _string.length()) {
            _date = Datetime().toEpoch() + isop.toTime_t();
            break;
          }

          if (dateFormat != "") {
            _date = Datetime(_string, dateFormat).toEpoch();
            break;
          }
        } break;
        case type_duration: {
          _duration = 0;
          std::string::size_type pos = 0;
          Duration iso;
          if (iso.parse(_string, pos) && pos == _string.length()) {
            _duration = iso.toTime_t();
          }
        } break;
      }
      break;

    case type_date:
      switch (new_type) {
        case type_boolean:
          _bool = _date != 0 ? true : false;
          break;
        case type_integer:
          _integer = (long long)_date;
          break;
        case type_real:
          _real = static_cast<double>(_date);
          break;
        case type_string:
          _string = (std::string) * this;
          break;
        case type_date:
          break;
        // TODO: Not exactly correct (should duration convert into date?), but
        // currently needed for symmetry, which is assumed by operators.
        case type_duration:
          _duration = _date - time(nullptr);
          break;
      }
      break;

    case type_duration:
      switch (new_type) {
        case type_boolean:
          _bool = _duration != 0 ? true : false;
          break;
        case type_integer:
          _integer = (long long)_duration;
          break;
        case type_real:
          _real = static_cast<double>(_duration);
          break;
        case type_string:
          _string = (std::string) * this;
          break;
        case type_date:
          _date = time(nullptr) + _duration;
          break;
        case type_duration:
          break;
      }
      break;
  }

  _type = new_type;
}

////////////////////////////////////////////////////////////////////////////////
int Variant::type() { return _type; }

////////////////////////////////////////////////////////////////////////////////
bool Variant::trivial() const {
  return (_type == type_integer && _integer == 0) || (_type == type_real && _real == 0.0) ||
         (_type == type_string && _string == "") || (_type == type_date && _date == 0) ||
         (_type == type_duration && _duration == 0);
}

////////////////////////////////////////////////////////////////////////////////
bool Variant::get_bool() const { return _bool; }

////////////////////////////////////////////////////////////////////////////////
long long Variant::get_integer() const { return _integer; }

////////////////////////////////////////////////////////////////////////////////
double Variant::get_real() const { return _real; }

////////////////////////////////////////////////////////////////////////////////
const std::string& Variant::get_string() const { return _string; }

////////////////////////////////////////////////////////////////////////////////
time_t Variant::get_date() const { return _date; }

////////////////////////////////////////////////////////////////////////////////
time_t Variant::get_duration() const { return _duration; }

////////////////////////////////////////////////////////////////////////////////
