#include "StdAfx.h"
#include "emule.h"
#include "barshader.h"
#include "math.h"

#ifndef M_PI_2
#define M_PI_2     1.57079632679489661923
#endif

// Barry
#ifndef PI
#define PI 3.14159265358979323846264338328
#endif

#define HALF(X) (((X) + 1) / 2)

CBarShader::CBarShader(uint32 height, uint32 width) {
	m_iWidth = width;
	m_iHeight = height;
	m_uFileSize = 1;
	m_FirstSpan = new BarSpan(0, 1);
	m_Modifiers = NULL;
}

CBarShader::~CBarShader(void) {
	delete[] m_Modifiers;
	m_FirstSpan->DeleteUpTo(NULL);
	delete m_FirstSpan;
}

void CBarShader::Reset() {
	Fill(0);
}

void CBarShader::BuildModifiers() {
	if(m_Modifiers != NULL)
		delete[] m_Modifiers;

	m_used3dlevel=theApp.glob_prefs->Get3DDepth();
	// Barry - New property page slider to control depth of gradient

	// Depth must be at least 2
	// 2 gives greatest depth, the higher the value, the flatter the appearance
	// m_Modifiers[count-1] will always be 1, m_Modifiers[0] depends on the value of depth
	
	int depth = (7-theApp.glob_prefs->Get3DDepth());
	int count = HALF(m_iHeight);
	double piOverDepth = PI/depth;
	double base = piOverDepth * ((depth / 2.0) - 1);
	double increment = piOverDepth / (count - 1);

	m_Modifiers = new float[count];
	for (int i = 0; i < count; i++)
		m_Modifiers[i] = (float)(sin(base + i * increment));
}

void CBarShader::SetFileSize(uint32 fileSize) {
	if(m_uFileSize != fileSize) {
		m_uFileSize = fileSize;
		m_dPixelsPerByte = (double)m_iWidth / m_uFileSize;
		m_dBytesPerPixel = (double)m_uFileSize / m_iWidth;
	}
}

void CBarShader::FillRange(uint32 start, uint32 end, COLORREF color) {
	if(end > m_uFileSize)
		end = m_uFileSize;

	if(start >= end)
		return;

	BarSpan *bsPrev, *bsStart, *bsEnd;
	bsPrev = bsStart = m_FirstSpan;
	while(bsStart->next != NULL && bsStart->end < start)
		bsStart = bsStart->next;

	//place new span, unless it's the same color
	BarSpan *bsNew;

	//case 0, same color
	if(bsStart->color == color) {

		//same color, ends before end.
		if(bsStart->end > end)
			return;

		//case 1, same color, ends after end.
		bsNew = bsStart;
		bsNew->end = end;

	}
	
	else if(bsStart->start == start) {

		//case 2, begins at start, ends before end.
		if(bsStart->end > end) {
			//the 'ol switcheroo
			bsNew = new BarSpan(bsStart, end, bsStart->end, bsStart->color);
			bsStart->end = end;
			bsStart->color = color;
			return;
		}

		//case 3, begins at start, ends after end.
		else {
			//hostile takeover
			bsNew = bsStart;
			bsNew->end = end;
			bsNew->color = color;
		}

	}

	else if(bsStart->start < start) {

		//case 4, begins after start, ends before end
		if(bsStart->end > end) {
			bsNew = new BarSpan(bsStart, start, end, color);
			bsNew = new BarSpan(bsNew, end, bsStart->end, bsStart->color);
			bsStart->end = start;
			return;
		}

		//case 5, begins after start, ends after end
		else {
			bsNew = new BarSpan(bsStart, start, end, color);
			bsStart->end = start;
		}
	} else {
		ASSERT(FALSE);//should never get here
	}

	bsEnd = bsNew->next;
	while(bsEnd != NULL && bsEnd->end <= end)
		bsEnd = bsEnd->next;

	if(bsEnd != NULL)
		bsEnd->start = end;

	bsNew->DeleteUpTo(bsEnd);
}

