/*
 *  ReactOS winfile
 *
 *  treeview.c
 *
 *  Copyright (C) 2002  Robert Dickenson <robd@reactos.org>
 *
 *  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.
 */

#ifdef _MSC_VER
#include "stdafx.h"
#else
#define WIN32_LEAN_AND_MEAN     // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <commctrl.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <process.h>
#include <stdio.h>
#endif

#include <windowsx.h>
#include <shellapi.h>
#include <ctype.h>
//#include <assert.h>
//#define ASSERT assert

#include "main.h"
#include "treeview.h"
#include "entries.h"
#include "utils.h"

#include "trace.h"


// Global variables and constants 
// Image_Open, Image_Closed, and Image_Root - integer variables for indexes of the images. 
// CX_BITMAP and CY_BITMAP - width and height of an icon. 
// NUM_BITMAPS - number of bitmaps to add to the image list. 
int Image_Open; 
int Image_Closed; 
int Image_Root; 

#define CX_BITMAP    16
#define CY_BITMAP    16
#define NUM_BITMAPS  3


#if 0
/*
// AddItemToTree - adds items to a tree view control. 
// Returns the handle to the newly added item. 
// hwndTV - handle to the tree view control. 
// lpszItem - text of the item to add. 
// nLevel - level at which to add the item. 

HTREEITEM AddItemToTree(HWND hwndTV, LPTSTR lpszItem, int nLevel)
{ 
    TVITEM tvi; 
    TVINSERTSTRUCT tvins; 
    static HTREEITEM hPrev = (HTREEITEM)TVI_FIRST; 
    static HTREEITEM hPrevRootItem = NULL; 
    static HTREEITEM hPrevLev2Item = NULL; 
    HTREEITEM hti; 
 
    tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN  | TVIF_PARAM; 
    // Set the text of the item. 
    tvi.pszText = lpszItem; 
    tvi.cchTextMax = lstrlen(lpszItem); 
    // Assume the item is not a parent item, so give it an image. 
    tvi.iImage = Image_Root; 
    tvi.iSelectedImage = Image_Root; 
    tvi.cChildren = 1; 

    // Save the heading level in the item's application-defined data area. 
    tvi.lParam = (LPARAM)nLevel; 
 
    tvins.item = tvi; 
    tvins.hInsertAfter = hPrev; 
 
    // Set the parent item based on the specified level. 
    if (nLevel == 1) 
        tvins.hParent = TVI_ROOT; 
    else if (nLevel == 2) 
        tvins.hParent = hPrevRootItem; 
    else 
        tvins.hParent = hPrevLev2Item; 
 
    // Add the item to the tree view control. 
    hPrev = (HTREEITEM)SendMessage(hwndTV, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins); 
 
    // Save the handle to the item. 
    if (nLevel == 1) 
        hPrevRootItem = hPrev; 
    else if (nLevel == 2) 
        hPrevLev2Item = hPrev; 
 
    // The new item is a child item. Give the parent item a 
    // closed folder bitmap to indicate it now has child items. 
    if (nLevel > 1) { 
        hti = TreeView_GetParent(hwndTV, hPrev); 
        tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE; 
        tvi.hItem = hti; 
        tvi.iImage = Image_Closed; 
        tvi.iSelectedImage = Image_Closed; 
        TreeView_SetItem(hwndTV, &tvi); 
    } 
    return hPrev; 
} 
 */
#endif


static void init_output(HWND hWnd)
{
	TCHAR b[16];
	HFONT old_font;
	HDC hdc = GetDC(hWnd);

	if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, _T("1000"), 0, b, 16) > 4)
		Globals.num_sep = b[1];
	else
		Globals.num_sep = _T('.');

	old_font = SelectFont(hdc, Globals.hFont);
	GetTextExtentPoint32(hdc, _T(" "), 1, &Globals.spaceSize);
	SelectFont(hdc, old_font);
	ReleaseDC(hWnd, hdc);
}


