/*	Table widget for FLTK-2
	Copyright(C) 2007 Alex Ivanov
	http://www.aleksoft.net
*/

#include "Table.H"
#include <stdlib.h>            // abs()
Table::Table(int X, int Y, int W, int H, uint rows, 
		uint cols, 
		uint rowHeight, 
		uint colWidth) : Group(X,Y,colWidth*cols,rows*rowHeight) 
{
	box(FLAT_BOX);
	this->rows=rows;
	this->cols=cols;
	scroll = new ScrollGroup(X,Y,W,H);
	scroll->box(DOWN_BOX);
	scroll->add(this);
	rowHeights.setDefault(rowHeight);
	colWidths.setDefault(colWidth);
	selectedRow=(uint)-3;
	showEditor=0;
	drawCell=0;
	x(0);y(0);
	editor=0;
};

int Table::colOffset(uint C)
{
	int offs=0;
	if(C==-1)
		return scroll->xposition();
	if(C==-2)
	{
		int tmp=scroll->xposition()+scroll->w()-4-colWidth(-2)-scroll->scrollbar_width() 
			* scroll->scrollbar.visible();
		offs=colOffset(cols);
		return (offs < tmp) ? offs : tmp;
	}
	if(C<0) return 0;
	offs=colWidths.getDefault()*C;
	if (fixedRows & ROWHEADER )
		offs += colWidth(-1);
	for(uint i=0;i<colWidths.count();i++)
	{
		if(colWidths.key(i) >= C) continue;
		offs += colWidths.value(i) - colWidths.getDefault();
	}
	return offs;
};

int Table::rowOffset(uint R)
{
	int offs=0;
	if(R == (uint)-1) 
		return scroll->yposition();
	if(R == (uint)-2) 
	{
		int tmp = scroll->yposition() + scroll->h() - rowHeight(-2) 
			- scroll->scrollbar_width() * scroll->hscrollbar.visible();
		offs = rowOffset(rows);
		return (offs < tmp) ? offs : tmp;
	}
	if(R>rows) return 0;
	offs=rowHeights.getDefault() * R;
	if(fixedRows & COLHEADER)
		offs += rowHeight(-1);
	for(uint i=0;i<rowHeights.count();i++)
	{
		if((uint)rowHeights.key(i) >= R) continue;
		offs += rowHeights.value(i) - rowHeights.getDefault();
	}
	return offs;
}

uint Table::colAt(int X, int noHeaders_Footers)
{
	int offs=0;
	int lastkey=-2;
	int c = X/colWidths.getDefault(); 
	if (!noHeaders_Footers)
	{
		if(X<scroll->xposition()+colWidth((uint)-1)*((fixedRows & ROWHEADER)==ROWHEADER))
			return (uint)-1;
		if(X>colOffset((uint)-2) && (fixedRows & ROWFOOTER))
			return (uint)-2;
	}
	while (X > colOffset(c+1) && c < cols) c++;
	while (X < colOffset(c) && c>0) c--;
	return c;
}

uint Table::rowAt(int Y, int noHeaders_Footers)
{
	int offs=0;
	int lastkey=-2;
	if(Y<0) Y=0;
	int r=Y/rowHeights.getDefault();
	if (!noHeaders_Footers)
	{
		if(Y < scroll->yposition()+rowHeight(-1)*((fixedRows & COLHEADER)==COLHEADER))
			return (uint)-1;
		if(Y > rowOffset((uint)-2) && (fixedRows & COLFOOTER))
			return (uint)-2;
	}
	while (Y > rowOffset(r+1) && r < rows) r++;
	while (Y < rowOffset(r) && r>0) r--;
	return r;
}

uint Table::colWidth(int C)
{
	return colWidths.value(C);
};

uint Table::rowHeight(int R)
{
	return rowHeights.value(R);
}

void Table::colWidth(int C, int value)
{
	colWidths.value(C,value);
}

void Table::rowHeight(int R, int value)
{
	rowHeights.value(R,value);
}

void Table::draw() 
{
	static int drawing=0;
	if (drawing || !drawCell) return;
	drawing++;
	firstrow=rowAt(-y(),1);
	firstcol=colAt(-x(),1);
	if (firstrow >= (uint)-2) 
		firstrow=0;
	if (firstcol >= (uint)-2) 
		firstcol=0;
	//Draw the regular cells
	for (uint i=firstrow;i<firstrow+(uint)scroll->h()/rowHeight(-1)+rowHeight(-1);i++)
	{
		if(i >= rows)
			break;
		for (uint j=firstcol; j<firstcol+(uint)scroll->w()/colWidth(-1)+colWidth(-1); j++)
		{
			if(j >= cols) break;
			drawCell(*this,i,j); 
		}
	}
	// Draw the editor
	if(children()) draw_child(*child(0));
	// Lastly, draw the fixed rows
	if(fixedRows)
		drawHeaderFooter(firstrow);
	drawing=0;
};