void CBarShader::Fill(COLORREF color) {
	m_FirstSpan->DeleteUpTo(NULL);
	m_FirstSpan->start = 0;
	m_FirstSpan->end = m_uFileSize;
	m_FirstSpan->color = color;
}

void CBarShader::Draw(CDC* dc, int iLeft, int iTop, bool bFlat) {
	BarSpan *bsCurrent = m_FirstSpan;
	RECT rectSpan;
	rectSpan.top = iTop;
	rectSpan.bottom = iTop + m_iHeight;
	rectSpan.right = iLeft;

	int iBytesInOnePixel = (int)(m_dBytesPerPixel + 0.5f);
	uint32 start = 0;//bsCurrent->start;
	while(bsCurrent != NULL && rectSpan.right < (iLeft + m_iWidth)) {
		uint32 uSpan = bsCurrent->end - start;
		int iPixels = (int)(uSpan * m_dPixelsPerByte + 0.5f);
		if(iPixels > 0) {
			rectSpan.left = rectSpan.right;
			rectSpan.right += iPixels;
			FillRect(dc, &rectSpan, bsCurrent->color, bFlat);

			start += (int)(iPixels * m_dBytesPerPixel + 0.5f);
		} else {
			float fRed = 0;
			float fGreen = 0;
			float fBlue = 0;
			uint32 iEnd = start + iBytesInOnePixel;
			int iLast = start;
			do {
				float fWeight = (min(bsCurrent->end, iEnd) - iLast) * m_dPixelsPerByte;
				fRed   += GetRValue(bsCurrent->color) * fWeight;
				fGreen += GetGValue(bsCurrent->color) * fWeight;
				fBlue  += GetBValue(bsCurrent->color) * fWeight;
				if(bsCurrent->end > iEnd)
					break;
				iLast = bsCurrent->end;
				bsCurrent = bsCurrent->next;
			} while(bsCurrent != NULL);
			rectSpan.left = rectSpan.right;
			rectSpan.right++;
			FillRect(dc, &rectSpan, fRed, fGreen, fBlue, bFlat);
			start += iBytesInOnePixel;
		}
		while(bsCurrent != NULL && bsCurrent->end < start)
			bsCurrent = bsCurrent->next;
	}
}

void CBarShader::FillRect(CDC *dc, LPRECT rectSpan, COLORREF color, bool bFlat) {
	if(!color || bFlat)
		dc->FillRect(rectSpan, &CBrush(color));
	else
		FillRect(dc, rectSpan, GetRValue(color), GetGValue(color), GetBValue(color), false);
}

void CBarShader::FillRect(CDC *dc, LPRECT rectSpan, float fRed, float fGreen,
						  float fBlue, bool bFlat) {
	if(bFlat) {
		COLORREF color = RGB((int)(fRed + .5f), (int)(fGreen + .5f), (int)(fBlue + .5f));
		dc->FillRect(rectSpan, &CBrush(color));

	} else {
		if (m_Modifiers == NULL || m_used3dlevel!=theApp.glob_prefs->Get3DDepth())
			BuildModifiers();
		RECT rect;
		memcpy(&rect, rectSpan, sizeof(RECT));
		int iTop = rect.top;
		int iBot = rect.bottom;
		int iMax = HALF(m_iHeight);
		for(int i = 0; i < iMax; i++) {
			CBrush cbNew(RGB((int)(fRed * m_Modifiers[i] + .5f), (int)(fGreen * m_Modifiers[i] + .5f), (int)(fBlue * m_Modifiers[i] + .5f)));
			
			rect.top = iTop + i;
			rect.bottom = iTop + i + 1;
			dc->FillRect(&rect, &cbNew);

			rect.top = iBot - i - 1;
			rect.bottom = iBot - i;
			dc->FillRect(&rect, &cbNew);
		}
	}
}
