//this file is part of eMule
//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include "stdafx.h"
#include "emule.h"
#include "emuledlg.h"
#include "SpeedGraphWnd.h"
#include "preferences.h"
#include "BandWidthControl.h"
#include "MemDC.h"
#include "DropTarget.h"
#include "ini2.h"

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

#define WINWIDTH 48
#define WINBORDER 2
#define DRAWWIDTH (WINWIDTH - 2*WINBORDER)

#ifndef PI
#define PI 3.141592653589793f
#endif

CSpeedGraph::CSpeedGraph()
{
	TrafficStats = NULL;
	TrafficEntries = 0;
	Color1 = 0;
	Color2 = 0;
	lastvalid = 0;
	//type = GT_RECT;
	overlap = false;
	m_ui_MaxAmount = 0;
}

CSpeedGraph::~CSpeedGraph()
{
	if(TrafficStats)
		delete[] TrafficStats; 
}

bool CSpeedGraph::Set_TrafficValue(uint32 value) 
{
	// Shift whole array 1 step to left and calculate local maximum
	for(uint_ptr x = 0; x < TrafficEntries - 1; x++)
		TrafficStats[x] = TrafficStats[x + 1];

	TrafficStats[TrafficEntries - 1] = (value >= m_ui_MaxAmount)?m_ui_MaxAmount:value;
	if(value)
		lastvalid = TrafficEntries;
	else if(lastvalid)
		--lastvalid;
	else
		return false;
	return true;
}

void CSpeedGraph::InitArray()
{
	lastvalid = 0;
	memset(TrafficStats, 0, TrafficEntries*sizeof(TrafficStats[0]));
}

void CSpeedGraph::Init(/*EGraphType _type, */bool _overlap, const CRect&_region, INT unit, ARGB c_Color1, ARGB c_Color2) 
{
	//type = _type;
	overlap = _overlap;
	region = _region;
	uint_ptr oldTrafficEntries = TrafficEntries;
	//if(type == GT_RECT){
		if(unit > 1)
			TrafficEntries = region.Width() / unit;
		else
			TrafficEntries = region.Width();
	/*}
	else /*if(type == GT_ELLIPSE)*{
		if(unit > 1)
			TrafficEntries = 360 / unit;
		else
			TrafficEntries = 60;
	}*/
	if(TrafficEntries < 10)
		TrafficEntries = 10;
	++TrafficEntries;
	Color1 = c_Color1;
	Color2 = c_Color2;
	if(oldTrafficEntries != TrafficEntries){
		delete[] TrafficStats;
		TrafficStats = new uint32[TrafficEntries];
		InitArray();
	}
}

void CSpeedGraph::DrawGraph(Graphics&g){
	if(lastvalid){
		g.SetCompositingMode(overlap?CompositingModeSourceOver:CompositingModeSourceCopy);
		Point*p = new Point[lastvalid + 2];
		//if(type == GT_RECT){
			INT unit = region.Width() / (TrafficEntries - 1);
			LinearGradientBrush brush(
				Point(0, region.top - 1),
				Point(0, region.bottom),
				Color(Color1),
				Color(Color2));
			p[lastvalid].X = region.left + (lastvalid-1)*unit;
			p[lastvalid].Y = region.bottom;
			p[lastvalid+1].X = region.left;
			p[lastvalid+1].Y = region.bottom;
			for (int i = 0;i < lastvalid; ++i){
				p[i].X = region.left + i*unit;
				p[i].Y = region.bottom - TrafficStats[i] * region.Height() / m_ui_MaxAmount;
			}
			g.FillPolygon(&brush, p, lastvalid + 2);
		/*}
		else{
			GraphicsPath path;
			INT a = (region.right - region.left)/2;
			INT b = (region.bottom - region.top)/2;
			Point centerpoint(region.left + a, region.top + b);
			if(a != b){
				for (int i = 0;i < lastvalid; ++i){
					float theta = i * (2 * PI) / (TrafficEntries - 1);
					float sint = sin(theta);
					float cost = cos(theta);
					float r = a * b * TrafficStats[i] / sqrt(b * b * sint * sint + a * a * cost * cost) / m_ui_MaxAmount;
					p[i].X = centerpoint.X - (INT)(cost * r);
					p[i].Y = centerpoint.Y + (INT)(sint * r);
				}
			}
			else{
				for (int i = 0;i < lastvalid; ++i){
					float theta = i * (2 * PI) / (TrafficEntries - 1);
					float sint = sin(theta);
					float cost = cos(theta);
					float r = (float)(a * TrafficStats[i]) / m_ui_MaxAmount;
					p[i].X = centerpoint.X - (INT)(cost * r);
					p[i].Y = centerpoint.Y + (INT)(sint * r);
				}
			}
			p[lastvalid] = p[0];

			path.AddPolygon(p, lastvalid + 1);
			PathGradientBrush pthGrBrush(&path);
			pthGrBrush.SetCenterPoint(centerpoint);
			pthGrBrush.SetCenterColor(Color(Color2));
			Color colors[] = {Color(Color1)};
			INT count = 1;
			pthGrBrush.SetSurroundColors(colors, &count);
			g.FillEllipse(&pthGrBrush, region.left, region.top, region.Width(), region.Height());
		}*/
		delete [] p;
	}
}