void Table::drawHeaderFooter(int firstrow) 
{
	// Column Headers & Footers
	if(fixedRows & COLSONLY)
	{
		uint showcols=firstcol+(uint)scroll->w()/colWidth(-1)+colWidth(-1);
		if (showcols > cols) showcols=cols;
		for (uint j=firstcol; j<showcols; j++)
		{
			//Header
			if(fixedRows & COLHEADER) 
			{
				drawCell(*this,-1,j);
			}
			//Footer
			if(fixedRows & COLFOOTER)
			{
				drawCell(*this,-2,j);
			}
		} //for
	}// if(fixedRows & COLSONLY)
	
	//Row Headers & Footers
	if (fixedRows & ROWSONLY)
	{
		uint showrows=firstrow+(uint)scroll->h()/rowHeight(-1)+rowHeight(-1);
		if (showrows > rows)
			showrows=rows;
		for (uint i=firstrow;i<showrows;i++)
		{
			if (fixedRows & ROWHEADER)
				drawCell(*this,i,-1);
			if (fixedRows & ROWFOOTER)
				drawCell(*this,i,-2);
		} //for
	} // if (fixedRows & ROWSONLY)

	//Corners
	if(fixedRows)
	{
		// Top Left
		if((fixedRows & ROWHEADER) && (fixedRows & COLHEADER))
			drawCell(*this,-1,-1);
		// Bottom Left
		if((fixedRows & ROWFOOTER) && (fixedRows & COLFOOTER))
			drawCell(*this,-2,-1);
		// Bottom Right
		if((fixedRows & ROWHEADER) && (fixedRows & COLFOOTER))
			drawCell(*this,-2,-2);
		// Top Right
		if((fixedRows & ROWFOOTER) && (fixedRows & COLHEADER))
			drawCell(*this,-1,-2);
	} //end of corners
}; //drawHeaderFooter()

void Table::adjustSize()
{
	int hs=0,ws=0,x1=x(),y1=y();
	if(fixedRows & ROWFOOTER)
		ws+=colWidth(-2);
	if(fixedRows & COLFOOTER)
		hs+=rowHeight(-2);
	resize(0,0,colOffset(cols)+ws+1,rowOffset(rows)+hs+1);
	x(x1);y(y1);
}

void Table::setFixedRows(int frows)
{
	fixedRows=frows;
	adjustSize();
}

void Table::removeEditor()
{
	if(editor)
	{
		remove(editor);
		delete(editor);
		editor=0;
	}
}

int Table::handle(int event)
{
	static int resizing=0;
	static int allowresize=0;
	static uint c;
	static uint r;

	if(!resizing)
	{
		c = colAt(event_x());
		r = rowAt(event_y());
	}
	switch (event)
	{
	case SHORTCUT: 
		if(children()) 
		{
			if(++selectedCol >= cols)
			{
				selectedCol = 0;
				selectedRow++;
				if(selectedRow >= rows) 
					selectedRow = 0;
			}
			int x=scroll->xposition()-colOffset((uint)-2)+colOffset(selectedCol)+colWidth(selectedCol);
			if(x<0) x=0;
			int y=scroll->yposition()-rowOffset((uint)-2)+rowOffset(selectedRow)+rowHeight(selectedRow);
			if(y<0) y=0;
			scroll->scrollTo(x,y);
			showEditor(*this,selectedRow,selectedCol,&editor);
		}
		return 1;
	case PUSH:
		selectedRow=r; selectedCol=c;
		if(event_clicks()>0)
		{
		if(showEditor && c < (uint) -2 && r < (uint) -2) 
			showEditor(*this,r,c,&editor);
		}
		else
		{
			removeEditor();
			redraw();
		}
		return 1;
	case RELEASE:
		if(resizing) adjustSize();
		resizing=0;
		allowresize=0;
		return 1;
	case ENTER:
		return 1;
	case MOVE:
		if((abs(event_x() - colOffset(c)) < 2 ) || (abs(event_x() - colOffset(c)-colWidth(c)) < 2 ))
		{
			cursor(CURSOR_WE);
			allowresize=1;
		}
		else if((abs(event_y() - rowOffset(r)) < 2 ) || (abs(event_y() - rowOffset(r)-rowHeight(r)) < 2 ))
		{
			cursor(CURSOR_NS);
			allowresize=2;
		}
		else
		{
			cursor(CURSOR_DEFAULT);
			allowresize=0;
		}
		return 1;
	case DRAG:
		if(allowresize == 1) 
		{
			if(!resizing)
			{
				resizing=1;
				if((event_x() - colOffset(c)) < 4)
					c=colAt(event_x(),1) - !(c==-2 && !c>=cols);
			}
			int newval = event_x()-colOffset(c);
			if(newval<1) newval=1;
			colWidths.value(c,newval);
			if(event_x()>colOffset((uint)-2)) scroll->scrollTo(scroll->xposition()-colOffset((uint)-2)+event_x(),scroll->yposition());
		}
		if(allowresize == 2) 
		{
			if(!resizing)
			{
				resizing=1;
				if((event_y() - rowOffset(r)) < 4)
					r=rowAt(event_y(),1) - !(r==-2 && r>=rows);
			}
			int newval =  event_y()-rowOffset(r);
			if(newval < 0) newval=1;
			rowHeights.value(r,newval);
			if(event_y()>rowOffset((uint)-2)) scroll->scrollTo(scroll->xposition(),scroll->yposition()-rowOffset((uint)-2)+event_y());
		}
		adjustSize();
		return 1;		
	}
	return 0;
};
