// pstable.cpp 
//
// This program is free software. See the file COPYING for details.
// Author: Mattias Engdegård, 1997-1999

// ** toooo complex,so need new Design ... should be more simple.. (by fasthyun@magicn.com) 


#include "proc.h"
#include "pstable.h"
#include "misc.h"

///#include <limits.h>
#include <QApplication>
#include <QClipboard>
#include <QToolTip>


// When a subtree is folded away, selections inside it disappear to prevent
// unexpected behaviour
static void clear_subtree_selections(Procinfo *p)
{
	for(int i = 0; i < p->table_children.size(); i++) {
		Procinfo *c = p->table_children[i];
		c->selected = FALSE;
		clear_subtree_selections(c);
	}
}


// SLOT :
// connect(this, SIGNAL(titleClicked(int)), SLOT(setSortColumn(int)));
void Pstable::setSortColumn(int col)
{
	procview->setSortColumn(col,false);
	setSortedCol(col);	//void HeadedTable::setSortedCol(int col)
	refresh(); // rebuild table
}

// AWKWARD
// sync Procview and HeadedTable set sorted column of table to procview->sortcol
// called by 1. Pstable::moveCol(int col, int place) 
void Pstable::set_sortcol() 
{
	for(int i = 0; i < procview->cats.size(); i++)
	{
		if(procview->cats[i] == procview->sortcat) {
			HeadedTable::setSortedCol(i);	// HeadedTable
			return;
		}
	}
	setSortedCol(-1);
}


// for Optimization
int Pstable::sizeHintForColumn(int col) const
{
	//int aw=fontMetrics().averageCharWidth();
 	int aw=fontMetrics ().width("0");
	int cat_idx=procview->cats[col]->index;
	//if(col==0) printf("col.idx=%d %s\n",cat_idx,procview->cats[col]->name);
	switch(cat_idx)
	{	
// 	only COMMON Field
//		case F_PID :
		case F_RSS :
		case F_SIZE :
		case F_TTY :
			return aw*6;
		case F_MEM :
		case F_START:
		case F_CPU : //PCPU
			return aw*6+2;
		case F_TIME :
			return aw*5;
		case F_WCPU :
		case F_NICE :
			return aw*5; 
		case F_CMD :
			return -(aw*20);

		case F_USER : 
//		case F_USER : return aw*10;
		case F_CMDLINE:	
		default:
			return -1;
	}
	return -1;
}

// inner 
QString Pstable::title(int col)
{
	if(col>=procview->cats.size())
		{
				qDebug("Qps Bug!: title() over col !! %d\n",col);
				return "";
		}
	return procview->cats[col]->name;
}

// TESTING 
void Pstable:: overpaintCell(QPainter *p, int row, int col, int xpos)
{
	if(col!=0) return;
	if(procview->cats[col]->id != F_CMD) return;
	
	int w;

	Procinfo *pi=procview->linear_procs[row];

	int n=pi->nthreads;
	if(n==1) return;
#ifdef LINUX 
	if(pi->pid!=pi->tgid) return; // LINUX 

#endif
	w= p->fontMetrics ().width(text(row,col));
	
	
	QFont font=p->font();
	int size=font.pointSize(); // point size
	
	if(size<=0) return; // saver!
	
	int h=body->cellHeight();
	h= p->fontMetrics().height(); // return pixel
	
	int msize=h*3.0/8.0;
	//printf("DEBUG: height=%d, msize=%d\n",h,msize);
	
	if(h<=11) return; // saver!
	
	if(msize<6) msize=6;

	//font.setPointSize(msize); // not pixel!
	font.setPixelSize(msize); // not pixel!
	p->setFont(font);
	p->drawText(xpos+w+2, msize+msize/3,QString::number(n)); 
	font.setPointSize(size);
	p->setFont(font);
}
// 
QString Pstable::text(int row, int col)
{
		if(col>=procview->cats.size())
		{
				qDebug("Qps Bug!: text() over col !! %d\n",col);
				return "";
		}
	return procview->cats[col]->string(procview->linear_procs[row]);
}