HTREEITEM AddEntryToTree(HWND hwndTV, Entry* entry)
{ 
    HTREEITEM hItem = 0;
    TVITEM tvi; 
    TVINSERTSTRUCT tvins; 
    static HTREEITEM hPrev = (HTREEITEM)TVI_FIRST; 
    static HTREEITEM hPrevRootItem = NULL; 
    static HTREEITEM hPrevLev2Item = NULL; 

    //TRACE("AddEntryToTree(level:%u - %s)\n", entry->level, entry->data.cFileName); 
    
    tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM; 
/*
    // Set the text of the item. 
    tvi.pszText = entry->data.cFileName; 
    tvi.cchTextMax = lstrlen(entry->data.cFileName); 
    // Assume the item is not a parent item, so give it an image. 
    tvi.iImage = Image_Root; 
    tvi.iSelectedImage = Image_Root; 
    tvi.cChildren = 1; 
    // Save the heading level in the item's application-defined data area. 
    //tvi.lParam = (LPARAM)entry->level; 
 */
    tvi.pszText = LPSTR_TEXTCALLBACK;
    tvi.cchTextMax = 0;
    tvi.iImage = I_IMAGECALLBACK;
    tvi.iSelectedImage = I_IMAGECALLBACK;
    tvi.cChildren = I_CHILDRENCALLBACK;
    // Save the entry pointer in the item's application-defined data area. 
    tvi.lParam = (LPARAM)entry; 
 
    tvins.item = tvi; 
    tvins.hInsertAfter = hPrev; 
 
    // Set the parent item based on the specified level. 
    if (entry->level == 0) {
        tvins.hParent = TVI_ROOT; 
    } else if (entry->level == 1) {
        tvins.hParent = hPrevRootItem; 
    } else {
        tvins.hParent = hPrevLev2Item; 
        if (hPrevLev2Item) {
            tvins.hParent = entry->up->hTreeItem; 
        }
    }
 
    // Add the item to the tree view control. 
    hPrev = (HTREEITEM)SendMessage(hwndTV, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins); 
 
    // Save the handle to the item. 
    if (entry->level == 0) 
        hPrevRootItem = hPrev; 
    else if (entry->level == 1) 
        hPrevLev2Item = hPrev; 
/* 
    // The new item is a child item. Give the parent item a 
    // closed folder bitmap to indicate it now has child items. 
    if (entry->level > 1) { 
        HTREEITEM hti; 
        hti = TreeView_GetParent(hwndTV, hPrev); 
        tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE; 
        tvi.hItem = hti; 
        tvi.iImage = Image_Closed; 
        tvi.iSelectedImage = Image_Closed; 
        TreeView_SetItem(hwndTV, &tvi); 
    } 
 */
    hItem = hPrev; 
    return hItem;
}


static BOOL InitTreeViewItems(HWND hwndTV) 
{ 
    return TRUE; 
} 
 
// insert treectrl entries after index idx
static void insert_tree_entries(HWND hWnd, Entry* entry, int idx)
{
    static HTREEITEM hItemVisible;
    static int hItemVisibleIdx;

	if (!entry)
		return;

	if (entry->hTreeItem)
		return;

	ShowWindow(hWnd, SW_HIDE);
	for(; entry; entry=entry->next) {
#ifndef _LEFT_FILES
		if (!(entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY))
			continue;
#endif
		// don't display entries "." and ".." in the left pane
        if ((entry->data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) && entry->data.cFileName[0]==_T('.')) {
			if (entry->data.cFileName[1] == _T('\0') ||
               (entry->data.cFileName[1] == _T('.')  && 
                entry->data.cFileName[2] == _T('\0'))) {
				continue;
            }
        }
//		if (idx != -1)
//			idx++;
//		ListBox_InsertItemData(hWnd, idx, entry);
        //TRACE("Adding item %u [level:%u] - %s\n", ++idx, entry->level, entry->data.cFileName); 

	
        if (entry->hTreeItem) continue;


        entry->hTreeItem = AddEntryToTree(hWnd, entry); 
        if (entry->expanded) {
            insert_tree_entries(hWnd, entry->down, idx + 1);
            TreeView_Expand(hWnd, entry->hTreeItem, TVE_EXPAND);
        }
        if (idx > hItemVisibleIdx) {
            hItemVisibleIdx = idx;
            hItemVisible = entry->hTreeItem;
        }
	}
    if (hItemVisible && idx == 0) {
        TreeView_SelectSetFirstVisible(hWnd, hItemVisible);
    }
	ShowWindow(hWnd, SW_SHOW);
}


// InitTreeViewImageLists - creates an image list, adds three bitmaps 
// to it, and associates the image list with a tree view control. 
// Returns TRUE if successful, or FALSE otherwise. 
// hwndTV - handle to the tree view control. 

