/* ================================================================================== */
/* Copyright (c) 1998-1999 3Com Corporation or its subsidiaries. All rights reserved. */
/* ================================================================================== */

#include "EmulatorCommon.h"
#include "Marshal.h"

#include "Byteswapping.h"		// Canonical

// -------------------------
// ----- Binary buffer -----
// -------------------------

void* Marshal::GetBuffer (uaecptr p, long len)
{
	void*	result = NULL;

	if (p)
	{
		result = Platform::AllocateMemory (len);

		if (result)
		{
			uae_memcpy (result, p, len);
		}
	}

	return result;
}

#if 0	// inline
template <class T>
void Marshal::PutBuffer (uaecptr p, T*& buf, long len)
{
	if (p)
	{
		uae_memcpy (p, (void*) buf, len);
		Platform::DisposeMemory (buf);
		buf = NULL;
	}
}
#endif


// ---------------------
// ----- EventType -----
// ---------------------

EventType Marshal::GetEventType (uaecptr p)
{
	EventType	event;
	memset (&event, 0, sizeof (event));

	if (p)
	{
		event.eType		= get_word (p + offsetof (EventType, eType));
		event.penDown	= get_byte (p + offsetof (EventType, penDown));
		event.screenX	= get_word (p + offsetof (EventType, screenX));
		event.screenY	= get_word (p + offsetof (EventType, screenY));

		// Aargh! Need to do all the union structs!
	}

	return event;
}


void Marshal::PutEventType (uaecptr p, EventType& event)
{
	if (p)
	{
		put_word (p + offsetof (EventType, eType),		event.eType);
		put_byte (p + offsetof (EventType, penDown),	event.penDown);
		put_word (p + offsetof (EventType, screenX),	event.screenX);
		put_word (p + offsetof (EventType, screenY),	event.screenY);

		// Aargh! Need to do all the union structs!
	}
}


// -----------------------------
// ----- NetSocketAddrType -----
// -----------------------------

NetSocketAddrType Marshal::GetNetSocketAddrType (uaecptr p)
{
	NetSocketAddrType	netSocketAddr;
	memset (&netSocketAddr, 0, sizeof (netSocketAddr));

	if (p)
	{
		// We should be able to pretty much just block-copy this sucker without
		// having to worry too much about byte-swapping the contents.  A generic
		// address block is a 2-byte family in HBO followed by 14 bytes of data.
		// For Internet addresses, this 14 bytes of data consists of an IP address
		// and a socket in NBO, so no byteswapping is needed.  For Raw addresses,
		// Palm OS has defined the contents to be a 2-byte instance and a 4-byte
		// creator; there's no mention of their byte-ordering, so for now let's
		// assume NBO as well.
		//
		// By doing a blind memcpy of the contents, we relieve ourselves of the
		// responsibity of determining if they're actually initialized (if they're
		// not initialized, the family field may contain garbage, and we may go
		// off in the weeds trying to interpret the rest of the contents).

		switch (get_word (p))
		{
			case netSocketAddrRaw:
				uae_memcpy ((void*) &netSocketAddr, p, sizeof (NetSocketAddrRawType));
				break;

			case netSocketAddrINET:
				uae_memcpy ((void*) &netSocketAddr, p, sizeof (NetSocketAddrINType));
				break;

			default:
				// Do the best we can...
				uae_memcpy ((void*) &netSocketAddr, p, sizeof (NetSocketAddrType));
				break;
		}

		Canonical (netSocketAddr.family);
	}

	return netSocketAddr;
}


void Marshal::PutNetSocketAddrType (uaecptr p, NetSocketAddrType& netSocketAddr)
{
	if (p)
	{
		SWord	family = netSocketAddr.family;

		Canonical (netSocketAddr.family);

		switch (family)
		{
			case netSocketAddrRaw:
				uae_memcpy (p, (void*) &netSocketAddr, sizeof (NetSocketAddrRawType));
				break;

			case netSocketAddrINET:
				uae_memcpy (p, (void*) &netSocketAddr, sizeof (NetSocketAddrINType));
				break;

			default:
				// Do the best we can...
				uae_memcpy (p, (void*) &netSocketAddr, sizeof (NetSocketAddrType));
				break;
		}

		Canonical (netSocketAddr.family);
	}
}


// --------------------------
// ----- NetIOParamType -----
// --------------------------

