#include "stdafx.h"// Slider.cpp : Implementation of cSlider
#include <stdio.h>
#include "DecalControls.h"
#include "Slider.h"

/////////////////////////////////////////////////////////////////////////////
// cSlider

cSlider::cSlider() : m_nTextColor(RGB(0, 0, 0)),
	m_bMouseIn(VARIANT_FALSE),
	m_bGrappled(VARIANT_FALSE),
	m_nMinimum(0),
	m_nMaximum(100),
	m_nMinSliderColor( RGB(200,0,0) ),
	m_nMaxSliderColor( RGB(0,200,0) ),
	m_nSliderPos(0),
	m_strTextLeft(NULL),
	m_strTextRight(NULL),
	m_bVertical(VARIANT_FALSE) {
}

cSlider::~cSlider() {
	if (m_strTextLeft)
		free(m_strTextLeft);

	if (m_strTextRight)
		free(m_strTextRight);
}

STDMETHODIMP cSlider::get_Font(IFontCacheDisp **pVal) {
   _ASSERTE(pVal != NULL);

   if (m_pFont.p == NULL)
      *pVal = NULL;
   else
      m_pFont->QueryInterface(pVal);

	return S_OK;
}

STDMETHODIMP cSlider::putref_Font(IFontCacheDisp *newVal) {
   _ASSERTE(newVal != NULL);

   if (m_pFont.p)
      m_pFont.Release();

   HRESULT hRes = newVal->QueryInterface(&m_pFont);

   _ASSERTE(SUCCEEDED(hRes));
   m_pSite->Reformat();

	return S_OK;
}

STDMETHODIMP cSlider::get_TextColor(long *pVal) {
   _ASSERTE(pVal != NULL);

   *pVal = m_nTextColor;

	return S_OK;
}

STDMETHODIMP cSlider::put_TextColor(long newVal) {
	_ASSERTE((newVal & 0xFF000000L) == 0);

	m_nTextColor = newVal;
	m_pSite->Invalidate();
	Reformat();

	return S_OK;
}


STDMETHODIMP cSlider::get_SliderPosition(long *pVal) {
	_ASSERTE(pVal != NULL);

	*pVal = m_nSliderPos;

	return S_OK;
}

STDMETHODIMP cSlider::put_SliderPosition(long newVal) {
	if (m_nSliderPos == newVal)
		return S_OK;

	if (newVal < m_nMinimum || newVal > m_nMaximum)
		return S_FALSE;

	m_nSliderPos = newVal;
	Reformat();
	m_pSite->Invalidate();


	return S_OK;
}

STDMETHODIMP cSlider::get_Minimum(long *pVal) {
	_ASSERTE(pVal != NULL);

	*pVal = m_nMinimum;

	return S_OK;
}

STDMETHODIMP cSlider::put_Minimum(long newVal) {
	if (m_nMinimum == newVal)
		return S_OK;

	m_nMinimum = newVal;
	Reformat();
	m_pSite->Invalidate();

	return S_OK;
}

STDMETHODIMP cSlider::get_Maximum(long *pVal) {
	_ASSERTE(pVal != NULL);

	*pVal = m_nMaximum;

	return S_OK;
}

STDMETHODIMP cSlider::put_Maximum(long newVal) {
	if (m_nMaximum == newVal)
		return S_OK;

	m_nMaximum = newVal;
	Reformat();
	m_pSite->Invalidate();

	return S_OK;
}