static BOOL InitTreeViewImageLists(HWND hwndTV) 
{ 
    HIMAGELIST himl;  // handle to image list 
    HBITMAP hbmp;     // handle to bitmap 

    // Create the image list. 
    if ((himl = ImageList_Create(CX_BITMAP, CY_BITMAP, 
        FALSE, NUM_BITMAPS, 0)) == NULL) 
        return FALSE; 

    // Add the open file, closed file, and document bitmaps. 
    hbmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_FOLDER_RED)); 
    Image_Open = ImageList_Add(himl, hbmp, (HBITMAP) NULL); 
    DeleteObject(hbmp); 

    hbmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_FOLDER_OPEN)); 
    Image_Closed = ImageList_Add(himl, hbmp, (HBITMAP) NULL); 
    DeleteObject(hbmp); 

    hbmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_FOLDER)); 
    Image_Root = ImageList_Add(himl, hbmp, (HBITMAP) NULL); 
    DeleteObject(hbmp); 

    // Fail if not all of the images were added. 
    if (ImageList_GetImageCount(himl) < 3) 
        return FALSE; 

    // Associate the image list with the tree view control. 
    TreeView_SetImageList(hwndTV, himl, TVSIL_NORMAL); 

    return TRUE; 
} 

BOOL OnTreeExpanding(HWND hwndTV, NMTREEVIEW* pnmtv)
{ 
    static int expanding;

    Entry* entry = (Entry*)pnmtv->itemNew.lParam;

    if (expanding) return FALSE;
    expanding = TRUE;
    if (entry) {
        insert_tree_entries(hwndTV, entry->down, 0);
//		insert_tree_entries(hwndTV, entry, 0);
    }
    expanding = FALSE;
    return TRUE;
} 

#ifndef _MSC_VER
#define NMTVDISPINFO TV_DISPINFO
#define NMTVDISPINFO TV_DISPINFO
#endif

static void OnGetDispInfo(NMTVDISPINFO* ptvdi)
{
//    static TCHAR buffer[200];
//    LVITEM* pItem = &(ptvdi->item);
//    Entry* entry = (Entry*)pItem->lParam;
    Entry* entry = (Entry*)ptvdi->item.lParam;
    ASSERT(entry);

    if (ptvdi->item.mask & TVIF_CHILDREN ) {
        ptvdi->item.cChildren = 5;
    }
    if (ptvdi->item.mask & TVIF_IMAGE) {
        ptvdi->item.iImage = Image_Root; 
    }
    if (ptvdi->item.mask & TVIF_SELECTEDIMAGE) {
        ptvdi->item.iSelectedImage = Image_Closed; 
    }
    if (ptvdi->item.mask & TVIF_TEXT) {
        ptvdi->item.pszText = entry->data.cFileName; 
        ptvdi->item.cchTextMax = lstrlen(entry->data.cFileName); 
    }
} 

// OnEndLabelEdit - processes the LVN_ENDLABELEDIT notification message. 
// Returns TRUE if the label is changed, or FALSE otherwise. 

static BOOL OnEndLabelEdit(NMTVDISPINFO* ptvdi)
{ 
//    if (ptvdi->item.iItem == -1) 
//        return FALSE; 
 
    // Copy the new label text to the application-defined structure. 
//    lstrcpyn(rgPetInfo[ptvdi->item.iItem].szKind, ptvdi->item.pszText, 10);
    
    return TRUE;
    // To make a more robust application you should send an EM_LIMITTEXT
    // message to the edit control to prevent the user from entering too
    // many characters in the field. 
} 

void UpdateStatus(HWND hWnd, Entry* pEntry)
{
    int file_count = 0;
    int files_size = 0;
    TCHAR suffix[10];
    TCHAR number[50];
    TCHAR Text[260];

    while (pEntry) {
        if (pEntry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
        } else {
            ++file_count;
            files_size += pEntry->data.nFileSizeLow;
        }
        pEntry = pEntry->next;
    };

    _tcscpy(suffix, _T(" bytes"));
    {
        NUMBERFMT numFmt;
        memset(&numFmt, 0, sizeof(numFmt));
        numFmt.NumDigits = 0;
        numFmt.LeadingZero = 0;
        numFmt.Grouping = 3;
        numFmt.lpDecimalSep = _T(".");
        numFmt.lpThousandSep = _T(",");
        numFmt.NegativeOrder = 0;

        wsprintf(Text, _T("%d"), files_size);
        if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, Text, &numFmt, number, sizeof(number))) {
            wsprintf(Text, _T("Total %d file(s) (%s%s)"), file_count, number, suffix);
        } else {
            wsprintf(Text, _T("Total %d file(s) (%d%s)"), file_count, files_size, suffix);
        }
        SendMessage(Globals.hStatusBar, SB_SETTEXT, 1, (LPARAM)Text);
    }
}