int Pstable::totalRow()//???
{
	return procview->linear_procs.size();
}

int Pstable::colWidth(int col)
{
	// this is -1 for variable width fields, htable keeps track of it
	if(col>=procview->cats.size())
		{
				qDebug("Qps Bug!: colWidth() over col !! %d\n",col);
				return -1;
		}
	return procview->cats[col]->width();
}

int Pstable::alignment(int col)
{
	//qDebug("debug:alignment()\n");
	Category *cat = procview->cats[col];
	return cat->alignment();
}



// virtual of HeadedTable 
void Pstable::setSelected(int row, bool sel)
{
///	qDebug("debug:setSelected()\n");
	Procinfo *pi= procview->linear_procs[row];
	if(pi->selected!=sel)
	{
		pi->selected=sel; 
	//	body->repaintRow(row);  // TEMP trick..
	}
}

// virtual 
bool Pstable::isSelected(int row) 
{ 
	Procinfo *pi= procview->linear_procs[row];
	return pi->selected;	//VALGRIND 
}

int Pstable::rowDepth(int row)
{
	Procinfo *pi= procview->linear_procs[row];
	if(pi)
		return pi->level; //VALGRIND segfault 
	
	qDebug("Qps bug: over row  %d!!!\n",row);
	return 0;
}

bool Pstable::lastChild(int row)
{
	Procinfo *pi= procview->linear_procs[row];
	if(pi)
		return pi->lastchild;
	qDebug("Qps bug: over row  %d!!!\n",row);
	return 0;
}

// Segfault !!!!
// return  not PPID ! just virtual parent in tree
int Pstable::parentRow(int row)
{
	return procview->linear_procs[row]->parent_row;
	/* int ppid= procview->linear_procs[row]->ppid;
	Procinfo *pi= procview->getProcinfoByPID(ppid);
	if(pi) return ppid;
	else return -1; */
}

//DRAFT tooltip
QString Pstable::tipText(int col)
{
	Category *cat = procview->cats[col];
	QString s(cat->help);
	// trick
	if(cat->index == F_STAT)
		s.append("\n(R =Running, S =Sleeping, T =sTopped, Z=Zombie)");
	if(cat->index == F_PLCY)
		s.append("\n(TS =Time Sharing)");
	if(cat->index == F_RSS);
		//s.append("\nRSS = CODE + DATA + SHARE\n"
		//	   "RSS = TRS  + DRS  + SHARE\n");
		///s.append("\n(RSS = TRS + DRS)");
	if(cat == procview->sortcat)
		s.append(procview->reversed ? "\n(sorted backwards)" : "\n(sorted)");

//	printf("size=%d\n",s.capacity());
	return s;
}

void Pstable::showTip(QPoint p,int idx)
{
    QToolTip::showText(p, tipText(idx));
}

extern	TFrame 		*infobox;  // testing

//SLOT
void Pstable::mouseOutOfCell()
{
	infobox->hide();
}

void Pstable::mouseOnCell(int row,int col)
{
	//printf("cursor on (%d,%d)\n",row,col);
	
	Category *cat = procview->cats[col];
	QString s="";


	if(cat->index == F_USER)
	{
		Procinfo *pi= procview->linear_procs[row];
		//s.append("");
		s=pi->username;

	}
	else 
	{		
		infobox->hide();	
		return;
	}


	if(infobox!=NULL)
	{	
		//p.setY(p.y() + geometry().y());
		//p.setY(QCursor::pos().y() - geometry().y());
		//p.setY(QCursor::pos().y() - frameGeometry().y());
		//p.setX(QCursor::pos().x() - frameGeometry().x());
		//QPoint p=QCursor::pos();
	//	if(htable->numSelected()>=2)
		
		infobox->show();
		infobox->setPos();
		if(s>0)
		{
			infobox->setText(s);
		}
		else
			infobox->hide();
	}

	//char *str=htable->total_selectedRow(col);
}