STDMETHODIMP cSlider::SchemaLoad(IView *pView, IUnknown *pSchema)
{
	// First attempt to load the font
	CComPtr< IPluginSite > pPlugin;
	m_pSite->get_PluginSite( &pPlugin );

	pPlugin->CreateFontSchema(14, 0, pSchema, &m_pFont);

	MSXML::IXMLDOMElementPtr pElement = pSchema;

	_variant_t vTextColor = pElement->getAttribute(_T("textcolor")),
		vMinSliderColor = pElement->getAttribute(_T("mincolor")),
		vMaxSliderColor = pElement->getAttribute(_T("maxcolor")),
		vVertical = pElement->getAttribute(_T("vertical")),
		vDefault = pElement->getAttribute(_T("default")),
		vMaximum = pElement->getAttribute(_T("maximum")),
		vMinimum = pElement->getAttribute(_T("minimum")),
		vName = pElement->getAttribute(_T("name"));

	// Maximum must exist
	_ASSERTE(vMaximum.vt == VT_INT);

	if (vTextColor.vt != VT_NULL) {
		try {
			m_nTextColor = static_cast<long>(vTextColor);
		} catch(...) {
			// Type conversion error
			_ASSERTE(FALSE);
		}
	}

	if (vMinSliderColor.vt != VT_NULL) {
		try {
			_bstr_t szWhat = vMinSliderColor.bstrVal;
			m_nMinSliderColor = wcstoul( szWhat, 0, 16 );
		} catch( ... ) {
			// Type conversion error
			_ASSERTE(FALSE);
		}
	}

	if (vMaxSliderColor.vt != VT_NULL) {
		try {
			_bstr_t szWhat = vMaxSliderColor.bstrVal;
			m_nMaxSliderColor = wcstoul( szWhat, 0, 16 );
		} catch( ... ) {
			// Type conversion error
			_ASSERTE(FALSE);
		}
	}

	if (vVertical.vt != VT_NULL) {
		try {
			m_bVertical = (static_cast<bool>(vVertical)) ? VARIANT_TRUE : VARIANT_FALSE;
		} catch( ... ) {
			// Type conversion error
			_ASSERTE(FALSE);
		}
	}

	if (vMaximum.vt != VT_NULL) {
		try {
			m_nMaximum = static_cast<long>(vMaximum);
		} catch( ... ) {
			// Type conversion error
			_ASSERTE(FALSE);
		}
	}

	if (vMinimum.vt != VT_NULL) {
		try {
			m_nMinimum = static_cast<long>(vMinimum);
		} catch( ... ) {
			// Type conversion error
			_ASSERTE(FALSE);
		}
	}

	if (vDefault.vt != VT_NULL) {
		try {
			m_nSliderPos = static_cast<long>(vDefault);
		} catch( ... ) {
			m_nSliderPos = m_nMinimum;
		}
	}

/*	fprintf( f, "name - %s\n", OLE2A(vName.bstrVal) );
	fprintf( f, "textcolor - %08X\n", m_nTextColor );
	fprintf( f, "mincolor - %08X\n", m_nMinSliderColor );
	fprintf( f, "maxcolor - %08X\n", m_nMaxSliderColor );
	fprintf( f, "vertical - %X\n", m_bVertical );
	fprintf( f, "maximum - %08X\n", m_nMaximum );
	fprintf( f, "minimum - %08X\n\n", m_nMinimum ); */

/*	// it's a pain in the ass that we need set the min and max for them to work, aye? let's fix it
	RECT rc;
	m_pSite->get_Position(&rc);

	long siteHHalf = (rc.bottom - rc.top) / 2;

	// The background bar
	m_rcBackground.left = 0;
	m_rcBackground.top = siteHHalf - 4;
	m_rcBackground.bottom = siteHHalf + 6;
	m_rcBackground.right = rc.right - rc.left;

	// The middle bar
	m_rcBar.top = siteHHalf;
	m_rcBar.left = 0;
	m_rcBar.bottom = siteHHalf + 3;
	m_rcBar.right = rc.right - rc.left;

	// Ensure sane slider position
	if (m_nSliderPos < m_nMinimum)
		m_nSliderPos = m_nMinimum;

	if (m_nSliderPos > m_nMaximum)
		m_nSliderPos = m_nMaximum;

	long siteDiff = abs(m_rcBar.right - m_rcBar.left);
	long extentDiff = abs(m_nMaximum - m_nMinimum);
	long valDiff = m_nSliderPos - m_nMinimum;

	if (valDiff == 0) {
		m_ptSlider.x = m_rcBar.left;
		
	} else {
		long diff = (long)(((float)valDiff / (float)extentDiff) * (float)siteDiff);

		m_ptSlider.x = m_rcBar.left + diff;

		if (m_ptSlider.x >= m_rcBar.right - 6)
			m_ptSlider.x = m_rcBar.right - 6;
	}

	m_ptSlider.y = siteHHalf - 5;
*/

//	fclose( f );
	return S_OK;
}

