/******************************************************************************
 *
 * Copyright (c) 1999	TOSHIYUKI ARAI. ALL RIGHTS RESERVED. 
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *  
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *	ClassTreeView.cpp
 *
 *****************************************************************************/


// SOL++2000 
// 2000.02.18


#include <sol\stdio.h>
#include <sol\Profile.h>
#include <sol\FileFinder.h>
#include "Pair.h"
#include "ClassTreeView.h"
#include <sol\ClientDC.h>

#include "resource.h"


ClassTreeView::ClassTreeView(Application& applet, const char* name, Args& args)
	:ApplicationView(applet, name, args)
{
	initialized = FALSE;
	root  = NULL;
	pairList = new LinkedList();

	Args ar;

	int fontSize = -14;
	if(GetSystemMetrics(SM_CXSCREEN) < 1024 &&
			   GetSystemMetrics(SM_CYSCREEN) < 768) {
		fontSize = -12;
	}
	ClientDC dc(this);
	UINT charSet = dc.getTextCharsetInfo(NULL);

	ar.reset();
	ar.set(XmNheight, fontSize);
	ar.set(XmNcharSet, (ulong)charSet);
	font.create(ar);
	
	ar.reset();
	ar.set(XmNexStyle, (ulong)WS_EX_CONTROLPARENT);
	ar.set(XmNdirection, SplitPane::HORIZONTAL);
	hpanedw.create(this, "", ar);

	ar.reset();
	ar.set(XmNexStyle, (ulong)WS_EX_CLIENTEDGE);
	ar.set(XmNstyle, (ulong)(WS_GROUP|\
			TVS_HASBUTTONS|TVS_SHOWSELALWAYS|
			TVS_HASLINES|TVS_LINESATROOT));
	treev.create(&hpanedw, "", ar);
	treev.setFont(&font);
	hpanedw.add(&treev);

	treev.addCallback(XmNselChangedCallback, this,
			(Callback)&ClassTreeView::selChanged, NULL);

	addCallback(XmNmenuCallback, ID_DIR, this, (Callback)&ClassTreeView::selectDir, NULL);
	addCallback(XmNmenuCallback, ID_EXIT, this, (Callback)&ClassTreeView::exit, NULL);
  
	addEventHandler(WM_CLOSE, this, (Handler)&ClassTreeView::close, NULL);

	ar.reset();
	richText.create(&hpanedw, "", ar);
	richText.setFont(&font);

	richText.exLimitText(500*1000);
	hpanedw.add(&richText);

	ar.reset();
	dirDlg.create(this, "Directory", ar);

	restorePlacement();
	Profile profile;
	classLibrary[0] = Zero;
	profile.get("PATH", "ClassLibrary", "", classLibrary, 
				sizeof(classLibrary)-1);

	if (strlen(classLibrary)) {
		startup(classLibrary);
	}
}


ClassTreeView::~ClassTreeView()
{
	delete root;
	delete pairList;
}


long ClassTreeView::close(Event& event)
{
	savePlacement();
	return defaultProc(event);
}


void ClassTreeView::startup(char* dir)
{
	if(dir) {
		delete root;
		root = new Node(dir, 0);
		delete pairList;
		pairList = new LinkedList();
		setSysCursor(IDC_WAIT);

		findClass(dir);
		//		DumpPairList();

		if(root) {
			treev.deleteAllItems();

			char* name = root -> getName();
			HTREEITEM ritem = treev.addItem(NULL, TVI_ROOT, name);
			treev.buildTree(ritem, name, pairList);
			treev.sortAllChildren(ritem); 
			treev.expandChildren(ritem, TVE_EXPAND);
		}
		setSysCursor(IDC_ARROW);
	}
}


void ClassTreeView::selectDir(Action& action)
{
	dirDlg.popup(action);
	if(action.getResult() == IDOK) {
		char* dir = dirDlg.getDir();
		Printf("Directory %s\r\n", dir);
		char text[_MAX_PATH+40];
		sprintf(text, "%s - ClassTreeView", dir);
		setText(text);

		if(dir) {
			delete root;
			root = new Node(dir, 0);
			delete pairList;
			pairList = new LinkedList();
			setSysCursor(IDC_WAIT);

			if (classLibrary[0]) {
				findClass(classLibrary);
			}

			findClass(dir);
	//		DumpPairList();

			if(root) {
				treev.deleteAllItems();

				char* name = root -> getName();
				HTREEITEM ritem = treev.addItem(NULL, TVI_ROOT, name);
				treev.buildTree(ritem, name, pairList);
				treev.sortAllChildren(ritem); 
				treev.expandChildren(ritem, TVE_EXPAND);
			}
			setSysCursor(IDC_ARROW);
		}
	}
}


long ClassTreeView::size(Event& event)
{
	int w, h;
	event.getSize(w, h);
	hpanedw.reshape(0, 0, w, h);
	
	if (initialized == FALSE) {
		hpanedw.setSashPosition(300);
		initialized = TRUE;
	}
	
	return 0;
}


