#ifndef VALUE_READER_H
#define VALUE_READER_H

#include <map>
#include "protocol.h"
#include "structs.h"

class ValueReader {
private:
	std::map<std::string, std::string> * values_;
	message * protocolMessage_;
public:
	ValueReader(message * protocolMessage) {
		this->values_ = &(protocolMessage->getValues());
		protocolMessage_ = protocolMessage;
	}

	static int string2int(const std::string & value) {
		std::istringstream is(value);

		int num;
		is >> num;
		return num;
	}

	static unsigned int string2uint(const std::string & value) {
		std::istringstream is(value);

		unsigned int num;
		is >> num;
		return num;
	}

	static unsigned long string2ulong(const std::string & value) {
		std::istringstream is(value);

		unsigned long num;
		is >> num;
		return num;
	}

	static unsigned long long string2ulonglong(const std::string & value) {
		std::istringstream is(value);

		unsigned long long num;
		is >> num;
		return num;
	}

	static long long string2longlong(const std::string & value) {
		std::istringstream is(value);

		long long num;
		is >> num;
		return num;
	}

	static float string2float(const std::string & value) {
		std::istringstream is(value);

		float num;
		is >> num;
		return num;
	}

	static long string2long(const std::string & value) {
		std::istringstream is(value);

		long num;
		is >> num;
		return num;
	}

	static double string2double(const std::string & value) {
		std::istringstream is(value);

		double num;
		is >> num;
		return num;
	}

	static char string2char(const std::string & value) {
		std::istringstream is(value);

		char num;
		is >> num;
		return num;
	}

	static speed_t string2speed(const std::string & value) {
		std::istringstream is(value);

		speed_t num;
		is >> num;
		return num;
	}

	static course_t string2course(const std::string & value) {
		std::istringstream is(value);

		course_t num;
		is >> num;
		return num;
	}

	static weight_t string2weight(const std::string & value) {
		std::istringstream is(value);

		weight_t num;
		is >> num;
		return num;
	}

	static dt_distance_t string2distance(const std::string & value) {
		std::istringstream is(value);

		dt_distance_t num;
		is >> num;
		return num;
	}

	static position_t string2position(const std::string & value) {
		std::istringstream is(value);

		position_t num;
		is >> num;
		return num;
	}

	static bool string2bool(const std::string & value) {
		std::istringstream is(value);

		int num;
		is >> num;

		if (num == 1) {
			return true;
		}
		else {
			return false;
		}
	}

	static id_t string2id(const std::string & value) {
		std::istringstream is(value);

		id_t num;
		is >> num;
		return num;
	}

	static const std::string long2string(const long value) {
		std::stringstream ss;
		ss << value;
		return ss.str();
	}

	const std::string getMapValue(const std::string & value) const {
		const std::map<std::string, std::string>::const_iterator cit = values_->find(value);

		if (cit != values_->end()) {
			return (*cit).second;
		}
		else {
			std::string error =  "Value " + value + " not found in message " + protocolMessage_->getRequestName();					
			throw std::exception(error.c_str());
		}
	}

	const std::string getString(const std::string & value) const {
		try {
			return getMapValue(value);		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return "";
		}
	}

	const std::string getString(const std::string & value, int index) const {
		return getString(create_param_name(value, index));
	}

	const std::string getString(const std::string & value, int topindex, int subindex) const {
		return getString(create_param_name(value, topindex, subindex));
	}

