#include <string>
#include <iostream>

#include <scpp_os.hpp>
#include <scpp_unittest.hpp>
#include <scpp_thread.hpp>

using namespace scpp;


class TestThreadClassRunnable : public Runnable
{
	public:
		TestThreadClassRunnable(const std::string& str) : msg(str)
		{
		}
		void run()
		{
			Thread::sleep(200);
		}
	private:
		std::string msg;

};

class TestThreadClass : public Thread
{
	public:
		TestThreadClass(const std::string& str) : Thread(), msg(str)
		{
		}
		void run()
		{
			Thread::sleep(200);
		}
	private:
		std::string msg;

};

class TestThreadClassErr : public Thread
{
	public:
		TestThreadClassErr() : Thread()
		{
			// NOP
		}
		void run() {
		}
	protected:
		bool createThread()
		{
			return false;
		}
	private:

};

static int testCounter;
class TestMutex : public Thread
{
	public:
		TestMutex(Mutex* m) : Thread()
		{
			mutex = m;
		}
		void run()
		{
			mutex->lock();
			int cnt = testCounter;
			for (int i = 0; i < 30; i++)
			{
				Thread::sleep(10);
				Assertion::assertEquals(cnt + i, testCounter);
				testCounter++;
			}
			mutex->unlock();
		}
	private:
		Mutex* mutex;
};


/***
 * Thred 単体テスト.
 */
class ThreadTest : public TestCase
{
	public:
		ThreadTest() {}
		~ThreadTest() {}

		void testThread()
		{
			TestThreadClass r1("Thread1");
			TestThreadClass r2("Thread2");
			TestThreadClass r3("Thread3");
			r1.start();
			r2.start();
			r3.start();
			Thread::sleep(100);
			Assertion::assertTrue(r1.isAlive());
			Assertion::assertTrue(r2.isAlive());
			Assertion::assertTrue(r3.isAlive());
			r1.join();
			r2.join();
			r3.join();
			Assertion::assertFalse(r1.isAlive());
			Assertion::assertFalse(r2.isAlive());
			Assertion::assertFalse(r3.isAlive());

			TestThreadClassRunnable rr("Runnable");
			Thread t(&rr);
			t.start();
			Thread::sleep(100);
			Assertion::assertTrue(t.isAlive());
			t.join();
			Assertion::assertFalse(t.isAlive());
		}

		void testThreadCopy()
		{
			TestThreadClass r1("Thread");
			TestThreadClass r2(r1);
			r1.start();
			try
			{
				r1.start();
				Assertion::fail();
			}
			catch (ThreadException& t)
			{
			}
			r1.join();
		}

		void testThreadErr()
		{
			TestThreadClassErr t;
			try
			{
				t.start();
				Assertion::fail();
			}
			catch (ThreadException& t)
			{
			}
		}

		void testThreadException()
		{
			ThreadException t1;
			ThreadException t2(t1);
			ThreadException t3("msg");
			Assertion::assertEquals("msg", t3.what());
			ThreadException* t4;
			t4 = new ThreadException();
			delete t4;
		}

		void testMutex()
		{
			Mutex m;
			TestMutex m1(&m);
			TestMutex m2(&m);
			TestMutex m3(&m);

			m1.start();
			m2.start();
			m3.start();
			m1.join();
			m2.join();
			m3.join();
		}

		void suite()
		{
			RUN_TEST(testThread);
			RUN_TEST(testThreadCopy);
			RUN_TEST(testThreadErr);
			RUN_TEST(testThreadException);
			RUN_TEST(testMutex);
		}


};

