/**------------------------------------------------------------------------
 * @file
 *
 * Copyright 2001 by the University of Toronto (UT).
 *
 *                       All Rights Reserved
 * 
 * Permission to copy and modify this software and its documentation 
 * only for internal use in your organization is hereby granted, 
 * provided that this notice is retained thereon and on all copies.  
 * UT makes no representations as to the suitability and operability 
 * of this software for any purpose.  It is provided "as is" without 
 * express or implied warranty.
 *
 * UT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
 * SHALL UT BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * No other rights, including for example, the right to redistribute 
 * this software and its documentation or the right to prepare 
 * derivative works, are granted unless specifically provided in a 
 * separate license agreement.
 *
 * Author: Diego Macrini
 *
 *-----------------------------------------------------------------------*/
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>

#include "LigatureSegment.h"

#define ADDIN_API    __declspec(dllexport)

// Declare derived classes in order to access the protected
// members of the objects that we are interested in
struct BranchSegmentDbg : public dml::BranchSegment 
{ 
	unsigned int DbgFirst() const { return m_first; }
	unsigned int DbgLast() const  { return m_last; }
};

struct LigatureSegmentDbg : public dml::LigatureSegment
{ 
	unsigned int DbgFirst() const { return m_first; }
	unsigned int DbgLast() const  { return m_last; }
};

// *** NOTE ***
// This DLL is copied to the $(DevEnvDir) folder by a Post-build event.s
// Such a folder requires administrator privileges

// CustomViewer callback functions parameters
// HRESULT WINAPI CustomViewer(
//    DWORD dwAddress,       // low 32-bits of address
//    DEBUGHELPER *pHelper,  // callback pointer to access helper functions
//    int nBase,             // decimal or hex
//    BOOL bIgnore,          // not used
//    char *pResult,         // where the result needs to go
//    size_t max,            // how large the above buffer is
//    DWORD dwReserved       // always pass zero
// )

/*!
	Debug helper

	@see doc/"Customizing the Visual Studio Debugger Display.pdf"
	@see doc/"Expression Evaluator Add-In Sample.pdf"
*/
typedef struct tagDEBUGHELPER
{
    DWORD dwVersion;

    HRESULT (WINAPI *ReadDebuggeeMemory)(struct tagDEBUGHELPER *pThis, DWORD dwAddr, 
		DWORD nWant, VOID* pWhere, DWORD *nGot);

    // from here only when dwVersion >= 0x20000
    DWORDLONG (WINAPI *GetRealAddress)(struct tagDEBUGHELPER *pThis);

    HRESULT (WINAPI *ReadDebuggeeMemoryEx)(struct tagDEBUGHELPER *pThis, DWORDLONG qwAddr, 
		DWORD nWant, VOID* pWhere, DWORD *nGot);

    int (WINAPI *GetProcessorType)(struct tagDEBUGHELPER *pThis);
} DEBUGHELPER;

HRESULT HandleMemoryError(DEBUGHELPER* pHelper, char* pResult)
{
	wsprintf(pResult,"memory error");

	return S_OK;
}

HRESULT ReadDDSEdge(DEBUGHELPER* pHelper, DWORDLONG qwAddress, sg::DDSEdge& edge)
{
	DWORD nGot = 0; // number of bytes read

	// Read only first three pointers (crashes if more is read)
	DWORD nWant = sizeof(void*) * 3; 

	// Read DDS edge
	return pHelper->ReadDebuggeeMemoryEx(pHelper, qwAddress, nWant, &edge, &nGot);
}

HRESULT ReadDDSNode(DEBUGHELPER* pHelper, DWORDLONG qwAddress, sg::DDSNode& node)
{
	DWORD nGot = 0; // number of bytes read

	// Read only first pointer and flux point (crashes if more is read)
	DWORD nWant = sizeof(void*) + sizeof(sg::FluxPoint);

	// Read DDS node
	return pHelper->ReadDebuggeeMemoryEx(pHelper, qwAddress, nWant, &node, &nGot);
}