STDMETHODIMP cSlider::Reformat() {
	USES_CONVERSION;

	char strTemp[16];

	if (m_strTextLeft)
		free(m_strTextLeft);

	if (m_strTextRight)
		free(m_strTextRight);

	_snprintf(strTemp, 15, "%i", m_nMinimum);
	m_strTextLeft = strdup(strTemp);

	_snprintf(strTemp, 15, "%i", m_nMaximum);
	m_strTextRight = strdup(strTemp);

	if (m_pFont.p == NULL) {
		// No font was specified, create the default font
		CComPtr<IPluginSite> pPlugin;
		m_pSite->get_PluginSite(&pPlugin);

		BSTR bstrFontName;
		pPlugin->get_FontName(&bstrFontName);
		pPlugin->CreateFont(bstrFontName /*_bstr_t(_T("Times New Roman"))*/, 14, 0, &m_pFont);
	}

	RECT rc;
	m_pSite->get_Position(&rc);

	SIZE textRight;

	m_pFont->MeasureText(_bstr_t(m_strTextRight), &textRight);

	long siteHHalf = (rc.bottom - rc.top) / 2;

	// The background bar
	m_rcBackground.left = 0;
	m_rcBackground.top = siteHHalf - 4;
	m_rcBackground.bottom = siteHHalf + 6;
	m_rcBackground.right = rc.right - rc.left;

	// The middle bar
	m_rcBar.top = siteHHalf;
	m_rcBar.left = 0;
	m_rcBar.bottom = siteHHalf + 3;
	m_rcBar.right = rc.right - rc.left;

	m_ptLeftText.x = m_rcBackground.left + 1;
	m_ptLeftText.y = m_rcBackground.bottom + 1;
	m_ptRightText.x = m_rcBackground.right - (textRight.cx + 1);
	m_ptRightText.y = m_rcBackground.bottom + 1;

	// Ensure sane slider position
	if (m_nSliderPos < m_nMinimum)
		m_nSliderPos = m_nMinimum;

	if (m_nSliderPos > m_nMaximum)
		m_nSliderPos = m_nMaximum;

	long siteDiff = abs(m_rcBar.right - m_rcBar.left);
	long extentDiff = abs(m_nMaximum - m_nMinimum);
	long valDiff = m_nSliderPos - m_nMinimum;

	if (valDiff == 0) {
		m_ptSlider.x = m_rcBar.left;
		
	} else {
		long diff = (long)(((float)valDiff / (float)extentDiff) * (float)siteDiff);

		m_ptSlider.x = m_rcBar.left + diff;

		if (m_ptSlider.x >= m_rcBar.right - 6)
			m_ptSlider.x = m_rcBar.right - 6;
	}

	m_ptSlider.y = siteHHalf - 5;

	return S_OK;
}

STDMETHODIMP cSlider::Render(ICanvas *pCanvas)
{
	// intensity to lose or gain for 3d effect
	#define COLOR_BAND 20

	USES_CONVERSION;

	CComPtr<IPluginSite> pPlugin;
	m_pSite->get_PluginSite(&pPlugin);

	CComPtr<IIconCache> pIcon;
	SIZE szIcon = {7, 12};
	pPlugin->GetIconCache(&szIcon, &pIcon);

	// we're averaging color differences w/bias...
	float fractional = m_nSliderPos / m_nMaximum;
	float lFraction = (1 - fractional) * 2;
	float rFraction = fractional * 2;

	long lMinSliderRed		= lFraction * (BYTE) m_nMinSliderColor;
	long lMinSliderGreen	= lFraction * (BYTE) (m_nMinSliderColor >> 8);
	long lMinSliderBlue		= lFraction * (BYTE) (m_nMinSliderColor >> 16);

	long lMaxSliderRed		= rFraction * (BYTE) m_nMaxSliderColor;
	long lMaxSliderGreen	= rFraction * (BYTE) (m_nMaxSliderColor >> 8);
	long lMaxSliderBlue		= rFraction * (BYTE) (m_nMaxSliderColor >> 16);

	long lFillColorTop, lFillColorMiddle, lFillColorBottom;

	long	lFillColorRed	= (lMinSliderRed + lMaxSliderRed) / 2,
			lFillColorGreen	= (lMinSliderGreen + lMaxSliderGreen) / 2,
			lFillColorBlue	= (lMinSliderBlue + lMaxSliderBlue) / 2;

	if( m_nMinSliderColor && m_nMaxSliderColor ) // not black .. who would choose all black for both bars anyway? grr.
	{
		// find the most intense color channel to maul for our evil purposes (we're using std::max here)
		long lMax = max( max( lFillColorRed, lFillColorGreen ), lFillColorBlue );

		// if we're dark, don't overflow
		long lOffset	= lMax > (COLOR_BAND - 1) ? COLOR_BAND : 0;
		long lOffset2	= lMax > (COLOR_BAND - 1) ? 0 : COLOR_BAND;

		// red
		if( lMax == lFillColorRed )
		{
			lFillColorTop		= RGB( lFillColorRed - lOffset, lFillColorGreen, lFillColorBlue );
			lFillColorMiddle	= RGB( lFillColorRed + lOffset2, lFillColorGreen, lFillColorBlue );
			lFillColorBottom	= RGB( lFillColorRed - lOffset, lFillColorGreen, lFillColorBlue );
		}

		// green
		else if( lMax == lFillColorGreen )
		{
			lFillColorTop		= RGB( lFillColorRed, lFillColorGreen - lOffset, lFillColorBlue );
			lFillColorMiddle	= RGB( lFillColorRed, lFillColorGreen + lOffset2, lFillColorBlue );
			lFillColorBottom	= RGB( lFillColorRed, lFillColorGreen - lOffset, lFillColorBlue );
		}

		// has to be blue
		else
		{
			lFillColorTop		= RGB( lFillColorRed, lFillColorGreen, lFillColorBlue - lOffset );
			lFillColorMiddle	= RGB( lFillColorRed, lFillColorGreen, lFillColorBlue + lOffset2 );
			lFillColorBottom	= RGB( lFillColorRed, lFillColorGreen, lFillColorBlue - lOffset );
		}
	}

	// someone's a GOTH
	else
	{
		lFillColorTop		= 0;
		lFillColorMiddle	= 0x00161616;
		lFillColorBottom	= 0;
	}

	pCanvas->Fill( &m_rcBar, lFillColorMiddle );

	RECT topBar = {m_rcBar.left, m_rcBar.top, m_rcBar.right, m_rcBar.top + 1};
	pCanvas->Fill( &topBar, lFillColorTop );

	RECT bottomBar = {m_rcBar.left, m_rcBar.bottom - 1, m_rcBar.right, m_rcBar.bottom};
	pCanvas->Fill( &bottomBar, lFillColorBottom );

	pIcon->DrawIcon(&m_ptSlider, 0x06001286, 0, pCanvas);

	m_pFont->DrawText(&m_ptLeftText, _bstr_t(m_strTextLeft), RGB(0, 0, 0), pCanvas);
	m_pFont->DrawText(&m_ptRightText, _bstr_t(m_strTextRight), RGB(0, 0, 0), pCanvas);

	return S_OK;
}