// CSpeedGraphWnd dialog
//IMPLEMENT_DYNAMIC(CSpeedGraphWnd, CDialog)

BEGIN_MESSAGE_MAP(CSpeedGraphWnd, CModDialog)
ON_WM_CONTEXTMENU()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONDBLCLK()
ON_WM_PAINT()
ON_WM_DESTROY()
ON_WM_SHOWWINDOW()
END_MESSAGE_MAP()

CSpeedGraphWnd::CSpeedGraphWnd()
	: CModDialog(CSpeedGraphWnd::IDD) // NEO: MLD - [ModelesDialogs] <-- Xanatos --
{
	blendPixelFunction.BlendOp = AC_SRC_OVER;
	blendPixelFunction.BlendFlags = 0;
	blendPixelFunction.SourceConstantAlpha = 255;
	blendPixelFunction.AlphaFormat = AC_SRC_ALPHA;
	m_pDropTarget = new CMainFrameDropTarget();
}

CSpeedGraphWnd::~CSpeedGraphWnd()
{
	delete m_pDropTarget;
}

void CSpeedGraphWnd::LoadConfiguration(LPCTSTR pszFilePath){
	CIni ini(pszFilePath, _T("Config"));
	blendPixelFunction.SourceConstantAlpha = ini.GetInt(L"Alpha", 255);
	CString strBmpFileName = ini.GetString(_T("Background"));
	CString colorstr = ini.GetString(L"Color", NULL, L"Text");
	ColorText = _tcstoul(colorstr, NULL, 16);
	textoverlap = ini.GetBool(L"Overlap", true);
	ptText = ini.GetPoint(L"Position");
	TextSize = ini.GetFloat(L"FontSize", 12.0);
	m_bgimg.DeleteObject();
	if(!strBmpFileName.IsEmpty()){
		CString strBmpFilePath;
		if (PathIsRelative(strBmpFileName)){
			TCHAR szConfigDir[MAX_PATH];
			_tcsncpy(szConfigDir, pszFilePath, _countof(szConfigDir) - 1);
			szConfigDir[_countof(szConfigDir) - 1] = _T('\0');
			LPTSTR pszFileName = _tcsrchr(szConfigDir, _T('\\'));
			if (pszFileName != NULL){
				*(pszFileName + 1) = _T('\0');
				strBmpFilePath.Format(_T("%s%s"), szConfigDir, strBmpFileName);
			}
		}
		else
			strBmpFilePath = strBmpFileName;

		if(!strBmpFilePath.IsEmpty() && m_bgimg.LoadImage(strBmpFilePath)){
			bool overlap = ini.GetBool(L"Overlap", true, L"Upload");
			//int type = ini.GetInt(L"Type", 0);
			colorstr = ini.GetString(L"Color1");
			ARGB color1 = _tcstoul(colorstr, NULL, 16);
			colorstr = ini.GetString(L"Color2");
			ARGB color2 = _tcstoul(colorstr, NULL, 16);
			int unit = ini.GetInt(L"Unit", 1);
			CRect rect = ini.GetRect(L"Rect");
			m_co_UpTrafficGraph.Init(/*type==0?GT_RECT:GT_ELLIPSE, */overlap, rect, unit, color1?color1:_ARGB(144, 255, 130, 0), color2?color2:_ARGB(144, 255, 255, 80));

			overlap = ini.GetBool(L"Overlap", true, L"Download");
			//type = ini.GetInt(L"Type", 0);
			colorstr = ini.GetString(L"Color1");
			color1 = _tcstoul(colorstr, NULL, 16);
			colorstr = ini.GetString(L"Color2");
			color2 = _tcstoul(colorstr, NULL, 16);
			unit = ini.GetInt(L"Unit", 1);
			rect = ini.GetRect(L"Rect");
			m_co_DownTrafficGraph.Init(/*type==0?GT_RECT:GT_ELLIPSE, */overlap, rect, unit, color1?color1:_ARGB(144, 255, 130, 0), color2?color2:_ARGB(144, 255, 255, 80));
			DrawWindow();
			return;
		}
	}
	CRect rect(WINBORDER, WINBORDER, WINWIDTH - WINBORDER, WINWIDTH - WINBORDER);
	m_co_UpTrafficGraph.Init(/*GT_RECT, */true, rect, 1, _ARGB(144, 255, 130, 0), _ARGB(144, 255, 255, 80));
	m_co_DownTrafficGraph.Init(/*GT_RECT, */true, rect, 1, _ARGB(144, 0, 130, 255), _ARGB(144, 80, 255, 255));
	CreateDefaultImage();
	DrawWindow();
};

