/*
    PIP - Platform Independent Primitives
    Variant types
    Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "pivarianttypes.h"

#include "colors_p.h"
#include "pipropertystorage.h"
#ifndef MICRO_PIP
#  include "piiodevice.h"
#endif


int PIVariantTypes::Enum::selectedValue() const {
	for (const auto & e: enum_list)
		if (e.name == selected) return e.value;
	return 0;
}


bool PIVariantTypes::Enum::selectValue(int v) {
	for (const auto & e: enum_list)
		if (e.value == v) {
			selected = e.name;
			return true;
		}
	return false;
}


bool PIVariantTypes::Enum::selectName(const PIString & n) {
	for (const auto & e: enum_list)
		if (e.name == n) {
			selected = e.name;
			return true;
		}
	return false;
}


int PIVariantTypes::Enum::value(const PIString & n) const {
	for (const auto & e: enum_list)
		if (e.name == n) return e.value;
	return 0;
}


PIString PIVariantTypes::Enum::name(int v) const {
	for (const auto & e: enum_list)
		if (e.value == v) return e.name;
	return PIString();
}


PIVector<int> PIVariantTypes::Enum::values() const {
	PIVector<int> ret;
	for (const auto & e: enum_list)
		ret << e.value;
	return ret;
}


PIStringList PIVariantTypes::Enum::names() const {
	PIStringList ret;
	for (const auto & e: enum_list)
		ret << e.name;
	return ret;
}


PIVariantTypes::IODevice::IODevice() {
#ifndef MICRO_PIP
	mode = PIIODevice::ReadWrite;
#else
	mode = 0; // TODO: PIIODevice for MICRO PIP
#endif // MICRO_PIP
	options = 0;
}


void PIVariantTypes::IODevice::set(const PIPropertyStorage & ps) {
	props.clear();
	props << ps;
}


PIPropertyStorage PIVariantTypes::IODevice::get() const {
	PIPropertyStorage ret;
	PIByteArray ba = props;
	if (!ba.isEmpty()) ba >> ret;
	return ret;
}


PIString PIVariantTypes::IODevice::toPICout() const {
	PIString s;
	s += "IODevice(" + prefix + ", mode=";
	int rwc = 0;
	if (mode & 1) {
		s += "r";
		++rwc;
	}
	if (mode & 2) {
		s += "w";
		++rwc;
	}
	if (rwc == 1) s += "o";
	s += ", flags=";
#ifndef MICRO_PIP // TODO: PIIODevice for MICRO PIP
	if (options != 0) {
		if (((PIIODevice::DeviceOptions)options)[PIIODevice::BlockingRead]) s += " br";
		if (((PIIODevice::DeviceOptions)options)[PIIODevice::BlockingWrite]) s += " bw";
	}
#endif // MICRO_PIP
	PIPropertyStorage ps = get();
	for (const auto & p: ps) {
		s += ", " + p.name + "=\"" + p.value.toString() + "\"";
	}
	s += ")";
	return s;
}


PIVariantTypes::Enum & PIVariantTypes::Enum::operator<<(const PIVariantTypes::Enumerator & v) {
	enum_list << v;
	return *this;
}


PIString PIVariantTypes::Enum::toString() {
	static PIString masks = "\"";
	PIString ret;
	if (enum_name.isNotEmpty()) ret += "\"" + enum_name.masked(masks) + "\":";
	ret += "(";
	bool comma = false;
	for (const auto & i: enum_list) {
		if (comma) ret += ';';
		comma = true;
		ret += "\"" + i.name.masked(masks) + "\":";
		ret += PIString::fromNumber(i.value);
	}
	ret += "):" + PIString::fromNumber(selectedValue());
	return ret;
}


PIVariantTypes::Enum PIVariantTypes::Enum::fromString(PIString str) {
	static PIString masks = "\"";
	Enum ret;
	str.trim();
	if (str.size() < 3) return ret;
	if (!str.startsWith("(")) {
		ret.enum_name = str.takeRange('"', '"').unmask(masks);
		str.trim();
		if (str.startsWith(":")) str.cutLeft(1).trim();
	}
	PIString el = str.takeRange('(', ')');
	int ps      = el.size_s();
	while (el.isNotEmpty()) {
		Enumerator e;
		e.name = el.takeRange('"', '"').unmask(masks);
		if (e.name.isEmpty()) break;
		el.trim();
		if (el.startsWith(":")) el.cutLeft(1).trim();
		e.value = el.takeInteger().toInt();
		el.trim();
		if (el.startsWith(";")) el.cutLeft(1).trim();
		ret.enum_list << e;
		if (ps == el.size_s()) break;
		ps = el.size_s();
	}
	str.trim();
	if (str.startsWith(":")) str.cutLeft(1).trim();
	ret.selectValue(str.toInt());
	return ret;
}


PIVariantTypes::Enum & PIVariantTypes::Enum::operator<<(const PIString & v) {
	if (enum_list.isEmpty()) {
		enum_list << Enumerator(0, v);
	} else {
		enum_list << Enumerator(enum_list.back().value + 1, v);
	}
	return *this;
}


PIVariantTypes::Enum & PIVariantTypes::Enum::operator<<(const PIStringList & v) {
	for (const auto & s: v)
		(*this) << s;
	return *this;
}


PIVariantTypes::Color PIVariantTypes::Color::fromString(const PIString & str) {
	if (str.size_s() < 2) return Color();
	PIString cn = str.toLowerCase().trim();
	if (cn.startsWith('#')) return Color(str.mid(1).toUInt(16));
	if (cn.startsWith("0x")) return Color(str.mid(2).toUInt(16));
	return PIColorCollection::instance().getCSSColor(cn);
}


PIString PIVariantTypes::Color::toString() const {
	return "#" + PIString::fromNumber(rgba, 16);
}


PIString PIVariantTypes::Color::toName() const {
	PIString ret = PIColorCollection::instance().getCSSName(*this);
	if (ret.isNotEmpty()) return ret;
	return toString();
}
