/* PROJECT:         ReactOS Downloader
 * LICENSE:         GPL - See COPYING in the top level directory
 * FILE:            base/applications/downloader/xml.c
 * PURPOSE:         Main program
 * PROGRAMMERS:     Maarten Bosma
 */

#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include <stdio.h>
#include "resources.h"
#include "structures.h"

#define XML_PATH "C:\\ReactOS\\system32\\downloader.xml"

HWND hCategories, hApps, hDownloadButton, hUpdateButton, hHelpButton;
HBITMAP hLogo, hUnderline;
WCHAR* DescriptionHeadline = L"";
WCHAR* DescriptionText = L"";

struct Category Root;
struct Application* SelectedApplication;

INT_PTR CALLBACK DownloadProc (HWND, UINT, WPARAM, LPARAM);
BOOL ProcessXML (const char* filename, struct Category* Root);
VOID FreeTree (struct Category* Node);
WCHAR Strings [STRING_COUNT][MAX_STRING_LENGHT];

void ShowMessage (WCHAR* title, WCHAR* message)
{
	HWND hwnd;
	DescriptionHeadline = title;
	DescriptionText = message;

	hwnd = GetParent(hCategories);
	InvalidateRect(hwnd,NULL,TRUE); 
	UpdateWindow(hwnd);
}

void AddItems (HWND hwnd, struct Category* Category, struct Category* Parent)
{ 
	TV_INSERTSTRUCTW Insert; 

	Insert.item.lParam = (UINT)Category;
	Insert.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;;
	Insert.item.pszText = Category->Name;
	Insert.item.cchTextMax = lstrlenW(Category->Name); 
	Insert.item.iImage = Category->Icon;
	Insert.item.iSelectedImage = Category->Icon;
	Insert.hInsertAfter = TVI_LAST;
	Insert.hParent = Category->Parent ? Category->Parent->TreeviewItem : TVI_ROOT;

	Category->TreeviewItem = (HTREEITEM)SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&Insert);

	if(Category->Next)
		AddItems (hwnd,Category->Next,Parent);

	if(Category->Children)
		AddItems (hwnd,Category->Children,Category);
}

void CategoryChoosen (HWND hwnd, struct Category* Category)
{
	struct Application* CurrentApplication;
	TV_INSERTSTRUCTW Insert;
	SelectedApplication = NULL;
	
	if(Category->Children && !Category->Apps)
		ShowMessage(Category->Name, Strings[IDS_CHOOSE_SUB]);
	else if(!Category->Children && Category->Apps)
		ShowMessage(Category->Name, Strings[IDS_CHOOSE_APP]);
	else if(Category->Children && Category->Apps)
		ShowMessage(Category->Name, Strings[IDS_CHOOSE_BOTH]);
	else
		ShowMessage(Category->Name, Strings[IDS_NO_APPS]);

	(void)TreeView_DeleteItem(hwnd, TVI_ROOT);
	(void)TreeView_DeleteItem(hwnd, TVI_ROOT); // Delete twice to bypass bug in windows 

	Insert.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE;
	Insert.hInsertAfter = TVI_LAST;
	Insert.hParent = TVI_ROOT;
	Insert.item.iImage = 0;

	CurrentApplication = Category->Apps;

	while(CurrentApplication)
	{
		Insert.item.lParam = (UINT)CurrentApplication;
		Insert.item.pszText = CurrentApplication->Name;
		Insert.item.cchTextMax = lstrlenW(CurrentApplication->Name); 
		SendMessage(hwnd, TVM_INSERTITEM, 0, (LPARAM)&Insert);
		CurrentApplication = CurrentApplication->Next;
	}
}