void CSpeedGraphWnd::CreateDefaultImage()
{
	HDC hdc = ::GetDC(HWND_DESKTOP);
	CDC* pdc = CDC::FromHandle(hdc);
	CDC dc;
	dc.CreateCompatibleDC(pdc);
	m_bgimg.CreateCompatibleBitmap(pdc, WINWIDTH, WINWIDTH);
	m_bgimg.SetBitmapDimension(WINWIDTH, WINWIDTH);
	dc.SelectObject(m_bgimg);
	Graphics g(dc.GetSafeHdc());
	g.SetCompositingMode(CompositingModeSourceCopy);
	SolidBrush borderbrush(Color(0xccd8d8ff));
	Pen pen(&borderbrush);
	g.DrawRectangle(&pen, 0, 0, WINWIDTH - 1, WINWIDTH - 1);
	LinearGradientBrush brush(
		Point(0, 0),
		Point(0, WINWIDTH - WINBORDER),
		Color(0x7fffffff),
		Color(0x3fcccccc));
	g.FillRectangle(&brush, WINBORDER, WINBORDER, DRAWWIDTH, DRAWWIDTH);
	::ReleaseDC(HWND_DESKTOP, hdc);
}

BOOL CSpeedGraphWnd::OnInitDialog()
{
	CModDialog::OnInitDialog();
	int nScreenWidth = ::GetSystemMetrics(SM_CXSCREEN);
	int nScreenHeight = ::GetSystemMetrics(SM_CYSCREEN);
	int left = (thePrefs.speedgraphwindowLeft * nScreenWidth + nScreenWidth/2) / 100;
	int top = (thePrefs.speedgraphwindowTop * nScreenHeight + nScreenHeight/2) / 100;

	SetWindowPos(NULL, left, top, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE);

	// Traffic graph
	SetUSpeedMeterRange((uint32)thePrefs.GetMaxGraphUploadRate());
	SetDSpeedMeterRange((uint32)thePrefs.GetMaxGraphDownloadRate());

	VERIFY( m_pDropTarget->Register(this) );

	StrText = L"0.0/0.0";
	LoadConfiguration(thePrefs.speedgraphwindowConfiguration);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

// X-Ray :: Speedgraph :: Start
void CSpeedGraphWnd::Update_TrafficGraph()
{
	if(!CemuleDlg::IsRunning())
		return;
#if 0
	static float u=0;
	static float d=0;
	uint32 eMuleIn = (sin(u+=.2)+1)*m_co_DownTrafficGraph.m_ui_MaxAmount/2;
	uint32 eMuleOut = (cos(d+=.2)+1)*m_co_UpTrafficGraph.m_ui_MaxAmount/2;
#else
	uint32 eMuleIn = theApp.emuledlg->GetDownloadDatarate();
	uint32 eMuleOut = theApp.emuledlg->GetUploadDatarate();
#endif
	bool refresho = m_co_UpTrafficGraph.Set_TrafficValue(eMuleOut);
	bool refreshi = m_co_DownTrafficGraph.Set_TrafficValue(eMuleIn);

	if(refresho || refreshi)
	{
		if(ColorText != 0)
			StrText.Format(L"%.1f/%.1f", eMuleOut / 1024.0, eMuleIn / 1024.0);
		DrawWindow();
	}
}
// X-Ray :: Speedgraph :: End

void CSpeedGraphWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
	CModDialog::OnLButtonDown(nFlags, point);
	PostMessage(WM_NCLBUTTONDOWN,HTCAPTION,MAKELPARAM(point.x,point.y));
}

