#ifndef __KERNEL_THREAD_H
#define __KERNEL_THREAD_H

#include <sys/types.h>
#include <arch/Thread.h>
#include <kernel/KObject.h>
#include <kernel/Scheduler.h>
#include <kernel/Spinlock.h>
#include <List.h>

namespace OS {
namespace Kernel {

struct ArchThreadState;
class Dispatcher;

class Thread : public KObject {
  public:
	Thread(void *container, void (*startRoutine)(unsigned long), unsigned long arg, const char *name, int priority = Scheduler::kDefaultPriority);
	virtual ~Thread();

	// only valid to set custom stack before calling ::Run
	status_t SetCustomStack(vaddr_t base, size_t len);

	// start and exit thread
	status_t Run(bool reschedule = true);
	static void ExitCurrent();
	void Kill();

	// some thread accessors
	int GetPriority() const { return mPriority; }
	int SetPriority(int priority);
	vaddr_t GetStackBase() const { return mStackBase; }
	vaddr_t GetStackTop() const { return mStackBase + mStackSize; }
	size_t GetStackSize() const { return mStackSize; }
	void *GetContainer() const { return mContainer; }

	// get and test against the current thread
	static Thread *GetCurrent() { return sCurrent; }
	static kobj_id GetCurrentId() { return sCurrent->Id(); }
	bool IsCurrentThread() const { return GetCurrent() == this; }

	// look up a thread from it's id
	static Thread *LookupThread(kobj_id id);

	// thread state
	enum ThreadState {
		INITIAL = 0,
		READY,
		RUNNING,
		BLOCKED,
		SUSPENDED,
		DEAD
	};

	// debug
	void Dump();

  private:
	// arch specific code fills in the following
	void ArchContextSwitchTo(Thread *from);
	void ArchInitialize();
	void ArchInitializeStack();

	// initial routine to run at thread creation
	static void ThreadStartRoutine(Thread *t);

	// preemption disable and enable
	// can only be called on the current thread
	void DisablePreemption();
	void RestorePreemption();

	// the callback the dispatcher makes on us when we are dispatched
	bool DispatchSignal();
	status_t DispatchWait(Dispatcher *d, Spinlock &lock);

	// the thread's main queue node
	ListNode<Thread> mQueueNode;

	// thread state
	ThreadState mThreadState;

	// priority
	int mPriority;

	// remaining quantum
	int mRemainingQuantum;

	// stack
	vaddr_t mStackBase;
	size_t mStackSize;

	// starting routine
	void (*mStartRoutine)(unsigned long);
	unsigned long mStartRoutineArg;

	// architecturally dependent state
	ArchThreadState mArchState;

	// preemption disable count
	// > 0 means preemption is disabled (interrupts disabled)
	// = 0 means preemption enable
	// < 0 is not allowed
	int mPreemptionDisable;

	// current Dispatcher we may be blocked on
	Dispatcher *mDispatcher;

	// blocked data
	void *mBlockedData;

	// no copy constructor
	Thread(const Thread &t);
	Thread &operator=(const Thread &t);

	// current thread
	static Thread *sCurrent; // set by the scheduler

	// container (OS::Thread)
	void *mContainer;

	friend class Scheduler; // the scheduler can mess with our contents
	friend class ThreadPreemptRef;
	friend class IRQPreemptRef;
	friend class Dispatcher;
};

// create one of these to acquire and hold a ref to the current thread's preemption counter
class ThreadPreemptRef {
  public:
	ThreadPreemptRef() 
		: mThread(*Thread::GetCurrent()), mHeld(false) { 
		mThread.DisablePreemption(); 
		mHeld = true;
	}
	~ThreadPreemptRef() { 
		Release();
	}

	void Release() {
		if (mHeld)
			mThread.RestorePreemption();
		mHeld = false;
	}

  private:
	Thread &mThread;
	bool mHeld;
};

// create one of these to emulate the preemption counter being incremented and decremented
class IRQPreemptRef {
  public:
	IRQPreemptRef() 
		: mThread(*Thread::GetCurrent()) { 
		mThread.mPreemptionDisable++; 
	}
	~IRQPreemptRef() { 
		mThread.mPreemptionDisable--;
	}

  private:
	Thread &mThread;
};

}} // namespace OS::Kernel

#endif
