/*-*-c++-*-
 * $Id: ctrace.cpp,v 1.1 2002/01/28 15:38:32 holzheu Exp $
 *
 * This file is part of qlcrash, a GUI for the dump-analysis tool lcrash.
 *
 * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
 *
 * Authors:
 * Michael Geselbracht (let@users.sourceforge.net)
 * Fritz Elfert (elfert@de.ibm.com)
 * Michael Holzheu (holzheu@de.ibm.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version. See the file COPYING for more
 * information.
 */
#include "ctrace.h"

#include <qframe.h>
#include <qlistview.h>
#include <qheader.h>
#include <qsplitter.h>
#include <qtimer.h>
#include <qcombobox.h>
#include <qvaluelist.h>
#include <qmultilineedit.h>

#include <ctype.h>

#include "qlcrashdoc.h"
#include "cconfigmanager.h"
#include "ctrace_priv.h"
#include "ctraceitem.h"
#include "ctracelowcore.h"
#include "clistview.h"
#include "clistviewitem.h"

// test
#include <iostream>

// number of columns in function list
#define COLUMN_COUNT 3

// column containing the address of the process
#define PROCESS_ADDRESS_COL	2

// ID's for popup menu
#define POPUP_TASK_EXPAND		1
#define POPUP_TASK_COLLAPSE		2
#define POPUP_FKT_DISASSEMBLE	1

CTrace::CTrace(QLcrashDoc* doc, QWidget* parent, const char* name)
	: QWidget(parent, name, WDestructiveClose)
	, oDoc(doc)
	, oSelectedItem(0)
	, oParent(parent)
{
	oSplitter = new QSplitter(Horizontal, this);
	oSplitter->move(0, 0);
	
	oFktList = new QListView(oSplitter);
	oFktList->addColumn(tr("Pid"));
	oFktList->addColumn(tr("Name"));
	oFktList->addColumn(tr("Address"));
	oFktList->addColumn(tr("Level"));
	oFktList->setRootIsDecorated(true);
	oFktList->setAllColumnsShowFocus(true);
	oFktList->setShowSortIndicator(true);
	connect(oFktList, SIGNAL(clicked(QListViewItem*)), SLOT(slotSelectionChanged(QListViewItem*)));
	connect(oFktList, SIGNAL(doubleClicked(QListViewItem*)), SLOT(slotListActivated(QListViewItem*)));
	connect(oFktList, SIGNAL(rightButtonPressed(QListViewItem*, const QPoint&, int)), SLOT(slotRightClicked(QListViewItem*, const QPoint&, int)));
	
	oDisassembly = new CTrace_priv(oSplitter);
	
	oSplitter->setResizeMode(oFktList, QSplitter::KeepSize);
	
	initPopup();
	setTask();
}

CTrace::~CTrace()
{	
}

void
CTrace::resizeEvent(QResizeEvent*)
{
	QHeader* hdr = oFktList->header();
	int size = 0;
	const int len = hdr->count();
	
	for (int i = 0; i < len; i++) {
		size = size + hdr->sectionSize(i);
	}
	QValueList<int> vl;
	vl.append(size + 20);
	oSplitter->setSizes(vl);
	oSplitter->resize(width(), height());
}

void
CTrace::closeEvent(QCloseEvent* e)
{
	emit sigClosed();
	
	QWidget::closeEvent(e);
}

void
CTrace::initPopup()
{
	oTaskPopup = new QPopupMenu(this);
	oTaskPopup->insertItem(tr("Expand all"), POPUP_TASK_EXPAND);
	oTaskPopup->insertItem(tr("Collapse all"), POPUP_TASK_COLLAPSE);
	
	oFktPopup = new QPopupMenu(this);
	oFktPopup->insertItem(tr("Disassemble"), POPUP_FKT_DISASSEMBLE);
}

void
CTrace::slotSelectionChanged(QListViewItem* item)
{
	if (item != 0 && oSelectedItem != 0 && item != oSelectedItem && item->parent() != 0) {
		oDisassembly->area()->clear();
		oSelectedItem = 0;
	}
}

