// Draft of tests for exception handling.
// Outdated: The integer constant exceptions need to be replaced with virtual
// exceptions for the new system.

// ERROR: exceptions do not interact with ^?{} properly.

#include <stdio.h>
#include <stdbool.h>

#include "except-mac.h"
TRIVIAL_EXCEPTION(yin)
TRIVIAL_EXCEPTION(yang)
TRIVIAL_EXCEPTION(zen)


// Local type to mark exits from scopes. (see ERROR)
struct signal_exit {
	const char * area;
};

void ?{}(signal_exit * this, const char * area) {
	this->area = area;
}

void ^?{}(signal_exit * this) {
	printf("Exiting: %s\n", this->area);
//	sout | "Exiting:" | this->area | endl;
}


// Mark throws: make sure to only pass in exception types.
forall(dtype T)
void terminate(T * except_value) {
	signal_exit a = {"terminate function"};
	THROW(except_value);
	printf("terminate returned\n");
}

forall(dtype T)
void resume(T * except_value) {
	signal_exit a = {"resume function"};
	THROW_RESUME(except_value);
	printf("resume returned\n");
}

// Termination Test: Two handlers: no catch, catch
void bar() {
	signal_exit a = {"bar function"};
	try {
		terminate(&(zen){});
	} catch (yin * error) {
		printf("bar caught exception yin.\n");
	}
}

void foo() {
	signal_exit a = {"foo function"};
	try {
		bar();
	} catch (yang * error) {
		printf("foo caught exception yang.\n");
	} catch (zen * error) {
		printf("foo caught exception zen.\n");
	}
}

// Resumption Two Handler Test: no catch, catch.
void beta() {
	signal_exit a = {"beta function"};
	try {
		zen x;
		resume(&x);
	} catchResume (yin * error) {
		printf("beta caught exception yin\n");
	}
}

void alpha() {
	signal_exit a = {"alpha function"};
	try {
		beta();
	} catchResume (yang * error) {
		printf("alpha caught exception yang\n");
	} catchResume (zen * error) {
		printf("alpha caught exception zen\n");
	}
}

// Finally Test:
void farewell(bool jump) {
	try {
		if (jump) {
			printf("jump out of farewell\n");
			goto endoffunction;
		} else {
			printf("walk out of farewell\n");
		}
	} finally {
		printf("See you next time\n");
	}
	endoffunction:
	printf("leaving farewell\n");
}

// Resume-to-Terminate Test:
void fallback() {
	try {
		zen x;
		resume(&x);
	} catch (zen * error) {
		printf("fallback caught termination zen\n");
	}
}

// Terminate Throw New Exception:
void terminate_swap() {
	signal_exit a = {"terminate_swap"};
	try {
		yin x;
		terminate(&x);
	} catch (yin * error) {
		yang y;
		terminate(&y);
	}
}

void terminate_swapped() {
	signal_exit a = {"terminate_swapped"};
	try {
		terminate_swap();
	} catch (yang * error) {
		printf("terminate_swapped caught exception yang\n");
	}
}

// Resume Throw New Exception:
void resume_swap() {
	signal_exit a = {"resume_swap"};
	try {
		yin x;
		resume(&x);
	} catchResume (yin * error) {
		yang y;
		resume(&y);
	}
}

void resume_swapped() {
	try {
		resume_swap();
	} catchResume (yang * error) {
		printf("resume_swapped caught exception yang\n");
	}
}

// Terminate Rethrow:
void reterminate() {
	try {
		try {
			zen x;
			terminate(&x);
		} catch (zen * error) {
			printf("reterminate zen caught and "
			       "will rethrow exception zen\n");
			throw;
		}
	} catch (zen * error) {
		printf("reterminate 1 caught exception zen\n");
	}
}

// Resume Rethrow:
void reresume() {
	try {
		try {
			zen x;
			resume(&x);
		} catchResume (zen * error) {
			printf("reresume zen caught and rethrows exception zen\n");
			throwResume;
		}
	} catchResume (zen * error) {
		printf("reresume 1 caught exception zen\n");
	}
}

// Terminate-Resume interaction:
void fum() {
	// terminate block, call resume
	try {
		zen x;
		resume(&x);
	} catch (zen * error) {
		printf("fum caught exception zen\n");
	}
}

void foe() {
	// resume block, call terminate
	try {
		zen y;
		terminate(&y);
	} catchResume (zen * error) {
		printf("foe caught exception zen\n");
	}
}

void fy() {
	// terminate block calls fum, call foe
	try {
		foe();
	} catch (zen * error) {
		printf("fy caught exception zen\n");
		fum();
	}
}

void fee() {
	// resume block, call fy
	try {
		fy();
	} catchResume (zen * error) {
		printf("fee caught exception zen\n");
	}
}


// main: choose which tests to run
int main(int argc, char * argv[]) {
	signal_exit a = {"main function"};

	foo(); printf("\n");
	alpha(); printf("\n");
	farewell(false); printf("\n");
	farewell(true); printf("\n");
	fallback(); printf("\n");
	terminate_swapped(); printf("\n");
	resume_swapped(); printf("\n");
	reterminate(); printf("\n");
	reresume(); printf("\n");
	fee(); printf("\n");

	// Uncaught termination test.
	printf("Throw uncaught.\n");
	yang z;
	terminate(&z);
}