STDMETHODIMP cSlider::MouseDown(struct MouseState *mouseState) {
	_ASSERTE(m_pSite != NULL);

	POINT pt = mouseState->client;

	if (pt.x >= (m_ptSlider.x) && (pt.x <= m_ptSlider.x + 6) &&
		pt.y >= (m_ptSlider.y) && (pt.y <= m_ptSlider.y + 12)) {
		// They hit the grapple point!
		m_ptLastMouse = mouseState->client;
		m_bGrappled = VARIANT_TRUE;
	} else {
		// The hit the bar - see if that's within the bar rect

		if (pt.x > m_rcBar.left && pt.x < m_rcBar.right &&
			pt.y > (m_rcBar.top - 3) && pt.y < (m_rcBar.bottom + 3)) {

			// Calcuate the new slider position
			float pixelValue = (float)((float)(m_nMaximum - m_nMinimum) / (float)(m_rcBar.right - m_rcBar.left));

			m_nSliderPos = (pt.x - m_rcBar.left) * pixelValue;

			Reformat();
			m_pSite->Invalidate();

			long nID;
			m_pSite->get_ID(&nID);
			_ASSERTE(m_pSite.p != NULL);
			Fire_Change(nID, m_nSliderPos);
		}
	}

	return S_OK;
}

STDMETHODIMP cSlider::MouseUp(struct MouseState *mouseState) {
	m_bGrappled = VARIANT_FALSE;

	return S_OK;
}

STDMETHODIMP cSlider::MouseMove(struct MouseState *mouseState) {
	if (m_bGrappled) {
		long dx;
		POINT pt = mouseState->client;

		dx = m_ptLastMouse.x - pt.x;
		m_ptLastMouse = pt;

		float pixelValue = (float)((float)(m_nMaximum - m_nMinimum) / (float)(m_rcBar.right - m_rcBar.left));
		m_nSliderPos -= ((float)dx * pixelValue);

		// I personally like being able to drag the slider
		// PAST THE EDGE OF THE SLIDE BAR without needing pixel accuracy...
		if( pt.x >= m_rcBar.right )
			m_nSliderPos = m_nMaximum;

		Reformat();
		m_pSite->Invalidate();

		long nID;
		m_pSite->get_ID(&nID);
		_ASSERTE(m_pSite.p != NULL);
		Fire_Change(nID, m_nSliderPos);
	}

	return S_OK;
}