void Pstable::leaveEvent ( QEvent * ) 
{

}



//DRAFT  who call?
// void TableBody::mouseMoveEvent(QMouseEvent *e)
char* Pstable::total_selectedRow(int col)
{
	static char buff[48];
	char mem_str[48];
	char *name;
	int	index;
	
	if(procview->cats.size()<=col or col<0 ) return 0;  // col == -1 
	index=procview->cats[col]->index;

	switch(index)
	{
		case F_SIZE:
			name="total SIZE: ";
			break;
		case F_RSS:
			name="total RSS: ";
			break;
#ifdef LINUX
		case F_TRS:
			name="total Text: ";
			break;
		case F_DRS:
			name="total Data: ";
			break;
		case F_STACK:
			name="total STACK: ";
			break;
#endif
		default:
			return 0;
	}
	
	int total=0;
	int rows = procview->linear_procs.size();
	for(int i = 0; i < rows; i++)
		if(procview->linear_procs[i]->selected)
		{
			switch(index)
			{
				case F_SIZE:
					total+=procview->linear_procs[i]->size;break;
				case F_RSS:
					total+=procview->linear_procs[i]->resident;break;
#ifdef LINUX
				case F_TRS:
					total+=procview->linear_procs[i]->trs;break;
				case F_DRS:
					total+=procview->linear_procs[i]->drs;break;
				case F_STACK:
					total+=procview->linear_procs[i]->stack;break;
#endif			
			}
		}

	mem_string(total,mem_str); // misc.h
	strcpy(buff,name);
	strcat(buff,mem_str);
	//sqDebug(buff,"total:%s",total);
	return buff;
}


Pstable::Pstable(QWidget *parent,Procview *pv)
	: HeadedTable(parent,0)
{
	procview=pv;
//	connect(this, SIGNAL(selectionChanged(const Svec<int> *)),SLOT(selection_update(const Svec<int> *))); //DEL?
//	connect(this, SIGNAL(selectionChanged()),SLOT(selection_update())); // when procinfo clicked
	connect(this, SIGNAL(titleClicked(int)), SLOT(setSortColumn(int)));
	connect(this, SIGNAL(foldSubTree(int)), SLOT(subtree_folded(int)));
	connect(head,SIGNAL(toolTip(QPoint ,int)),this,SLOT(showTip(QPoint,int)) );
	connect(this, SIGNAL(flyOnCell(int,int)), SLOT(mouseOnCell(int,int)));
	connect(this, SIGNAL(outOfCell()), SLOT(mouseOutOfCell()));
}

// who call this ? : from qps.cpp
void Pstable::setProcview(Procview *pv)
{
	procview = pv;
}

HeadedTable::NodeState Pstable::folded(int row)
{
	Procinfo *p = procview->linear_procs[row]; 
	return ( p->table_children.size() > 0) ? (p->hidekids ? Closed : Open) : Leaf;
}


// slot: called when selection changes
//	called by 
//		1.void HeadedTable::selectionNotify()
/*
void Pstable::selection_update(const Svec<int> *rows)
{
	//CLIPBOARD
	qDebug("debug:selection_update()\n");
	for(int i = 0; i < rows->size(); i++) {
		int row = (*rows)[i];
		procview->linear_procs[row]->selected = isSelected(row); // WRONG !!!
	}
	
	qps->update_menu_selection_status();
	
	if(numSelected() > 0 && qps->pids_to_selection) {
		// set the X11 selection to "PID1 PID2 PID3 ..."
		QString s, num;
		int n = numRows();
		for(int i = 0; i < n; i++) {
			if(isSelected(i)) {
				num.setNum(procview->linear_procs[i]->pid);
				s.append(num);
				if(i < n - 1)
					s.append(" ");
			}
		}

		// important: this mustn't be called non-interactively since Qt uses
		// the selection time of the last mouse or keyboard event
////		QApplication::clipboard()->setText(s);
	} 

} */

