// GraphBW.cpp: implementation of the CGraphBW class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Graph##CLASSNAME##.h"
#include "imgsource/ISource.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//Note to myself: Add to Node Master
//case ##NODENAME##: pNode = new CGraph##CLASSNAME##();break;
//#define ##NODENAME## XX

##LISTDEFINES##

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

#define ROUND(x) ((int) ((x) + 0.5))
#define CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x)))


CGraph##CLASSNAME##::CGraph##CLASSNAME##()
{
	
	SetCategory(##CATEGORY##);
	
	// Title
	m_sName = "##TITLE##";
	m_sDescription = "##DESCRIPTION##";

	// don't add .png, nor path
	m_sThumbnail = "##THUMBNAIL##";

	RequiredInputs(##REQINPUTS##);
	RequiredOutputs(##REQOUTPUTS##);

	// SIZE
	m_nSizeX = ##SIZEX##;
	m_nSizeY = ##SIZEY##;

	colorscheme = ##COLORSCHEME##;
	colorTitle = ##COLORSCHEMETEXT##;


	// FLAGS
##FLAGS##


##BINDING##>>
	//Binding default controls, at least the first one is essential
	AddParameter("No Link",0,0,0,TYPE_ONEOFMANY);
	AddParameter("Knob",0,0,0,TYPE_EDIT);
	AddParameter("Value",0,0,100,TYPE_SLIDER);

	SetUIBindingParam(2); // The Slider 2 will also change the Bind value
<<##BINDING##		

	// UI elements
##PARAMETERS##



	// Local Members Initialization **********
##INTERACTIVE##>>

	m_nClickedX = 0;
	m_nClickedY = 0;
	m_bClicked = FALSE;
	m_bMinimized = TRUE;

<<##INTERACTIVE##
##INPUTSPLICE##>>
	m_dZoom = 1.0;
	m_dX1 = 0;
	m_dY1 = 0;
	m_dX2 = 1;
	m_dY2 = 1;
	m_nFullW = 0;
	m_nFullH = 0;
<<##INPUTSPLICE##

}

CGraph##CLASSNAME##::~CGraph##CLASSNAME##()
{

}

// initialize is called just after creation
// initialize some memory intensive arrays or engines
void CGraph##CLASSNAME##::InitNode()
{
	
}
// Drawing in the Connection view, you can just call the build in functions
// 
void CGraph##CLASSNAME##::Draw(CDC *pDC, int nOffsetX, int nOffsetY, int nHighlightedID,float scale)
{
	
##DRAWCODE0##>>
// SIMPLE DRAWING **********************************************
##DRAWSIMPLE##>>
	DrawSimple(pDC,nOffsetX,nOffsetY,nHighlightedID,scale);
<<##DRAWSIMPLE##
##DRAWTHUMB##>>
	DrawThumbnailed(pDC,nOffsetX,nOffsetY,nHighlightedID,scale);
<<##DRAWTHUMB##
<<##DRAWCODE0##
##DRAWCODE1##>>
//	MORE ADVANCED DRAWING ************************************* 
//	break down each component separately
	if (m_nID==(UINT)nHighlightedID)
	{
		DrawRectangle(pDC,nOffsetX,nOffsetY,Lighten(colorscheme),Lighten(colorTitle),scale);
##DRAWTHUMB##>>
		DrawThumbnailOnBG(pDC, nOffsetX, nOffsetY,Lighten(colorscheme),scale);
<<##DRAWTHUMB##
		DrawHLRectangle(pDC,nOffsetX,nOffsetY,RGB(255,255,255),scale);
	}
	else
	{
		DrawRectangle(pDC,nOffsetX,nOffsetY,colorscheme,colorTitle,scale);
##DRAWTHUMB##>>
		DrawThumbnailOnBG(pDC, nOffsetX, nOffsetY,colorscheme,scale);
<<##DRAWTHUMB##
		
	}

	DrawSockets(pDC, nOffsetX, nOffsetY,scale);
	DrawError(pDC,nOffsetX, nOffsetY, scale);
<<##DRAWCODE1##
##DRAWCODE2##>>
//  MANUAL DRAWING *********************************************
//	Do it the hard way
//  Note: all final drawing has to be shifted with nOffsetX, nOffsetY and multiplied with scale before drawing in pDC
//  normally you work on local coordinates, then use MultiplyRect(CRect,nOffsetX, nOffsetY, scale) or MultiplyPoint(CPpoint, nOffsetX, nOffsetY, scale) before drawing

//	Background may look like this
//  ***********************************************
	CRect rc;
	rc.left   = m_nPosX;
	rc.top    = m_nPosY;
	rc.right  = m_nPosX+m_nSizeX;
	rc bottom = m_nPosY+m_nSizeY;
	MultiplyRect(rc,nOffsetX,nOffsetY,scale);

	CBrush brushBG(colorscheme);
	CBrush* pOldBrush = pDC->SelectObject(&brushBG);

	// create and select a thick, black pen
	CPen penBlack;
	penBlack.CreatePen(PS_SOLID, 2.5*scale, RGB(32, 32, 32));
	CPen* pOldPen = pDC->SelectObject(&penBlack);

	pDC->RoundRect(rc, CPoint(scale*12, scale*12));


//	Sockets may look like this
//  ***********************************************
	CPoint pt1 = GetInSocketPt(1);
	CRect socket1;
	InflateRectFromPoint(socket1,pt1,5,5);
	MultiplyRect(socket1,nOffsetX,nOffsetY,scale);
	pDC->Ellipse(socket1);

//	CPoint pt2 = GetInSocketPt(2);
	CPoint ptOut = GetOutSocketPt();
	CRect socketOut;
	InflateRectFromPoint(socketOut,pt1,5,5);
	MultiplyRect(socketOut,nOffsetX,nOffsetY,scale);
	pDC->Ellipse(socketOut);

	pDC->SelectObject(pOldPen);
	penBlack.DeleteObject();

//	Highlight rectangle may be something like this
//  ***********************************************
	CRect rcHL;
	rcHL.left   = m_nPosX;
	rcHL.top    = m_nPosY;
	rcHL.right  = m_nPosX+m_nSizeX;
	rcHL.bottom = m_nPosY+m_nSizeY;
	MultiplyRect(rcHL,nOffsetX,nOffsetY,scale);
	
	rcHL.DeflateRect(3,3);
	pDC->SelectStockObject(NULL_BRUSH);
	
	// create and select a thick, black pen
	CPen penBlackDot;
	penBlackDot.CreatePen(PS_DOT, 1,clr);
	CPen* pOldPenDot = pDC->SelectObject(&penBlackDot);
	
	pDC->RoundRect(rcHL, CPoint(scale*12, scale*12));
	
	// put back the old objects
	pDC->SelectObject(pOldPenDot);
	
	penBlackDot.DeleteObject();


	// draw error badge
	DrawError(pDC,nOffsetX, nOffsetY, scale);

//  Note:
//	what the MultiplyRect does is exactly this:
//	left   = (left+nOffsetX)*scale
//	top    = (top+nOffsetY)*scale;
//	right  = (right+nOffsetX)*scale
//	bottom = (bottom+nOffsetY)*scale;


<<##DRAWCODE2##	
}

##ONEINPUT##>>
// Main Processing Function for one input
//*****************************************
// Buffer is always 32 bit BGRA
// and it is upside down
BOOL CGraph##CLASSNAME##::ProcessImage(CQuickBitmap *pInput)
{
	if (pInput==NULL)
	{
		ASSERT(FALSE);
		m_sErrorMessage="Serious Error";
		return FALSE;
	}
	if (!pInput->IsValid())
	{
		ASSERT(FALSE);
		m_sErrorMessage="No Valid Input";
		return FALSE;
	}

	int nW = pInput->GetWidth();
	int nH = pInput->GetHeight();

	m_CurrentImage.Copy(pInput);

	BYTE* pInputBuffer = pInput->GetBits();
	BYTE* pDestinationBuffer = m_CurrentImage.GetBits();

	UINT32 nBytesPP = 4;
	UINT32 nRowStride = nW*4;

	//List of Parameters
##LISTPARAMETERS##

##INPUTSPLICE##>>
	// ATTN: some processing such as blur or sharpen needs to be adjusted by m_dZoom as they produce different results on different levels of zoom
	// NOTE: it is often as simple as multiplying the m_sZoom with dSigma value of Gaussian Blur.
<<##INPUTSPLICE##


	for (int y = 0; y< nH; y++)
	{
		for (int x = 0; x< nW; x++)
		{

			int nIdx = x*4+y*4*nW;

			int nR = pInputBuffer[nIdx+CHANNEL_R];
			int nG = pInputBuffer[nIdx+CHANNEL_G];
			int nB = pInputBuffer[nIdx+CHANNEL_B];

		
			int nA = CLAMP255((nR+nG+nB)/3);

			pDestinationBuffer[nIdx+CHANNEL_R] = nA;
			pDestinationBuffer[nIdx+CHANNEL_G] = nA;
			pDestinationBuffer[nIdx+CHANNEL_B] = nA;
			
		}


	}

	// return TRUE, unless there is some error

	return TRUE;
	
}
<<##ONEINPUT##
##TWOINPUT##>>
// Main Processing Function for two inputs
//*****************************************
// input 1 is top socket 
// input 2 is bottom socket 
// Buffer is always 32 bit BGRA
// and it is upside down
BOOL CGraph##CLASSNAME##::ProcessImage(CQuickBitmap *pInput1,CQuickBitmap* pInput2)
{
	if (pInput1==NULL || pInput2==NULL)
	{
		ASSERT(FALSE);
		m_sErrorMessage="Serious Error";
		return FALSE;
	}
	if (!pInput1->IsValid() || !pInput2->IsValid())
	{
		ASSERT(FALSE);
		m_sErrorMessage="No Valid Input";
		return FALSE;
	}
	
	int nW = pInput1->GetWidth();
	int nH = pInput1->GetHeight();

##INPUTSPLICE##>>
	if (m_nFullW == 0)
		m_nFullW = nW;
	
	if (m_nFullH == 0)
		m_nFullH = nH;
<<##INPUTSPLICE##

	// both inputs are ALWAYS the same size
	
	m_CurrentImage.Copy(pInput1);
	
	BYTE* pInpBuffA = pInput1->GetBits(); // top lAYER
	BYTE* pInpBuffB = pInput2->GetBits(); // bottom LAYER
	BYTE* pDestinationBuffer = m_CurrentImage.GetBits();

	UINT32 nComponentsPerPixel = 4;
	UINT32 nRowStride = nW*4;
	
	//List of Parameters
##LISTPARAMETERS##
	
	// return TRUE, unless there is some error
	return TRUE;
}

<<##TWOINPUT##
##INPUTSPLICE##>>
// special cases when the process NEEDS to know the full size of the image, the visible crop on the monitor and the zoom
// a good example for the zoom would be a blur function that should blur full image more than zoomed out image
// zoom is usually 1<dZoom<0
// nOrigW and nOrig H  full size of the input image
// nW, nH - currently processed size - this is the size ProcessImage receives
// dX1, dY1, dX2, dY2 - crop coefficients from the original image - there is only very few cases when you ever need this 
// dZoom - the zoom of the preview - usually < 1, unless users clicks 1:1

BOOL CGraph##CLASSNAME##::InputSlice(int nOrigW, int nOrigH, int nW, int nH, double dX1, double dY1, double dX2, double dY2, double dZoom)
{
	if (nOrigW==0||nOrigH==0)
	{
		return FALSE;
	}
	
	if (nW==0 || nH==0)
		return FALSE;
	
	m_dX1 = dX1;
	m_dY1 = dY1;
	m_dX2 = dX2;
	m_dY2 = dY2;
	
	m_dZoom = dZoom;
	
	m_nFullW = nOrigW;
	m_nFullH = nOrigH;

	return TRUE;
}
<<##INPUTSPLICE##
##INTERACTIVE##>>
// Interactive functions
// *******************************
// CPoint is in canvas coordinates
// to get local coordinates: 		
// int nX = point.x-m_nPosX;
// int nY = point.y-m_nPosY;

// Double Click
// ************************************** 
// Return TRUE if Handled 
BOOL CGraph##CLASSNAME##::RespondToDoubleClick(CPoint point)
{

	// Must return TRUE if it responds in any way
	// an example could be changing size of an object
	// m_nSizeX = 200;
	// m_nSizeY = 200;
	// or opening a dialog box for more data

	// Minimize code
	m_bMinimized = !m_bMinimized;
	
	if (m_bMinimized)
	{
		m_nSizeX=##SIZEX##;
		m_nSizeY=##SIZEY##;
		
	}
	else
	{
		m_nSizeX=##SIZEX##*2;
		m_nSizeY=##SIZEY##*2;
		
	}

	return FALSE;
}

// Button Down
// ************************************** 
// Return TRUE if Handled 
BOOL CGraph##CLASSNAME##::RespondToButtonDown(CPoint point)
{
	m_nClickedX = point.x-m_nPosX;
	m_nClickedY = point.y-m_nPosY;
	
	m_bClicked = FALSE;
	if (point.y>m_nPosY+20 && point.y<m_nPosY+m_nSizeY-4)
	{
		m_bClicked = TRUE;
		
##BINDING##>>
		// the parent object can change the value, so don't assume you know what it is or what it was
		// you need to get it from bind value
		double dLastValue = GetBindValue();
		StartChangingBindValue();// for UI to know if to update object
<<##BINDING##		
		
		return TRUE;
	}
	
	return FALSE;
}

// MouseMove
// ************************************** 
// Return TRUE only if previously clicked on the item 
BOOL CGraph##CLASSNAME##::RespondToMouseMove(CPoint point)
{
	if (m_bClicked)
	{
		
		
		int nX = point.x-m_nPosX;
		int nY = point.y-m_nPosY;

		int nDeltaX = m_nClickedX-nX;
		int nDeltaY = m_nClickedY-nY;

		m_nClickedX = point.x;
		m_nClickedY = point.y;

		// do whatever you need to do

##BINDING##>>
		// BINDING
		// Bind values are ALWAYS <0..1>
		double dValue = 1.0; // << some smart calculation of the bind value from the nDeltaX

		// set the Bind value, the system will update the correct object automatically
		// and will also 
		SetBindValue(dValue);

		// this is because we set up SetUIBindingParam(2) so we need to pass the data to param 2 as well
		SetParamValue01(2,dValue);
<<##BINDING##		

		return TRUE;

	}

	return FALSE;

}

// Mouse Wheel
// ************************************** 
// Return TRUE if Handled 
BOOL CGraph##CLASSNAME##::RespondToMouseWheel(CPoint point,short zDelta)
{
##BINDING##>>
	if (zDelta<0)
	{
		double dValue = GetBindValue();
		
		dValue+=0.05;
		
		SetBindValue(dValue);
		
		return TRUE;
	}
	if (zDelta>0)
	{
		double dValue = GetBindValue();
		
		dValue-=0.05;
		
		SetBindValue(dValue);

		return TRUE;
	}
<<##BINDING##		
	
	return FALSE;
}

// Button Up
// ************************************** 
// Return TRUE if Handled 

BOOL CGraph##CLASSNAME##::RespondToButtonUp(CPoint point)
{
	BOOL bClicked = m_bClicked;
	m_bClicked = FALSE;
	
	return bClicked;
}
<<##INTERACTIVE##

// RESET Parameters
//**************************************************************************
// helper function to reset parameters to their default value
// Normally you need to add a push button and call it from there
void CGraph##CLASSNAME##::ResetParams()
{
	
##RESETPARAMS##
	
	return;
}
// Called when Data from UI was changed
// ************************************
// nParam is the ID of the parameter currently changed
// there is not much to do here for normal process as the parameters are updated automatically
// but it can be used to load an image or change UI settings
// but this function  is called even when there is no image 
// UIButtonPushed is called first then UIDataChanged
BOOL CGraph##CLASSNAME##::UIDataChanged(int nParam)
{

	return FALSE;
}


##PUSHBUTTONS##>>
// Push BUTTONS called from Panel
// ************************************

BOOL CGraph##CLASSNAME##::UIButtonPushed(int nParam, int nSubButton)
{

##PUSHBUTTONRAMETERS##
	
	return TRUE;
}
<<##PUSHBUTTONS##