/*!
	Expresion evaluator callback for dml::LigatureSegment objects
*/
ADDIN_API HRESULT WINAPI EE_LigatureSegment(DWORD dwAddress, DEBUGHELPER* pHelper, int nBase, 
											BOOL bUniStrings, char* pResult, size_t max, 
											DWORD reserved)
{
	LigatureSegmentDbg ls; // object to read
	HRESULT hRes;          // result error code
	DWORD nGot = 0;        // number of bytes read

	DWORDLONG qwRealAddress = pHelper->GetRealAddress(pHelper); // good for 32 and 64 bits

	// Read the object from the debuggee's memory
	hRes = pHelper->ReadDebuggeeMemoryEx(pHelper, qwRealAddress, sizeof(ls), &ls, &nGot);

	if (hRes != S_OK)
		return HandleMemoryError(pHelper, pResult);

	if (ls.m_pBranch)
	{
		sg::DDSEdge e(NULL);

		if (ReadDDSEdge(pHelper, (DWORDLONG)ls.m_pBranch, e) != S_OK)
			return HandleMemoryError(pHelper, pResult);

		sg::FluxPoint fp;
		sg::DDSNode n1(fp);
		sg::DDSNode n2(fp);

		if (ReadDDSNode(pHelper, (DWORDLONG)e.n1, n1) != S_OK)
			return HandleMemoryError(pHelper, pResult);

		if (ReadDDSNode(pHelper, (DWORDLONG)e.n2, n2) != S_OK)
			return HandleMemoryError(pHelper, pResult);

		wsprintf(pResult,"n1:(%d,%d) n2:(%d,%d) [%u %u] %c %c ", (int)n1.fp.p.x, (int)n1.fp.p.y, 
			(int)n2.fp.p.x, (int)n2.fp.p.y, ls.DbgFirst(), ls.DbgLast(), 
			(ls.m_corner1.m_valid) ? 'Y' : 'N', (ls.m_corner2.m_valid) ? 'Y' : 'N');

		/*sprintf(pResult,"n1:(%.1f,%.1f) n2:(%.1f,%.1f) [%d %d %d] %c %c ", n1.fp.p.x, n1.fp.p.y, 
			n2.fp.p.x, n2.fp.p.y, ls.First(), ls.Last(), ls.Size(),
			(ls.m_corner1.m_valid) ? 'Y' : 'N', (ls.m_corner2.m_valid) ? 'Y' : 'N');*/
	}
	else
	{
		wsprintf(pResult,"empty branch");
	}

	return S_OK;
}

/*!
	Expresion evaluator callback for dml::BranchSegment objects
*/
ADDIN_API HRESULT WINAPI EE_BranchSegment(DWORD dwAddress, DEBUGHELPER* pHelper, int nBase, 
										  BOOL bUniStrings, char* pResult, size_t max, 
										  DWORD reserved)
{
	BranchSegmentDbg bs; // object to read
	HRESULT hRes;        // result error code
	DWORD nGot = 0;      // number of bytes read

	DWORDLONG qwRealAddress = pHelper->GetRealAddress(pHelper); // good for 32 and 64 bits

	// Read the object from the debuggee's memory
	hRes = pHelper->ReadDebuggeeMemoryEx(pHelper, qwRealAddress, sizeof(bs), &bs, &nGot);

	if (hRes != S_OK)
		return HandleMemoryError(pHelper, pResult);

	if (bs.m_pBranch)
	{
		sg::DDSEdge e(NULL);

		if (ReadDDSEdge(pHelper, (DWORDLONG)bs.m_pBranch, e) != S_OK)
			return HandleMemoryError(pHelper, pResult);

		sg::FluxPoint fp;
		sg::DDSNode n1(fp);
		sg::DDSNode n2(fp);

		if (ReadDDSNode(pHelper, (DWORDLONG)e.n1, n1) != S_OK)
			return HandleMemoryError(pHelper, pResult);

		if (ReadDDSNode(pHelper, (DWORDLONG)e.n2, n2) != S_OK)
			return HandleMemoryError(pHelper, pResult);

		wsprintf(pResult,"n1:(%d,%d) n2:(%d,%d) [%u %u] %s", (int)n1.fp.p.x, (int)n1.fp.p.y, 
			(int)n2.fp.p.x, (int)n2.fp.p.y,  bs.DbgFirst(), bs.DbgLast(), 
			(bs.IsInverted()) ? "inverted" : "");
	}
	else
	{
		wsprintf(pResult,"empty branch");
	}

	//wsprintf(pResult,"test %d %d %d", hRetVal, S_OK, E_FAIL);

	return S_OK;
}