void ClassTreeView::selChanged(Action& action)
{
	Event& event = action.getEvent();
	NM_TREEVIEW* nmtr = (NM_TREEVIEW*)event.getLParam();
	TV_ITEM tvItem = nmtr -> itemNew;
	HTREEITEM hselItem = tvItem.hItem;
	
	TV_ITEM hitem;
	memset(&hitem, 0, sizeof(hitem));

	hitem.mask       = TVIF_HANDLE|TVIF_PARAM;
	hitem.hItem      = hselItem;
	treev.getItem(&hitem);

	Pair* nodeInfo = (Pair*)hitem.lParam;
	if(nodeInfo) {
		Printf("Node name %s\r\n", nodeInfo->getName() );
		char prevFileName[MAX_PATH];
		prevFileName[0] = Zero;
		getText(prevFileName, sizeof(prevFileName)-1);

		char* fileName = nodeInfo -> getName();

//		if (strcmp(prevFileName, fileName) != 0) {
			richText.clear();
//			Printf("Loading a file %s\r\n", fileName);

			richText.streamIn(fileName, SF_TEXT);
			setText(fileName);

//		}
		Printf(">Scroll %d\r\n", nodeInfo->getLine());

		int line = nodeInfo->getLine()-1;
		DWORD indx = richText.lineIndex(line);

		char buff[512];
		int len = richText.getLine(line, buff, sizeof(buff)-1);
		if (len>0)
			buff[len] = '\0';

		Printf("line %d [%s]\r\n", line, buff); 
		richText.lineScroll(line, 1);		
	}
}


int ClassTreeView::findClass(char* dir)
{
    static char name[_MAX_PATH];
	strcpy(name, dir);

	strcat(name, "\\*.h");

	FindData data;

	FileFinder finder(name);
	if (finder.getFirst(&data)) {
		do {
			if(!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
				char path[_MAX_PATH];
				sprintf(path, "%s\\%s", dir, data.cFileName);
				searchClass(path);
			}
		} while(finder.getNext(&data));
	}
	return 1;
}


int ClassTreeView::searchClass(char* name)
{
    static char child[256];
    static char parent[256];
	static char delim[256];
	static char keyword[256];
	FileStream stream;

	if(stream.openReadOnly(name) == False) {
		showMessageDialog(name, "Cannot open!", MB_OK);
		return False;
	}

	static char line[512];
	int	lno= 0;
	while(stream.getLine(line, sizeof(line)) ) {
		const char* ptr = line;
		lno++;
		StringTokenizer tokenizer(line);
		static char token[128];
	
		char* comment = strstr(line, "//");
		if(comment) {
			*comment = Zero;
		}

		ptr = tokenizer.getToken(token, sizeof(token)-1);

		if(ptr && strcmp(token, "class") == 0 && strrchr(line, ';') == 0) {
			parent[0] = Zero;
			child[0]  = Zero;
			
			Node* parentNode = NULL;
			Node* childNode  = NULL;
			
			ptr = tokenizer.getToken(child, sizeof(child));
		
			if(ptr && strstr(child, "AFX_EXT_CLASS")) {
				ptr = tokenizer.getToken(child, sizeof(child));
			}
			if(ptr && (strstr(child, "_OWLCLASS") ||
				strstr(child, "_USERCLASS") ) ) {
				ptr = tokenizer.getToken(child, sizeof(child));
			}
			
			if(ptr) {
				ptr = tokenizer.getToken(delim, sizeof(delim));
			}
			if(strlen(delim) && strcmp(delim, ":") == 0) {
				if(ptr) {
					ptr = tokenizer.getToken(keyword, sizeof(keyword));
				}
				if(ptr) {
					ptr = tokenizer.getToken(parent, sizeof(parent));
				}
			}	
			if(strlen(delim) && strncmp(delim, ":p", 2) ==0) {
				if(ptr) {
					ptr = tokenizer.getToken(parent, sizeof(parent));
				}
			} 
			char* lparen = NULL;

			lparen = strrchr(child, '{');
			if(lparen) {
				*lparen = Zero;
			}
			lparen = strrchr(parent, '{');
			if(lparen) {
				*lparen = Zero;
			}

			if(strrchr(child, ';')) {
				child[0] = Zero;
			}

			if(strlen(parent)) {
				char* dot = strrchr(parent, ':');
				if (dot) 
					*dot = Zero;

				parentNode = new Node(parent, 0);
			}

			if(strlen(child)) {
				char* dot = strrchr(child, ':');
				if (dot) 
					*dot = Zero;
				childNode = new Node(child, 0);
			}

			if(parentNode == NULL) {
				if(root) {
					char* rootName = root->getName();
					Pair* pair = new Pair(new Node(rootName, 0), childNode);
					pair -> setName(name);
					pair -> setLine(lno);
					pairList->add(pair);	
				}
			}
			else if(parentNode && childNode) {
				Pair* pair = new Pair(parentNode, childNode);
				pair -> setName(name);
				pair -> setLine(lno);
				pairList->add(pair);		
			}
		}
	}
	stream.close();

    return True;
}	

// Only for debugging.
void ClassTreeView::dumpPairList()
{
	ListEntry* ptr = pairList->getEntry();

	while(ptr) {
		Pair* pair = (Pair*) ptr ->getObject();
		Printf("%s -> %s\r\n", pair->getParentName(),
						pair->getChildName());
		ptr = ptr ->getNext();
	}	
}


// ClassTreeView Main
void	Main(int argc, char** argv)
{
	const char* appClass = "ClassTreeView";
	Application applet(appClass, argc, argv);

	Args args;
	args.set(XmNbackground, (COLOR_BTNFACE+1));
	args.set(XmNclassName, appClass);
	ClassTreeView classTree(applet, appClass, args);
	classTree.realize();
	applet.run();
}