// call by 
// 	1.void Qps::set_table_mode(bool treemode)
// 	slot: changes table mode
void Pstable::setTreeMode(bool treemode)
{
	//qDebug("Pstable::setTreeMode() %d , procview.treeview =%d\n",treemode,procview->treeview);
	// no more HeadedTable::setTreeMode(treemode);  
	HeadedTable::setTreeMode(treemode);  
	procview->treeview = treemode;
	procview->fieldArrange();
	set_sortcol();
	refresh();	//==rebuild();
}


bool Pstable::columnMovable(int col)
{
	if(treemode) 
	{
		if(col==0) return false;

	}
	if(procview->cats[col]->index==F_CMDLINE)
		return false;

	return true;
}

// called by HeadedTable
// Description : FIELD movement by mouse drag 
//	 				To place From col
//	virtual HeadedTable::moveCol(col,place);
void Pstable::moveCol(int col, int place) 
{
	//qDebug("Pstable::moveCol\n");
	procview->moveColumn(col,place);
	set_sortcol() ;
//	update();
	refresh(); // width size changed ,...
	return;
	//updateColWidth(place); updateColWidth(col);// TEMP
}


// NEED Check !!
// Slot: called when a subtree is opened or closed
// row = row number of sheet
void Pstable::subtree_folded(int row)
{
					
	Procinfo *p = procview->linear_procs[row];
	p->hidekids = !p->hidekids;
	
	if(p->hidekids)	
		clear_subtree_selections(p);  // *** important 
	
	refresh(); // tree rebuild only
	return ;	
	// ???
	Procinfo *nextp = (row < numRows() - 1) ? procview->linear_procs[row + 1] : 0;
	if(!p->hidekids) {
		// Show as much as possible of the opened subtree
		int r = row + 1;
		while(r < numRows() && procview->linear_procs[r] != nextp)
			r++;
	//	setAutoUpdate(FALSE);
		showRange(row, r - 1);
		//setAutoUpdate(TRUE);
	}
	// This is a stopgap solution; it would be better to have htable
	// take care of the hiding of subtrees and repaint only the rows under
	// the line hidden
	
}

// DRAFT CODE:
// 1.procview->refresh: proc.refresh, rebuild
// 2.resetwidth
// 3.repaint

// called by 
// 	1.void Qps::refresh()
void STATUSBAR_SETCOUNT(int n);

static int kgen=123;
void Pstable::checkTableModel ()
{
//	int size=procview->linear_procs.size();
//	if(size!=numRows())
	{
	//	printf("Warnning: this is the bug.. wrong! %d %d\n",size,numRows());

	}
	if(procview->current_gen!=kgen)
	{
//		printf("Warnning: maybe this is the bug.. wrong! %d %d\n",kgen,procview->current_gen);
//		setAutoUpdate(false);  // ********** important for GUI-thread
//		HeadedTable::setTreeMode(procview->treeview);  
		procview->rebuild(); //for Table
		setNumRows(procview->linear_procs.size()); 	//1.
	//	qDebug("Pstable setTreeMode =%d , procview.treeview =%d\n",treemode,procview->treeview);
		setNumCols(procview->cats.size());  		//2. resetWidths() 
		kgen=procview->current_gen;
//		setAutoUpdate(true); 
	}
}


// paintEvent();

void Pstable::refresh()
{
//    qDebug("Pstable:refresh()");
	//	procview->refresh(); -> move to Qps::refresh()
	//	qDebug("catsize=%d\n",procview->cats.size());
	procview->rebuild(); //for Table
	setNumRows(procview->linear_procs.size()); 	//1.
	setNumCols(procview->cats.size());  		//2. resetWidths()  UNINITIAL
	kgen=procview->current_gen;
	//checkTableModel();
	repaint_changed();
	STATUSBAR_SETCOUNT(procview->num_process);
}