void
CTrace::slotListActivated(QListViewItem* item)
{
	if (item != 0 && item->parent() != 0) { // ignore double clicking a task
		QListView* listView = oDisassembly->area();
		
		// extract function name
		QString name;
		int pos = item->text(1).findRev('+');
		if (pos == -1) {
			pos = item->text(1).findRev('>');
		}
		if (pos != -1) {
			name = item->text(1).mid(1, pos - 1);
			
			QValueList<CDisassemble> dis = oDoc->getDisassemble(name);
			QValueList<CDisassemble>::Iterator it = dis.begin();
			
			// extract offset (+123 from <name+123>)
			QString offset = "+";
			pos = item->text(1).findRev('+');
			if (pos != -1) {
				while (isdigit(item->text(1).at(++pos).latin1())) {
					offset.append(item->text(1).at(pos));
				}
			}
			
			QListViewItem* after = 0;
			QListViewItem* select = 0;
			while (it != dis.end()) {
				QListViewItem* lvi = (after == 0)
					? new QListViewItem(listView)
					: new QListViewItem(listView, after)
				;
				after = lvi;
				lvi->setText(0, (*it).addr());
				lvi->setText(1, (*it).offset());
				lvi->setText(2, (*it).mnemonic());
				
				// the desired offset?
				if (offset.length() > 1U && (*it).offset().find(offset) > 0) {
					select = lvi;
				}
				
				++it;
			}
		
			if (select != 0) {
				listView->setSelected(select, true);
				listView->ensureItemVisible(select);
			}
			
			// slightly increase column width
			/*
			for (int i = 0; i < COLUMN_COUNT; i++) {
				listView->setColumnWidth(i, listView->columnWidth(i) + 10);
			}
			*/
			
			oSelectedItem = item;
		}
		else { // LOWCORE entry ?
			if (item->text(1) == "LOWCORE") { // Yes
				oSelectedItem = item->parent();
				emit sigLowcore(this, item->parent()->text(2));
				oSelectedItem = 0;
			}
		}
	}
}

void
CTrace::slotRightClicked(QListViewItem* item, const QPoint& pos, int col)
{
	if (item != 0) {
		int id;
		
		if (item->parent() == 0) { // process
			id = oTaskPopup->exec(pos);
			
			switch (id) {
				case POPUP_TASK_EXPAND:
					expandAll();
					break;
				case POPUP_TASK_COLLAPSE:
					collapseAll();
					break;
			}
		}
		else if (item->text(1) != "LOWCORE") { // function
			id = oFktPopup->exec(pos);
			
			switch (id) {
				case POPUP_FKT_DISASSEMBLE:
					slotListActivated(item);
					break;
			}
		}
	}
}

void
CTrace::setTask()
{
	oFktList->clear();
	oDisassembly->area()->clear();
	
	// request task list
	QValueList<CTaskItem> list = oDoc->getTaskList();
	QValueList<CTaskItem>::Iterator it = list.begin();
	
	while (it != list.end()) {
		CListViewItem* lvi = new CListViewItem(oFktList);
		lvi->setText(0, (*it).pid());
		lvi->setText(1, (*it).name());
		lvi->setText(2, (*it).address());
		
		QListViewItem *after = 0;
		
		if ((*it).cpu() != "-") {
			lvi->setColor(red);
#ifndef WIN32
#warning "This relies on an activated debug feature"
#endif
			// insert LOWCORE entry if we have a S/390
			if (oDoc->haveDebugFeature()) {
				after = new CListViewItem(lvi);
				after->setText(1, "LOWCORE");
			}
		}
		
		// request trace for current task
		QValueList<CTraceItem> cl = oDoc->getTrace((*it).address());
		QValueList<CTraceItem>::Iterator cit = cl.begin();
		if (cit != cl.end()) { // the first item is just the task name
			++cit;
		}
		
		while (cit != cl.end()) {
			QListViewItem* tvi = (after == 0)
				? new CListViewItem(lvi)
				: new CListViewItem(lvi, after)
			;
			after = tvi;
			
			tvi->setText(1, QString("<") + (*cit).function() + (*cit).offset() + ">");
			tvi->setText(2, (*cit).address());
			tvi->setText(3, (*cit).level());
			
			++cit;
		}
		
		++it;
	}
}

void
CTrace::setMarkedProcess(const QString& addr)
{
	if (!addr.isEmpty()) {
		QListViewItem* walker = oFktList->firstChild();
		
		while (walker != 0) {
			if (walker->text(PROCESS_ADDRESS_COL) == addr) {
				oFktList->setSelected(walker, true);
				oFktList->ensureItemVisible(walker);
				oFktList->setOpen(walker, true);
				
				break;
			}
			
			walker = walker->nextSibling();
		}
	}
}

void
CTrace::expandAll()
{
	setAllOpen(true);
}

void
CTrace::collapseAll()
{
	setAllOpen(false);
}

void
CTrace::setAllOpen(bool w)
{
	QListViewItem* walker = oFktList->firstChild();
	
	while (walker != 0) {
		walker->setOpen(w);
		walker = walker->nextSibling();
	}
}