NetIOParamType Marshal::GetNetIOParamType (uaecptr p)
{
	NetIOParamType	netIOParam;
	memset (&netIOParam, 0, sizeof (netIOParam));

	if (p)
	{
		netIOParam.addrLen			= get_word (p + offsetof (NetIOParamType, addrLen));
		netIOParam.iovLen			= get_word (p + offsetof (NetIOParamType, iovLen));
		netIOParam.accessRightsLen	= get_word (p + offsetof (NetIOParamType, accessRightsLen));

		netIOParam.addrP			= (BytePtr) GetBuffer (p + offsetof (NetIOParamType, addrP), netIOParam.addrLen);
		if (netIOParam.addrP)
			Canonical (((NetSocketAddrType*) (netIOParam.addrP))->family);

		netIOParam.accessRights		= (BytePtr) GetBuffer (p + offsetof (NetIOParamType, accessRights), netIOParam.accessRightsLen);

		netIOParam.iov				= (NetIOVecPtr) Platform::AllocateMemory (netIOParam.iovLen * sizeof (NetIOVecType));
		for (Word ii = 0; ii < netIOParam.iovLen; ++ii)
		{
			netIOParam.iov[ii].bufLen	= get_word (p + offsetof (NetIOParamType, iovLen) + ii * sizeof (NetIOVecType) + offsetof (NetIOVecType, bufLen));
			netIOParam.iov[ii].bufP		= (BytePtr) GetBuffer (p + offsetof (NetIOParamType, iovLen) + ii * sizeof (NetIOVecType) + offsetof (NetIOVecType, bufP), netIOParam.iov[ii].bufLen);
		}
	}

	return netIOParam;
}


void Marshal::PutNetIOParamType (uaecptr p, NetIOParamType& netIOParam)
{
	if (p)
	{
		put_word (p + offsetof (NetIOParamType, addrLen),			netIOParam.addrLen);
		put_word (p + offsetof (NetIOParamType, iovLen),			netIOParam.iovLen);
		put_word (p + offsetof (NetIOParamType, accessRightsLen),	netIOParam.accessRightsLen);

		if (netIOParam.addrP)
			Canonical (((NetSocketAddrType*) (netIOParam.addrP))->family);
		PutBuffer (p + offsetof (NetIOParamType, addrP),			netIOParam.addrP,			netIOParam.addrLen);
		if (netIOParam.addrP)
			Canonical (((NetSocketAddrType*) (netIOParam.addrP))->family);

//		PutBuffer (p + offsetof (NetIOParamType, iov),				netIOParam.iov,				netIOParam.iovLen);
		PutBuffer (p + offsetof (NetIOParamType, accessRights),		netIOParam.accessRights,	netIOParam.accessRightsLen);

		for (Word ii = 0; ii < netIOParam.iovLen; ++ii)
		{
			put_word (p + offsetof (NetIOParamType, iovLen) + ii * sizeof (NetIOVecType) + offsetof (NetIOVecType, bufLen), netIOParam.iov[ii].bufLen);
			PutBuffer (p + offsetof (NetIOParamType, iovLen) + ii * sizeof (NetIOVecType) + offsetof (NetIOVecType, bufP), netIOParam.iov[ii].bufP, netIOParam.iov[ii].bufLen);
		}
		Platform::DisposeMemory (netIOParam.iov);
	}
}


// ------------------------------
// ----- NetHostInfoBufType -----
// ------------------------------

/*
typedef struct {
	NetHostInfoType	hostInfo;
	{
		CharPtr			nameP; -------------------------+
		CharPtr*		nameAliasesP;-------------------|---+
		Word			addrType;                       |   |
		Word			addrLen;                        |   |
		BytePtr*		addrListP;----------------------|---|---+
	}                                                   |   |   |
										                |   |   |
	Char			name[netDNSMaxDomainName+1];   <----+   |   |
                                                            |   |
	CharPtr			aliasList[netDNSMaxAliases+1];   <------+   |
	Char			aliases[netDNSMaxAliases][netDNSMaxDomainName+1];
                                                                |
	NetIPAddr*		addressList[netDNSMaxAddresses];   <--------+
	NetIPAddr		address[netDNSMaxAddresses];

	} NetHostInfoBufType, *NetHostInfoBufPtr;
*/

NetHostInfoBufType	Marshal::GetNetHostInfoBufType (uaecptr p)
{
	NetHostInfoBufType	netHostInfoBufType;
	memset (&netHostInfoBufType, 0, sizeof (netHostInfoBufType));

	if (p)
	{
	}

	return netHostInfoBufType;
}