BOOL SetupControls (HWND hwnd)
{
	TV_INSERTSTRUCTW Insert = {0};
	HIMAGELIST hImageList;
	HINSTANCE hInstance = GetModuleHandle(NULL);

	// Parse the XML file
	if (ProcessXML (XML_PATH, &Root) == FALSE)
		return FALSE;

	// Set up the controls
	hCategories = CreateWindowExW(0, WC_TREEVIEWW, L"Categories", WS_CHILD|WS_VISIBLE|WS_BORDER|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|TVS_SHOWSELALWAYS, 
							0, 0, 0, 0, hwnd, NULL, hInstance, NULL);

	hApps = CreateWindowExW(0, WC_TREEVIEWW, L"Applications", WS_CHILD|WS_VISIBLE|WS_BORDER|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS|TVS_SHOWSELALWAYS, 
							0, 0, 0, 0, hwnd, NULL, hInstance, NULL);

	hLogo = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_LOGO));
	hUnderline = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_UNDERLINE));

	hHelpButton = CreateWindowW (L"Button", L"", WS_CHILD|WS_VISIBLE|BS_BITMAP, 550, 10, 40, 40, hwnd, 0, hInstance, NULL);
	hUpdateButton = CreateWindowW (L"Button", L"", WS_CHILD|WS_VISIBLE|BS_BITMAP, 500, 10, 40, 40, hwnd, 0, hInstance, NULL);
	hDownloadButton = CreateWindowW (L"Button", L"", WS_CHILD|WS_VISIBLE|BS_BITMAP, 330, 505, 140, 33, hwnd, 0, hInstance, NULL);

	SendMessageW(hHelpButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)(HANDLE)LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_HELP)));
	SendMessageW(hUpdateButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP,(LPARAM)(HANDLE)LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_UPDATE)));
	SendMessageW(hDownloadButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP,(LPARAM)(HANDLE)LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_DOWNLOAD)));

	// Set deflaut entry for hApps
	Insert.item.mask = TVIF_TEXT|TVIF_IMAGE;
	Insert.item.pszText = Strings[IDS_CHOOSE_CATEGORY];
	Insert.item.cchTextMax = lstrlenW(Strings[IDS_CHOOSE_CATEGORY]); 
	Insert.item.iImage = 0;
	SendMessage(hApps, TVM_INSERTITEM, 0, (LPARAM)&Insert); 

	// Create Tree Icons
	hImageList = ImageList_Create(16, 16, ILC_COLORDDB, 1, 1);
	SendMessageW(hCategories, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)(HIMAGELIST)hImageList);
	SendMessageW(hApps, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)(HIMAGELIST)hImageList);

	ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_0)), NULL); 
	ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_1)), NULL); 
	ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_2)), NULL); 
	ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_3)), NULL); 
	ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_4)), NULL); 
	ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_5)), NULL); 
	ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_6)), NULL); 
	ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_7)), NULL); 
	ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TREEVIEW_ICON_8)), NULL); 

	// Fill the TreeViews
	AddItems (hCategories, Root.Children, NULL);

	return TRUE;
}

static void ResizeControl (HWND hwnd, int x1, int y1, int x2, int y2)
{
	// Make resizing a little easier
	MoveWindow(hwnd, x1, y1, x2-x1, y2-y1, TRUE);
}

static void DrawBitmap (HDC hdc, int x, int y, HBITMAP hBmp)
{
	BITMAP bm;
	HDC hdcMem = CreateCompatibleDC(hdc);

	SelectObject(hdcMem, hBmp);
	GetObject(hBmp, sizeof(bm), &bm);
	TransparentBlt(hdc, x, y, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, 0xFFFFFF);

	DeleteDC(hdcMem);
}

static void DrawDescription (HDC hdc, RECT DescriptionRect)
{
	int i;
	HFONT Font;
	RECT Rect = {DescriptionRect.left+5, DescriptionRect.top+3, DescriptionRect.right-2, DescriptionRect.top+22};

	// Backgroud
	Rectangle(hdc, DescriptionRect.left, DescriptionRect.top, DescriptionRect.right, DescriptionRect.bottom);

	// Underline
	for (i=DescriptionRect.left+1;i<DescriptionRect.right-1;i++)
		DrawBitmap(hdc, i, DescriptionRect.top+22, hUnderline); // less code then stretching ;)

	// Headline
	Font = CreateFont(-16 , 0, 0, 0, FW_EXTRABOLD, FALSE, FALSE, FALSE, ANSI_CHARSET, 
						OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, L"Arial");
	SelectObject(hdc, Font);
	DrawTextW(hdc, DescriptionHeadline, lstrlenW(DescriptionHeadline), &Rect, DT_SINGLELINE|DT_NOPREFIX);
	DeleteObject(Font);

	// Description
	Font = CreateFont(-13 , 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, 
						OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, L"Arial");
	SelectObject(hdc, Font);
	Rect.top += 40;
	Rect.bottom = DescriptionRect.bottom-2;
	DrawTextW(hdc, DescriptionText, lstrlenW(DescriptionText), &Rect, DT_WORDBREAK|DT_NOPREFIX); // ToDo: Call TabbedTextOut to draw a nice table
	DeleteObject(Font);

}