void CSpeedGraphWnd::OnContextMenu(CWnd* /*pWnd*/, CPoint point){
	theApp.emuledlg->ShowToolPopup(true, true);
}

void CSpeedGraphWnd::DrawWindow(){ // X: [GPUI] - [GDI Plus UI]
	HDC hdc = ::GetDC(HWND_DESKTOP);
	CDC* pdc = CDC::FromHandle(hdc);
	CDC bmpdc;
	bmpdc.CreateCompatibleDC(pdc);
	bmpdc.SelectObject(m_bgimg);

	CSize szWindow = m_bgimg.GetBitmapDimension();

	CDC layerdc;
	layerdc.CreateCompatibleDC(pdc);
	CBitmap layerbitmap;
	layerbitmap.CreateCompatibleBitmap(pdc, szWindow.cx, szWindow.cy);
	layerdc.SelectObject(layerbitmap);
	layerdc.BitBlt(0, 0, szWindow.cx, szWindow.cy, &bmpdc, 0, 0, SRCCOPY);
	Graphics g(layerdc.GetSafeHdc());
	m_co_UpTrafficGraph.DrawGraph(g);
	m_co_DownTrafficGraph.DrawGraph(g);
	if(!StrText.IsEmpty()){
		g.SetCompositingMode(textoverlap?CompositingModeSourceOver:CompositingModeSourceCopy);
		StringFormat format;
		format.SetAlignment(StringAlignmentFar);
		g.DrawString(StrText, StrText.GetLength(), &Font(_T("Tahoma"), TextSize), PointF(ptText.x, ptText.y), &format, &SolidBrush(Color(ColorText)));
	}

	POINT ptSrc = {0,0};
	UpdateLayeredWindow(NULL, NULL, &szWindow, &layerdc,&ptSrc, 0, &blendPixelFunction, ULW_ALPHA);
	::ReleaseDC(HWND_DESKTOP, hdc);
}

void CSpeedGraphWnd::OnDestroy()
{
	m_pDropTarget->Revoke();
	RECT rcDlg;
	GetWindowRect(&rcDlg);
	int nScreenWidth = ::GetSystemMetrics(SM_CXSCREEN);
	int nScreenHeight = ::GetSystemMetrics(SM_CYSCREEN);
	thePrefs.speedgraphwindowLeft = rcDlg.left * 100 / nScreenWidth;
	thePrefs.speedgraphwindowTop = rcDlg.top * 100 / nScreenHeight;
	CModDialog::OnDestroy();
}

void CSpeedGraphWnd::OnLButtonDblClk(UINT nFlags, CPoint point)
{
	CModDialog::OnLButtonDblClk(nFlags, point);
	if (theApp.emuledlg->IsIconic())
		theApp.emuledlg->ShowWindow(SW_RESTORE);
	else if(!theApp.emuledlg->IsWindowVisible())
		theApp.emuledlg->RestoreWindow();
	else
		theApp.emuledlg->SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0);
}

void CSpeedGraphWnd::OnShowWindow(BOOL bShow, UINT nStatus)
{
	CModDialog::OnShowWindow(bShow, nStatus);
	if (bShow == FALSE){
		m_co_UpTrafficGraph.InitArray();
		m_co_DownTrafficGraph.InitArray();
		StrText = L"0.0/0.0";
		DrawWindow();
	}
}