////////////////////////////////////////////////////////////////////////////////
static WNDPROC g_orgTreeWndProc;

static LRESULT CALLBACK TreeWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	ChildWnd* child = (ChildWnd*)GetWindowLong(GetParent(hWnd), GWL_USERDATA);
	Pane* pane = (Pane*)GetWindowLong(hWnd, GWL_USERDATA);
	ASSERT(child);

	switch (message) {
#ifndef _NO_EXTENSIONS
	case WM_HSCROLL:
		set_header(pane);
		break;
#endif
    case WM_NOTIFY:
        switch (((LPNMHDR)lParam)->code) { 
//        case TVM_EXPAND: 
//            OnTreeExpand((int)wParam, (HTREEITEM*)lParam);
//            break;
        case TVN_GETDISPINFO: 
            OnGetDispInfo((NMTVDISPINFO*)lParam); 
            break; 
        case TVN_ITEMEXPANDING: 
            return OnTreeExpanding(hWnd, (NMTREEVIEW*)lParam);
            break;
        case TVN_SELCHANGED:

            UpdateStatus(hWnd, child->left.cur->down);
            break;
//            return OnSelChanged((NMTREEVIEW*)lParam);
//           break;
#if 0
        case TVN_SINGLEEXPAND:
            TRACE("TreeWndProc(...) TVN_SINGLEEXPAND\n");
            //lpnmtv = (LPNMTREEVIEW)lParam;
            //return TVNRET_DEFAULT;
//            return TVNRET_SKIPOLD; // Skip default processing of the item being unselected. 
//            return TVNRET_SKIPNEW; // Skip default processing of the item being selected. 
            break;
#endif
        case TVN_ENDLABELEDIT: 
            return OnEndLabelEdit((NMTVDISPINFO*)lParam);
            break;
        }
        return 0;
		break;
	case WM_SETFOCUS:
		child->nFocusPanel = pane == &child->right? 1: 0;
		//ListBox_SetSel(hWnd, TRUE, 1);
		//TODO: check menu items
        if (!child->nFocusPanel) {
            UpdateStatus(hWnd, pane->cur);
        }
		break;
	case WM_KEYDOWN:
		if (wParam == VK_TAB) {
			//TODO: SetFocus(Globals.hDriveBar)
			SetFocus(child->nFocusPanel ? child->left.hWnd: child->right.hWnd);
		}
        break;
	}
	return CallWindowProc(g_orgTreeWndProc, hWnd, message, wParam, lParam);
}

// CreateTreeView - creates a tree view control. 
// Returns the handle to the new control if successful, or NULL otherwise. 
// hwndParent - handle to the control's parent window. 

static HWND CreateTreeView(HWND hwndParent, int id) 
{ 
    RECT rcClient;
    HWND hwndTV;
 
    // Get the dimensions of the parent window's client area, and create the tree view control. 
    GetClientRect(hwndParent, &rcClient); 
    hwndTV = CreateWindowEx(0, WC_TREEVIEW, _T("Tree View"), 
        WS_VISIBLE | WS_CHILD | WS_BORDER | WS_EX_CLIENTEDGE | TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT,
        0, 0, rcClient.right, rcClient.bottom, 
        hwndParent, (HMENU)id, hInst, NULL); 
    // Initialize the image list, and add items to the control. 
    if (!InitTreeViewImageLists(hwndTV) || !InitTreeViewItems(hwndTV)) { 
        DestroyWindow(hwndTV); 
        return NULL; 
    } 
    return hwndTV;
} 

//void create_tree_window(HWND parent, Pane* pane, int id, int id_header, LPTSTR lpszFileName)
void CreateTreeWnd(HWND parent, Pane* pane, int id)
{
	static int s_init = 0;
	Entry* entry = pane->root;

	pane->treePane = 1;

    pane->hWnd = CreateTreeView(parent, id);
    SetWindowLong(pane->hWnd, GWL_USERDATA, (LPARAM)pane);
	g_orgTreeWndProc = SubclassWindow(pane->hWnd, TreeWndProc);
	SendMessage(pane->hWnd, WM_SETFONT, (WPARAM)Globals.hFont, FALSE);

	 // insert entries into treectrl
    if (entry) {
		insert_tree_entries(pane->hWnd, entry, 0);
    }

	// calculate column widths
	if (!s_init) {
		s_init = 1;
		init_output(pane->hWnd);
	}
//	calc_widths(pane, TRUE);
//#ifndef _NO_EXTENSIONS
//	pane->hwndHeader = create_header(parent, pane, id_header);
//#endif
}
