#pragma once

using namespace System;
using namespace System::Threading;
using namespace System::Runtime::InteropServices;



namespace Bitvise {
namespace FlowSshNet {


// Helper - private class

private ref class Helper abstract sealed
{
internal:
	static inline array<Byte>^ UnsignedCharsToByteArray(unsigned char const* dataPtr, unsigned int dataSize)
	{
		if (!dataPtr || !dataSize || dataSize > INT_MAX) return nullptr;

		array<Byte>^ dataNet = gcnew array<Byte>((int)dataSize);
		Marshal::Copy((IntPtr)const_cast<unsigned char*>(dataPtr), dataNet, 0, (int)dataSize);
		return dataNet;
	}

	static inline DateTime UnixTimeToDateTime(unsigned __int64 secondsSinceUnixEpoch, unsigned int nanoSeconds)
	{
		int miliSeconds = (int)((nanoSeconds + 500000) / 1000000);
		return DateTime(1970,1,1,0,0,0, DateTimeKind::Utc) + TimeSpan(0, 0, 0, (Int32)secondsSinceUnixEpoch, miliSeconds);
	}

	static inline void DateTimeToUnixTime(DateTime% date, [Out] unsigned __int64% secondsSinceUnixEpoch, [Out] unsigned int% nanoSeconds)
	{
		DateTime dateUtc = DateTime::SpecifyKind(date, DateTimeKind::Utc);
		TimeSpan diff	 = dateUtc - DateTime(1970, 1, 1, 0, 0, 0, 0);

		secondsSinceUnixEpoch = (unsigned __int64)diff.TotalSeconds;
		nanoSeconds			  = (unsigned int)diff.Milliseconds * 1000000;
	}

	static inline void ThrowDisposed(Object^ pObject)
	{
		throw gcnew ObjectDisposedException(pObject->GetType()->ToString());
	}

	static inline void NetStringToWStr(String^ from, std::wstring& to)
	{
		if (from == nullptr || from->Length < 0) { to = L""; return; }

		pin_ptr<wchar_t const> fromNet = PtrToStringChars(from);
		to.assign(fromNet, (unsigned int)from->Length);
	}

	static inline String^ WStrToNetStr(std::wstring const& from) { return gcnew String(from.c_str()); }
	static inline String^ WCharToNetStr(wchar_t const* from)	 { return gcnew String(from); }
	static inline String^ CharToNetStr(char const* from) 		 { return gcnew String(from); }
};


// Lock
//
// Important: gcroot also keeps the object alive!
//
//
// As creating/destructing gcroot takes some time, Lock may
// eventually be implemented as a managed "ref class". It
// would be used the same way and should have the same effect.
// Pending as some more testing must be performed though.
//
//	private ref class Lock
//	{
//	public:
//		Lock(Object^ pObject) : m_pObject(pObject)
//		{
//			if (pObject == nullptr)
//				throw gcnew NullReferenceException("Lock(pObject)");
//			Monitor::Enter(m_pObject);
//		}
//		~Lock() { Monitor::Exit(m_pObject); m_pObject = nullptr; } // Dispose **
//		// ** called once the locally declared object goes out of scope
//	private:
//		Object^ m_pObject;
//	};
//

class Lock
{
public:
	Lock(Object^ pObject) : m_pObject(pObject)
	{
		if (pObject == nullptr)
			throw gcnew NullReferenceException("Lock(pObject)");
		Monitor::Enter(m_pObject);
	}
	~Lock() { Monitor::Exit(m_pObject); }

private:
	gcroot<Object^> m_pObject;
};


// bvgcroot
//
// Implementation similar to gcroot.h with added
// weak reference support and ReleaseTarget.

template <class T, BOOL bNormal> struct _bvgcroot
{
#define __GCHANDLE_TO_VOIDPTR(x) ((GCHandle::operator System::IntPtr(x)).ToPointer())
#define __VOIDPTR_TO_GCHANDLE(x) (GCHandle::operator GCHandle(System::IntPtr(x)))

	_bvgcroot()    { AllocHandle(nullptr); }
	_bvgcroot(T t) { AllocHandle(t); }
	~_bvgcroot()   { GCHandle g = __VOIDPTR_TO_GCHANDLE(_handle); g.Free();	_handle = 0; }

	T ReleaseTarget() {
		GCHandle gchandle = __VOIDPTR_TO_GCHANDLE(_handle);
		T t = static_cast<T>(gchandle.Target); gchandle.Target = nullptr; return t;
	}

	operator T () const  { return static_cast<T>(__VOIDPTR_TO_GCHANDLE(_handle).Target); }
	T operator->() const { return static_cast<T>(__VOIDPTR_TO_GCHANDLE(_handle).Target); }
	_bvgcroot& operator=(T t) { __VOIDPTR_TO_GCHANDLE(_handle).Target = t; return *this; }

private:
	GCHandleType GetHandleType() { if(bNormal) return GCHandleType::Normal; return GCHandleType::Weak; }
	void AllocHandle(T t) { _handle = __GCHANDLE_TO_VOIDPTR(GCHandle::Alloc(t, GetHandleType())); }

	void* _handle;
	T* operator& ();
};

template<class T> struct bvgcroot
{
	typedef _bvgcroot<T, true>  normal;
	typedef _bvgcroot<T, false> weak;
};


// AutoKeepAlive

ref class ClientBase;
class AutoKeepAlive
{
public:
	AutoKeepAlive(ClientBase^ obj) : m_obj(obj) {}
	~AutoKeepAlive() {}

private:
	gcroot<ClientBase^> m_obj;
};


} // namespace FlowSshNet
} // namespace Bitvise