#pragma once

#include "nlc_errorcode.hpp"

#include <exception>
#include <string>

namespace nlc {

/**
* @brief Parent class of the Nanolib exceptions.
*/
class NanotecException: public std::exception {
public:
	explicit NanotecException(std::string const &msg, NlcErrorCode errCode, uint32_t exErrCode) : message(msg), errorCode(errCode), exErrorCode(exErrCode) {
	}

	virtual char const *what() const noexcept {
		return message.c_str();
	}

	NlcErrorCode getErrorCode() const {
		return errorCode;
	}

	uint32_t getExErrorCode() const {
		return exErrorCode;
	}

private:
	std::string message;
	NlcErrorCode errorCode;
	uint32_t exErrorCode;
};

/**
 * @brief Thrown in case of a canceled operation.
 */
class AbortException : public NanotecException {
public:
	explicit AbortException(std::string const &message, uint32_t abortCode)
		: NanotecException(message, NlcErrorCode::CommunicationError, abortCode) {
	}
};

/**
 * @brief Thrown when the Object dictionary address is wrong
 *
 */
class InvalidAddressException : public NanotecException {
public:
	explicit InvalidAddressException(std::string const &message)
		: NanotecException(message, NlcErrorCode::InvalidOdAddress, 0) {
	}
};

/**
 * @brief  Thrown in case of error or unexpected messages from the counterpart (for instance the
 * firmware), or when preconditions are not fulfilled
 */
class ProtocolException : public NanotecException {
public:
	explicit ProtocolException(std::string const &message)
		: NanotecException(message, NlcErrorCode::ProtocolError, 0) {
	}
};

/**
 * @brief  Thrown when an error with required local resources occur. For instance, in case of errors
 * with a CAN adapter.
 *
 */
class ResourceException : public NanotecException {
public:
	explicit ResourceException(std::string const &message)
		: NanotecException(message, NlcErrorCode::IoError, 0) {
	}

};

/**
 * @brief  Thrown when an certain period is exceeded
 *
 */
class TimeoutException : public NanotecException {
public:
	explicit TimeoutException(std::string const &message)
		: NanotecException(message, NlcErrorCode::TimeoutError, 0) {
	}

};

/**
 * @brief  Thrown when an unknown/unusable argument was given to a method
 *
 */
class InvalidArgumentException : public NanotecException {
public:
	explicit InvalidArgumentException(std::string const &message)
		: NanotecException(message, NlcErrorCode::InvalidArguments, 0) {
	}
};

/**
 * @brief  Thrown when an impossible processing state was reached
 *
 */
class LogicException : public NanotecException {
public:
	explicit LogicException(std::string const &message) : NanotecException(message, NlcErrorCode::GeneralError, 0) {
	}
};

} // namespace nlc