	const float getFloat(const std::string & value) const {
		try {
			return string2float(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const float getFloat(const std::string & value, int index) const {
		return getFloat(create_param_name(value, index));
	}

	const int getInt(const std::string & value) const {
		try {
			return string2int(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const int getInt(const std::string & value, int index) const {
		return getInt(create_param_name(value, index));
	}

	const unsigned long getULong(const std::string & value) const {
		try {
			return string2ulong(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const unsigned long getULong(const std::string & value, int index) const {
		return getULong(create_param_name(value, index));
	}

	const unsigned long long getULongLong(const std::string & value) const {
		try {
			return string2ulonglong(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const unsigned long long getULongLong(const std::string & value, int index) const {
		return getULongLong(create_param_name(value, index));
	}

	const char getChar(const std::string & value) const {
		try {
			return string2char(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const char getChar(const std::string & value, int index) const {
		return getChar(create_param_name(value, index));
	}

	const bool getBool(const std::string & value) const {
		try {
			return string2bool(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const bool getBool(const std::string & value, int index) const {
		return getBool(create_param_name(value, index));
	}

	const unsigned int getUInt(const std::string & value) const {
		try {
			return string2uint(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const unsigned int getUInt(const std::string & value, int index) const {
		return getUInt(create_param_name(value, index));
	}

	const long long getLongLong(const std::string & value) const {
		try {
			return string2longlong(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const long long getLongLong(const std::string & value, int index) const {
		return getLongLong(create_param_name(value, index));
	}

	const double getDouble(const std::string & value) const {
		try {
			return string2double(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const double getDouble(const std::string & value, int index) const {
		return getDouble(create_param_name(value, index));
	}

	const dt_distance_t getDistance(const std::string & value) const {
		try {
			return string2distance(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const dt_distance_t getDistance(const std::string & value, int index) const {
		return getDistance(create_param_name(value, index));
	}

	const id_t getId(const std::string & value) const {
		try {
			return string2id(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const id_t getId(const std::string & value, int index) const {
		return getId(create_param_name(value, index));
	}

	const weight_t getWeight(const std::string & value) const {
		try {
			return string2weight(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const weight_t getWeight(const std::string & value, int index) const {
		return getWeight(create_param_name(value, index));
	}

	const long getLong(const std::string & value) const {
		try {
			return string2long(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const long getLong(const std::string & value, int index) const {
		return getLong(create_param_name(value, index));
	}

	const position_t getPosition(const std::string & value) const {
		try {
			return string2position(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const position_t getPosition(const std::string & value, int index) const {
		return getPosition(create_param_name(value, index));
	}

	const speed_t getSpeed(const std::string & value) const {
		try {
			return string2speed(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const speed_t getSpeed(const std::string & value, int index) const {
		return getSpeed(create_param_name(value, index));
	}

	const course_t getCourse(const std::string & value) const {
		try {
			return string2course(getMapValue(value));		
		}
		catch (const std::exception & e) {
			KODIAK_WARNING << e.what();
			return 0;
		}
	}

	const course_t getCourse(const std::string & value, int index) const {
		return getCourse(create_param_name(value, index));
	}

	bool isAnswerOk() {
		return protocolMessage_->getAnswerKind() == AK_OK;
	}

private:
	static ETradingResult string2ETradingResult(const std::string & tradeResult) 
	{
		if (tradeResult == PROTOCOL_TRADE_RESULT_DONE) {
			return TR_DONE;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_FIRST_FORTRESS_WEIGHT_LIMIT) {
			return TR_FIRST_FORTRESS_WEIGHT_LIMIT;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_SECOND_FORTRESS_WEIGHT_LIMIT) {
			return TR_SECOND_FORTRESS_WEIGHT_LIMIT;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_FIRST_FORTRESS_CAPACITY_LIMIT) {
			return TR_FIRST_FORTRESS_CAPACITY_LIMIT;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_SECOND_FORTRESS_CAPACITY_LIMIT) {
			return TR_SECOND_FORTRESS_CAPACITY_LIMIT;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_FIRST_FORTRESS_NOT_STORING_ITEMS) {
			return TR_FIRST_FORTRESS_NOT_STORING_ITEMS;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_SECOND_FORTRESS_NOT_STORING_ITEMS) {
			return TR_SECOND_FORTRESS_NOT_STORING_ITEMS;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_FIRST_FORTRESS_MAGNETICAL_GRIDS_LACK) {
			return TR_FIRST_FORTRESS_MAGNETICAL_GRIDS_LACK;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_SECOND_FORTRESS_MAGNETICAL_GRIDS_LACK) {
			return TR_SECOND_FORTRESS_MAGNETICAL_GRIDS_LACK;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_FIRST_FORTRESS_TRANSPORT_CONTAINERS_LACK) {
			return TR_FIRST_FORTRESS_TRANSPORT_CONTAINERS_LACK;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_SECOND_FORTRESS_TRANSPORT_CONTAINERS_LACK) {
			return TR_SECOND_FORTRESS_TRANSPORT_CONTAINERS_LACK;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_FIRST_FORTRESS_ENERGETIC_CELLS_LACK) {
			return TR_FIRST_FORTRESS_ENERGETIC_CELLS_LACK;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_SECOND_FORTRESS_ENERGETIC_CELLS_LACK) {
			return TR_SECOND_FORTRESS_ENERGETIC_CELLS_LACK;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_NOT_IN_RANGE) {
			return TR_NOT_IN_RANGE;
		}
		else if (tradeResult == PROTOCOL_TRADE_RESULT_UNKNOWN_FAIL) {
			return TR_UNKNOWN_FAIL;
		}
		else {
			return TR_UNKNOWN_MAPPING;
		}
	}

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

	static ETradingCommitRole string2ETradingCommitRole(const std::string & role) 
	{
		if (role == PROTOCOL_TRADE_COMMIT_ROLE_HOST) {
			return TRADE_COMMIT_ROLE_HOST;
		}
		else if (role == PROTOCOL_TRADE_COMMIT_ROLE_INVITED) {
			return TRADE_COMMIT_ROLE_INVITED;
		}
		else if (role == PROTOCOL_TRADE_COMMIT_ROLE_NONE) {
			return TRADE_COMMIT_ROLE_NONE;
		}
		else {
			return TRADE_COMMIT_ROLE_UNKNOWN_MAPPING;
		}
	}
public:
	const ETradingResult getTradingResult(const std::string & value) const {
		return string2ETradingResult(getMapValue(value));
	}

	const ETradingResult getTradingResult(const std::string & value, int index) const {		
		return getTradingResult(create_param_name(value, index));
	}

	const ETradingCommitRole getTradingCommitRole(const std::string & value) const {
		return string2ETradingCommitRole(getMapValue(value));
	}

	const ETradingCommitRole getTradingCommitRole(const std::string & value, int index) const {		
		return getTradingCommitRole(create_param_name(value, index));
	}

	const game_resource_type getGameResourceType(const std::string & value) const {
		return stringToResourceType(getMapValue(value));
	}

	const game_resource_type getGameResourceType(const std::string & value, int index) const {		
		return getGameResourceType(create_param_name(value, index));
	}
};

#endif