void Marshal::PutNetHostInfoBufType (uaecptr p, NetHostInfoBufType& netHostInfoBuf)
{
	if (p)
	{
		int	ii;	// Goddampieceoshit VC++ doesn't scope loop variables right!

		// First copy over the easy fields (the ones with real values in them).

			// NetHostInfoBufType.hostInfo.addrType
		put_word (p + offsetof (NetHostInfoBufType, hostInfo.addrType),
					netHostInfoBuf.hostInfo.addrType);

			// NetHostInfoBufType.hostInfo.addrLen
		put_word (p + offsetof (NetHostInfoBufType, hostInfo.addrLen),
					netHostInfoBuf.hostInfo.addrLen);

			// NetHostInfoBufType.name
		uae_strcpy (p + offsetof (NetHostInfoBufType, name), netHostInfoBuf.name);

			// NetHostInfoBufType.aliases
		uae_memcpy (p + offsetof (NetHostInfoBufType, aliases),
					(void*) netHostInfoBuf.aliases,
					sizeof (netHostInfoBuf.aliases));

			// NetHostInfoBufType.address
			// Copy them one at a time to sort out endian issues.  Just how this
			// is supposed to be done is not clear (NetLib documentation says that
			// the addresses are in HBO, while the WinSock header says that the
			// addresses are supplied in NBO but returned in HBO), but the following
			// seems to work.
		for (ii = 0; ii < netDNSMaxAddresses; ++ii)
		{
			NetIPAddr	addr = ntohl (netHostInfoBuf.address[ii]);
			put_long (p + offsetof (NetHostInfoBufType, address[ii]), NetHToNL (addr));
		}

		// Second, set up the pointers to those values.

			// NetHostInfoType.hostInfo.nameP
		put_long (p + offsetof (NetHostInfoBufType, hostInfo.nameP),
					p + offsetof (NetHostInfoBufType, name));

			// NetHostInfoType.hostInfo.nameAliasesP
		put_long (p + offsetof (NetHostInfoBufType, hostInfo.nameAliasesP),
					p + offsetof (NetHostInfoBufType, aliasList));

			// NetHostInfoType.hostInfo.addrListP
		put_long (p + offsetof (NetHostInfoBufType, hostInfo.addrListP),
					p + offsetof (NetHostInfoBufType, addressList));

			// NetHostInfoType.aliasList
		for (ii = 0; ii < netDNSMaxAliases+1; ++ii)
		{
			if (netHostInfoBuf.aliasList[ii] != NULL)
			{
				put_long (p + offsetof (NetHostInfoBufType, aliasList[ii]),
					p + netHostInfoBuf.aliasList[ii] - (CharPtr) &netHostInfoBuf);
			}
			else
			{
				put_long (p + offsetof (NetHostInfoBufType, aliasList[ii]), UAE_NULL);
				break;
			}
		}

			// NetHostInfoType.addressList
		for (ii = 0; ii < netDNSMaxAddresses; ++ii)
		{
			if (netHostInfoBuf.addressList[ii] != NULL)
			{
				put_long (p + offsetof (NetHostInfoBufType, addressList[ii]),
							p + offsetof (NetHostInfoBufType, address[ii]));
			}
			else
			{
				put_long (p + offsetof (NetHostInfoBufType, addressList[ii]),
							UAE_NULL);
				break;
			}
		}
	}
}


// ------------------------------
// ----- NetServInfoBufType -----
// ------------------------------

NetServInfoBufType	Marshal::GetNetServInfoBufType (uaecptr p)
{
	NetServInfoBufType	netServInfoBuf;
	memset (&netServInfoBuf, 0, sizeof (netServInfoBuf));

	if (p)
	{
	}

	return netServInfoBuf;
}

void Marshal::PutNetServInfoBufType (uaecptr p, NetServInfoBufType& netServInfoBuf)
{
	if (p)
	{
		// =======================================================
		// Convert NetHostInfoBufType.hostInfo
		// =======================================================

		// Get a handy pointer to the servInfo field.
		uaecptr	p2 = p + offsetof (NetServInfoBufType, servInfo);

		// -------------------------------------------------------
		// NetServInfoBufType.servInfo.nameP = &NetServInfoBufType.name
		// -------------------------------------------------------

		put_long (p2 + offsetof (NetServInfoType, nameP), p + offsetof (NetServInfoBufType, name));

		// -------------------------------------------------------
		// NetServInfoBufType.servInfo.nameAliasesP = &NetServInfoBufType.aliasList
		// -------------------------------------------------------

		put_long (p2 + offsetof (NetServInfoType, nameAliasesP), p + offsetof (NetServInfoBufType, aliasList));

		Word	port = ntohs (netServInfoBuf.servInfo.port);
		put_word (p2 + offsetof (NetServInfoType, port), NetHToNS (port));

		// -------------------------------------------------------
		// NetServInfoBufType.servInfo.protoP = &NetServInfoBufType.protoName
		// -------------------------------------------------------

		put_long (p2 + offsetof (NetServInfoType, protoP), p + offsetof (NetServInfoBufType, protoName));


		// =======================================================
		// Convert remaining NetHostInfoBufType fields
		// =======================================================

		// -------------------------------------------------------
		// name, protoName
		// -------------------------------------------------------

		uae_memcpy (p + offsetof (NetServInfoBufType, name), (void*) &netServInfoBuf.name, netServMaxName+1);
		uae_memcpy (p + offsetof (NetServInfoBufType, protoName), (void*) &netServInfoBuf.protoName, netProtoMaxName+1);

		// -------------------------------------------------------
		// aliasList, addressList
		// -------------------------------------------------------

		long	offset;
		long	index = 0;
		while (netServInfoBuf.aliasList[index])
		{
			// aliasList contains a list of string addresses.  Calculate the offset
			// of that string from the beginning of the NetHostInfoBufType.
			offset = ((char*) &netServInfoBuf.aliasList[index]) - (char*) &netServInfoBuf;

			// Use that offset to calculate the address of the corresponding location
			// in the destination.
			put_long (p + offsetof (NetServInfoBufType, aliasList) + index * sizeof (CharPtr), p + offset);

			// Copy over the string.
			uae_strcpy (p + offset, netServInfoBuf.aliasList[index]);

			++index;
		}
		put_long (p + offsetof (NetServInfoBufType, aliasList) + index * sizeof (CharPtr), UAE_NULL);
	}
}