LRESULT CALLBACK WndProc (HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
	static RECT DescriptionRect;

	switch (Message)
	{
		case WM_CREATE:
		{
			if(!SetupControls(hwnd))
				return -1;
			ShowMessage(Strings[IDS_WELCOME_TITLE], Strings[IDS_WELCOME]);
		} 
		break;

		case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC hdc = BeginPaint(hwnd, &ps);
			HDC BackbufferHdc = CreateCompatibleDC(hdc);
			HBITMAP BackbufferBmp = CreateCompatibleBitmap(hdc, ps.rcPaint.right, ps.rcPaint.bottom);
			SelectObject(BackbufferHdc, BackbufferBmp);
			
			FillRect(BackbufferHdc, &ps.rcPaint, CreateSolidBrush(RGB(235,235,235)));
			DrawBitmap(BackbufferHdc, 10, 12, hLogo);
			DrawDescription(BackbufferHdc, DescriptionRect);
			
			BitBlt(hdc, 0, 0, ps.rcPaint.right, ps.rcPaint.bottom, BackbufferHdc, 0, 0, SRCCOPY);
			DeleteObject(BackbufferBmp);
			DeleteDC(BackbufferHdc);
			EndPaint(hwnd, &ps);
		}
		break;

		case WM_COMMAND:
		{
			if(HIWORD(wParam) == BN_CLICKED)
			{
				if (lParam == (LPARAM)hDownloadButton)
				{
					if(SelectedApplication)
						DialogBoxW(GetModuleHandle(NULL), MAKEINTRESOURCEW(IDD_DOWNLOAD), 0, DownloadProc);
					else
						ShowMessage(Strings[IDS_NO_APP_TITLE], Strings[IDS_NO_APP]);
				}
				else if (lParam == (LPARAM)hUpdateButton)
				{
					ShowMessage(Strings[IDS_UPDATE_TITLE], Strings[IDS_UPDATE]);
				}
				else if (lParam == (LPARAM)hHelpButton)
				{
					ShowMessage(Strings[IDS_HELP_TITLE], Strings[IDS_HELP]);
				}
			}
		}
		break;

		case WM_NOTIFY:
		{
			LPNMHDR data = (LPNMHDR)lParam;
			if(data->code == TVN_SELCHANGED)
			{
				if(data->hwndFrom == hCategories) 
				{
					struct Category* Category = (struct Category*) ((LPNMTREEVIEW)lParam)->itemNew.lParam;
					CategoryChoosen (hApps, Category);
				}
				else if(data->hwndFrom == hApps) 
				{
					SelectedApplication = (struct Application*) ((LPNMTREEVIEW)lParam)->itemNew.lParam;
					if(SelectedApplication)
						ShowMessage(SelectedApplication->Name, SelectedApplication->Description);
				}
			}
		}
		break;

		case WM_SIZING:
		{
			LPRECT pRect = (LPRECT)lParam;
			if (pRect->right-pRect->left < 400)
				pRect->right = pRect->left + 400;

			if (pRect->bottom-pRect->top < 300)
				pRect->bottom = pRect->top + 300;
		}
		break;

		case WM_SIZE:
		{
			int Split_Hozizontal = (HIWORD(lParam)-(45+60))/2 + 60;
			int Split_Vertical = 200;

			ResizeControl(hCategories, 10, 60, Split_Vertical, HIWORD(lParam)-10);
			ResizeControl(hApps, Split_Vertical+5, 60, LOWORD(lParam)-10, Split_Hozizontal);
			RECT Rect = {Split_Vertical+5, Split_Hozizontal+5, LOWORD(lParam)-10, HIWORD(lParam)-50};
			DescriptionRect = Rect;

			MoveWindow(hHelpButton, LOWORD(lParam)-50, 10, 40, 40, TRUE);
			MoveWindow(hUpdateButton, LOWORD(lParam)-100, 10, 40, 40, TRUE);
			MoveWindow(hDownloadButton, (Split_Vertical+LOWORD(lParam))/2-70, HIWORD(lParam)-45, 140, 35, TRUE);
		}
		break;

		case WM_DESTROY:
		{
			DeleteObject(hLogo);	
			if(Root.Children)
				FreeTree(Root.Children);
			PostQuitMessage(0);
			return 0;
		}
		break;
	}

	return DefWindowProc (hwnd, Message, wParam, lParam);
}

INT WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInst,
					LPSTR lpCmdLine, INT nCmdShow)
{
	HWND hwnd;
	int i;
	WNDCLASSEXW WndClass = {0};
	MSG msg;

	InitCommonControls();

	// Load strings
	for(i=0; i<STRING_COUNT; i++)
		LoadStringW(hInstance, i, Strings[i], MAX_STRING_LENGHT); // if you know a better method please tell me. 

	// Create the window
	WndClass.cbSize			= sizeof(WNDCLASSEX); 
	WndClass.lpszClassName	= L"Downloader";
	WndClass.lpfnWndProc	= WndProc;
	WndClass.hInstance		= hInstance;
	WndClass.style			= CS_HREDRAW | CS_VREDRAW;
	WndClass.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN));
	WndClass.hCursor		= LoadCursor(NULL, IDC_ARROW);

	RegisterClassExW(&WndClass);

	hwnd = CreateWindowW(L"Downloader", 
						Strings[IDS_WINDOW_TITLE],
						WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN,
						CW_USEDEFAULT,  
						CW_USEDEFAULT,   
						600, 550, 
						NULL, NULL,
						hInstance, 
						NULL);

	// Show it
	ShowWindow(hwnd, SW_SHOW);
	UpdateWindow(hwnd);

	// Message Loop
	while(GetMessage(&msg,NULL,0,0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return 0;
}
