#pragma once
#include "StrBuf.h"
#include "Maybe.h"

namespace storm {
	STORM_PKG(core);

	/**
	 * Checked operations to handle overflow nicely.
	 */


	/**
	 * Checked `Nat`. It is useful to think of this class as a wrapper for the `Nat` datatype that
	 * does not allow over- or underflow. If an over- or underflow happens, then all operations
	 * return an "empty" value. In this way, the represented value is similar to a `Maybe<Nat>`, as
	 * an instance of the value may represent the "empty" type as well. Note: We don't consider
	 * shifting bits out using `>>` as underflow.
	 */
	class CheckedNat {
		STORM_VALUE;
	public:
		// Create zero.
		STORM_CTOR CheckedNat();

		// Create from a normal `Nat`.
		STORM_CAST_CTOR CheckedNat(Nat from);

		// Create from other types. Checks values as needed.
		STORM_CAST_CTOR CheckedNat(Byte from);
		STORM_CAST_CTOR CheckedNat(Int from);
		STORM_CAST_CTOR CheckedNat(Word from);
		STORM_CAST_CTOR CheckedNat(Long from);

		// Get the resulting value.
		Maybe<Nat> STORM_FN nat() const;

		// Check if there is an error.
		Bool STORM_FN error() const;

		// To string.
		void STORM_FN toS(StrBuf *to) const;

		// Operators.
		CheckedNat STORM_FN operator +(CheckedNat other) const;
		CheckedNat STORM_FN operator -(CheckedNat other) const;
		CheckedNat STORM_FN operator *(CheckedNat other) const;
		CheckedNat STORM_FN operator /(CheckedNat other) const;
		CheckedNat STORM_FN operator <<(CheckedNat other) const;
		CheckedNat STORM_FN operator >>(CheckedNat other) const;

	private:
		// Actual value.
		Nat val;

		// Is the value OK?
		Bool ok;

		// Internal ctor.
		CheckedNat(Nat v, Bool ok) : val(v), ok(ok) {}
	};


	/**
	 * Checked `Word`. It is useful to think of this class as a wrapper for the `Word` datatype that
	 * does not allow over- or underflow. If an over- or underflow happens, then all operations
	 * return an "empty" value. In this way, the represented value is similar to a `Maybe<Word>`, as
	 * an instance of the value may represent the "empty" type as well. Note: We don't consider
	 * shifting bits out using `>>` as underflow.
	 */
	class CheckedWord {
		STORM_VALUE;
	public:
		// Create zero.
		STORM_CTOR CheckedWord();

		// Create from a normal `Word`.
		STORM_CAST_CTOR CheckedWord(Word from);

		// Create from other types. Checks values as needed.
		STORM_CAST_CTOR CheckedWord(Byte from);
		STORM_CAST_CTOR CheckedWord(Int from);
		STORM_CAST_CTOR CheckedWord(Nat from);
		STORM_CAST_CTOR CheckedWord(Long from);
		STORM_CAST_CTOR CheckedWord(CheckedNat from);

		// Get the resulting value.
		Maybe<Word> STORM_FN word() const;

		// Check if there is an error.
		Bool STORM_FN error() const;

		// To string.
		void STORM_FN toS(StrBuf *to) const;

		// Operators.
		CheckedWord STORM_FN operator +(CheckedWord other) const;
		CheckedWord STORM_FN operator -(CheckedWord other) const;
		CheckedWord STORM_FN operator *(CheckedWord other) const;
		CheckedWord STORM_FN operator /(CheckedWord other) const;
		CheckedWord STORM_FN operator <<(CheckedWord other) const;
		CheckedWord STORM_FN operator >>(CheckedWord other) const;

	private:
		// Actual value.
		Word val;

		// Is the value OK?
		Bool ok;

		// Internal ctor.
		CheckedWord(Word v, Bool ok) : val(v), ok(ok) {}
	};

}
