/*
 * Copyright (c) 2010-2019 Belledonne Communications SARL.
 *
 * This file is part of Liblinphone.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "linphone/utils/utils.h"

#include "variant.h"

// =============================================================================

using namespace std;

LINPHONE_BEGIN_NAMESPACE

// -----------------------------------------------------------------------------

class VariantPrivate {
public:
	union Value {
		int i;
		unsigned int ui;
		short s;
		unsigned short us;
		long l;
		unsigned long ul;
		long long ll;
		unsigned long long ull;
		char c;
		bool b;
		double d;
		float f;
		string *str;
		void *g;
	};

	~VariantPrivate () {
		deleteString();
	}

	inline int getType () const {
		return type;
	}

	inline void setType (int newType) {
		L_ASSERT(newType >= Variant::Invalid && newType != Variant::MaxDefaultTypes);

		if (newType != Variant::String)
			deleteString();
		else if (type != Variant::String)
			value.str = new string();

		type = newType;
	}

	Value value = {};

private:
	inline void deleteString () {
		if (type == Variant::String)
			delete value.str;
	}

	// Integer, because type can be a custom type.
	int type = Variant::Invalid;
};

// -----------------------------------------------------------------------------

Variant::Variant () {
	// Private can exist. (placement new)
	if (!mPrivate)
		mPrivate = new VariantPrivate();
}

Variant::Variant (Type type) : Variant() {
	L_D();
	d->setType(type);
}

Variant::Variant (const Variant &other) {
	// Don't call placement new.
	L_ASSERT(!mPrivate);
	mPrivate = new VariantPrivate();

	L_D();

	int type = other.getPrivate()->getType();
	d->setType(type);

	const VariantPrivate::Value &value = other.getPrivate()->value;
	if (type == String)
		*d->value.str = *value.str;
	else
		d->value = value;
}

Variant::Variant (Variant &&other) {
	// Don't call placement new.
	L_ASSERT(!mPrivate);
	::swap(mPrivate, other.mPrivate);
}

Variant::Variant (int value) : Variant(Int) {
	L_D();
	d->value.i = value;
}

Variant::Variant (unsigned int value) : Variant(UnsignedInt) {
	L_D();
	d->value.ui = value;
}

Variant::Variant (short value) : Variant(Short) {
	L_D();
	d->value.s = value;
}

Variant::Variant (unsigned short value) : Variant(UnsignedShort) {
	L_D();
	d->value.us = value;
}

Variant::Variant (long value) : Variant(Long) {
	L_D();
	d->value.l = value;
}

Variant::Variant (unsigned long value) : Variant(UnsignedLong) {
	L_D();
	d->value.ul = value;
}

Variant::Variant (long long value) : Variant(LongLong) {
	L_D();
	d->value.ll = value;
}

Variant::Variant (unsigned long long value) : Variant(UnsignedLongLong) {
	L_D();
	d->value.ull = value;
}

Variant::Variant (char value) : Variant(Char) {
	L_D();
	d->value.c = value;
}

Variant::Variant (bool value) : Variant(Bool) {
	L_D();
	d->value.b = value;
}

Variant::Variant (double value) : Variant(Double) {
	L_D();
	d->value.d = value;
}

Variant::Variant (float value) : Variant(Float) {
	L_D();
	d->value.f = value;
}

Variant::Variant (const string &value) : Variant(String) {
	L_D();
	*d->value.str = value;
}

Variant::~Variant () {
	L_D();

	// Note: Private data can be stolen by copy operator (r-value).
	// It can be null, but it useless to test it.
	delete d;
}

Variant &Variant::operator= (const Variant &other) {
	L_D();

	if (this != &other) {
		// Update type.
		int type = other.getPrivate()->getType();
		d->setType(type);

		// Update value.
		const VariantPrivate::Value &value = other.getPrivate()->value;
		if (type == String)
			*d->value.str = *value.str;
		else
			d->value = value;
	}

	return *this;
}

Variant &Variant::operator= (Variant &&other) {
	::swap(mPrivate, other.mPrivate);
	return *this;
}

bool Variant::isValid () const {
	L_D();
	return d->getType() != Invalid;
}

void Variant::clear () {
	L_D();
	d->setType(Invalid);
}

void Variant::swap (const Variant &other) {
	// TODO.
}

// -----------------------------------------------------------------------------
// Number helpers.
// -----------------------------------------------------------------------------

static inline long long getAssumedNumber (const VariantPrivate &p) {
	const int type = p.getType();
	L_ASSERT(type > Variant::Invalid && type < Variant::MaxDefaultTypes);

	switch (static_cast<Variant::Type>(type)) {
		case Variant::Int:
			return p.value.i;
		case Variant::Short:
			return p.value.s;
		case Variant::Long:
			return p.value.l;
		case Variant::LongLong:
			return p.value.ll;
		case Variant::Char:
			return p.value.c;
		case Variant::Double:
			return static_cast<long long>(p.value.d);
		case Variant::Float:
			return static_cast<long long>(p.value.f);

		default:
			L_ASSERT(false);
	}

	return 0;
}

static inline unsigned long long getAssumedUnsignedNumber (const VariantPrivate &p) {
	const int type = p.getType();
	L_ASSERT(type > Variant::Invalid && type < Variant::MaxDefaultTypes);

	switch (static_cast<Variant::Type>(type)) {
		case Variant::UnsignedInt:
			return p.value.ui;
		case Variant::UnsignedShort:
			return p.value.us;
		case Variant::UnsignedLong:
			return p.value.ul;
		case Variant::UnsignedLongLong:
			return p.value.ull;

		default:
			L_ASSERT(false);
	}

	return 0;
}

// -----------------------------------------------------------------------------
// Number conversions.
// -----------------------------------------------------------------------------

static inline long long getValueAsNumber (const VariantPrivate &p, bool *soFarSoGood) {
	const int type = p.getType();
	L_ASSERT(type > Variant::Invalid && type < Variant::MaxDefaultTypes);

	switch (static_cast<Variant::Type>(type)) {
		case Variant::Int:
		case Variant::Short:
		case Variant::Long:
		case Variant::LongLong:
		case Variant::Char:
		case Variant::Double:
		case Variant::Float:
			return getAssumedNumber(p);

		case Variant::UnsignedInt:
		case Variant::UnsignedShort:
		case Variant::UnsignedLong:
		case Variant::UnsignedLongLong:
			return static_cast<long long>(getAssumedUnsignedNumber(p));

		case Variant::Bool:
			return static_cast<long long>(p.value.b);

		case Variant::String:
			return Utils::stoll(*p.value.str);

		case Variant::Generic:
			return static_cast<long long>(!!p.value.g);

		default:
			*soFarSoGood = false;
	}

	return 0;
}

static inline unsigned long long getValueAsUnsignedNumber (const VariantPrivate &p, bool *soFarSoGood) {
	const int type = p.getType();
	L_ASSERT(type > Variant::Invalid && type < Variant::MaxDefaultTypes);

	switch (static_cast<Variant::Type>(type)) {
		case Variant::Int:
		case Variant::Short:
		case Variant::Long:
		case Variant::LongLong:
		case Variant::Char:
		case Variant::Double:
		case Variant::Float:
			return static_cast<unsigned long long>(getAssumedNumber(p));

		case Variant::UnsignedInt:
		case Variant::UnsignedShort:
		case Variant::UnsignedLong:
		case Variant::UnsignedLongLong:
			return getAssumedUnsignedNumber(p);

		case Variant::Bool:
			return static_cast<unsigned long long>(p.value.b);

		case Variant::String:
			return Utils::stoull(*p.value.str);

		case Variant::Generic:
			return static_cast<unsigned long long>(!!p.value.g);

		default:
			*soFarSoGood = false;
	}

	return 0;
}

// -----------------------------------------------------------------------------
// Specific conversions.
// -----------------------------------------------------------------------------

static inline bool getValueAsBool (const VariantPrivate &p, bool *soFarSoGood) {
	const int type = p.getType();
	L_ASSERT(type > Variant::Invalid && type < Variant::MaxDefaultTypes);

	switch (static_cast<Variant::Type>(type)) {
		case Variant::Int:
		case Variant::Short:
		case Variant::Long:
		case Variant::LongLong:
		case Variant::Char:
		case Variant::Double:
		case Variant::Float:
			return!!getAssumedNumber(p);

		case Variant::UnsignedInt:
		case Variant::UnsignedShort:
		case Variant::UnsignedLong:
		case Variant::UnsignedLongLong:
			return !!getAssumedUnsignedNumber(p);

		case Variant::Bool:
			return p.value.b;

		case Variant::String:
			return Utils::stob(*p.value.str);

		case Variant::Generic:
			return !!p.value.g;

		default:
			*soFarSoGood = false;
			break;
	}

	return false;
}

static inline double getValueAsDouble (const VariantPrivate &p, bool *soFarSoGood) {
	const int type = p.getType();
	L_ASSERT(type > Variant::Invalid && type < Variant::MaxDefaultTypes);

	switch (static_cast<Variant::Type>(type)) {
		case Variant::Int:
		case Variant::Short:
		case Variant::Long:
		case Variant::LongLong:
		case Variant::Char:
			return static_cast<double>(getAssumedNumber(p));

		case Variant::UnsignedInt:
		case Variant::UnsignedShort:
		case Variant::UnsignedLong:
		case Variant::UnsignedLongLong:
			return static_cast<double>(getAssumedUnsignedNumber(p));

		case Variant::Float:
			return static_cast<double>(p.value.f);

		case Variant::Double:
			return p.value.d;

		case Variant::Bool:
			return static_cast<double>(p.value.b);

		case Variant::String:
			return Utils::stod(*p.value.str);

		case Variant::Generic:
			return static_cast<double>(!!p.value.g);

		default:
			*soFarSoGood = false;
			break;
	}

	return 0.0;
}

static inline float getValueAsFloat (const VariantPrivate &p, bool *soFarSoGood) {
	const int type = p.getType();
	L_ASSERT(type > Variant::Invalid && type < Variant::MaxDefaultTypes);

	switch (static_cast<Variant::Type>(type)) {
		case Variant::Int:
		case Variant::Short:
		case Variant::Long:
		case Variant::LongLong:
		case Variant::Char:
			return static_cast<float>(getAssumedNumber(p));

		case Variant::UnsignedInt:
		case Variant::UnsignedShort:
		case Variant::UnsignedLong:
		case Variant::UnsignedLongLong:
			return static_cast<float>(getAssumedUnsignedNumber(p));

		case Variant::Float:
			return p.value.f;

		case Variant::Double:
			return static_cast<float>(p.value.d);

		case Variant::Bool:
			return static_cast<float>(p.value.b);

		case Variant::String:
			return Utils::stof(*p.value.str);

		case Variant::Generic:
			return static_cast<float>(!!p.value.g);

		default:
			*soFarSoGood = false;
			break;
	}

	return 0.f;
}

static inline string getValueAsString (const VariantPrivate &p, bool *soFarSoGood) {
	const int type = p.getType();
	L_ASSERT(type > Variant::Invalid && type < Variant::MaxDefaultTypes);

	switch (static_cast<Variant::Type>(type)) {
		case Variant::Int:
		case Variant::Short:
		case Variant::Long:
		case Variant::LongLong:
			return Utils::toString(getAssumedNumber(p));

		case Variant::UnsignedInt:
		case Variant::UnsignedShort:
		case Variant::UnsignedLong:
		case Variant::UnsignedLongLong:
			return Utils::toString(getAssumedUnsignedNumber(p));

		case Variant::Char:
			return string(1, p.value.c);

		case Variant::Bool:
			return string(p.value.b ? "true" : "false");

		case Variant::Double:
			return Utils::toString(p.value.d);

		case Variant::Float:
			return Utils::toString(p.value.f);

		case Variant::String:
			return *p.value.str;

		case Variant::Generic:
			return Utils::toString(p.value.g);

		default:
			*soFarSoGood = false;
	}

	return string();
}

static inline void *getValueAsGeneric (const VariantPrivate &p, bool *soFarSoGood) {
	if (p.getType() == Variant::Generic)
		return p.value.g;

	*soFarSoGood = false;
	return nullptr;
}

// -----------------------------------------------------------------------------

void Variant::getValue (int type, void *value, bool *soFarSoGood) const {
	L_ASSERT(type > Invalid && type != MaxDefaultTypes);

	L_D();

	*soFarSoGood = true;

	if (type > MaxDefaultTypes) {
		*soFarSoGood = false;
		// TODO: Unable to get value. It will be great to support custom types.
		return;
	}

	switch (static_cast<Type>(type)) {
		// Cast as Number.
		case Int:
			*static_cast<int *>(value) = static_cast<int>(getValueAsNumber(*d, soFarSoGood));
			break;
		case Short:
			*static_cast<short *>(value) = static_cast<short>(getValueAsNumber(*d, soFarSoGood));
			break;
		case Long:
			*static_cast<long *>(value) = static_cast<long>(getValueAsNumber(*d, soFarSoGood));
			break;
		case LongLong:
			*static_cast<long long *>(value) = getValueAsNumber(*d, soFarSoGood);
			break;
		case Char:
			*static_cast<char *>(value) = static_cast<char>(getValueAsNumber(*d, soFarSoGood));
			break;

		// Cast as Unsigned number.
		case UnsignedInt:
			*static_cast<unsigned int *>(value) = static_cast<unsigned int>(getValueAsUnsignedNumber(*d, soFarSoGood));
			break;
		case UnsignedShort:
			*static_cast<unsigned short *>(value) = static_cast<unsigned short>(getValueAsUnsignedNumber(*d, soFarSoGood));
			break;
		case UnsignedLong:
			*static_cast<unsigned long *>(value) = static_cast<unsigned long>(getValueAsUnsignedNumber(*d, soFarSoGood));
			break;
		case UnsignedLongLong:
			*static_cast<unsigned long long *>(value) = getValueAsUnsignedNumber(*d, soFarSoGood);
			break;

		// Cast as specific value.
		case Bool:
			*static_cast<bool *>(value) = getValueAsBool(*d, soFarSoGood);
			break;
		case Double:
			*static_cast<double *>(value) = getValueAsDouble(*d, soFarSoGood);
			break;
		case Float:
			*static_cast<float *>(value) = getValueAsFloat(*d, soFarSoGood);
			break;
		case String:
			*static_cast<string *>(value) = getValueAsString(*d, soFarSoGood);
			break;
		case Generic:
			*static_cast<void **>(value) = getValueAsGeneric(*d, soFarSoGood);
			break;

		case Invalid:
		case MaxDefaultTypes:
			*soFarSoGood = false;
			break;
	}
}

Variant Variant::createGeneric (void *value) {
	Variant variant(Generic);
	variant.getPrivate()->value.g = value;
	return variant;
}

LINPHONE_END_NAMESPACE
