////////////////////////////////////////////////////////////////////////////////
// GLTableView.cpp
// Copyright Bjoern Ganster 2007
////////////////////////////////////////////////////////////////////////////////

#include "Config.h"
#include "Font.h"
#include "GLTableView.h"

#define FILE_NUMBER FILE_NO_GLTABLEVIEW_CPP

////////////////////////////////////////////////////////////////////////////////
// Constructor

GLTableView::GLTableView (GLWindow* _parent, int _left, int _top, int _width, int _height, int _ID)
: GLWindow ("GLTableView", GLTableView_ID, _parent, _left, _top, _width, _height),
  dataSource (NULL),
  selectedRow (-1),
  vScrollbar (NULL),
  ID (_ID),
  mInnerMargin(2)
{
	SET_SCOPE;
	Config* config = getConfig();
	textColor = config->getTextColor();
	selectionColor = config->getActiveModeButtonColor();
	backgroundColor = config->getInactiveModeButtonColor();
	RETURN;
}

////////////////////////////////////////////////////////////////////////////////
// Destructor

GLTableView::~GLTableView()
{
	SET_SCOPE;
	// No, vScrollbar is registered with the common parent and will be deleted from there ...
	//safeDelete(vScrollbar);
	RETURN;
}

////////////////////////////////////////////////////////////////////////////////
// Get/Set column/row count

void GLTableView::setColWidth (size_t num, size_t newVal, size_t defaultWidth)
{
	// Store old size
	SET_SCOPE;
	size_t oldVal = 0;
	if (num+1 < colStart.size())
		oldVal = colStart[num+1] - colStart[num];

	// Add columns before num
	size_t i = colStart.size();
	while (i < num+1) {
		colStart.push_back (i*defaultWidth + mInnerMargin);
		++i;
	}

	// Adjust columns after num
	int delta = newVal - oldVal;
	i = num+1;
	while (i < colStart.size()) {
		colStart[i] += delta;
		i++;
	}
	RETURN;
}

// More flexible, but buggy: some lines are sometimes set to zero width
/*void GLTableView::setRowHeight (size_t num, size_t newVal, size_t defaultHeight)
{
   // Store old size
   size_t oldVal = 0;
   size_t rowCount = rowStart.size();
   if (num+1 < rowCount)
      oldVal = rowStart[num+1] - rowStart[num];

   // Add rows before num
   size_t i = 0, y = 0;
   if (rowCount > 0)
      y = rowStart[rowCount-1];
   while (rowStart.size() < num+2) {
      rowStart.push_back (y+i*defaultHeight);
      ++i;
   }

   // Adjust columns after num
   int delta = newVal - oldVal;
   i = num+1;
   while (i < rowStart.size()) {
      rowStart[i] += delta;
      i++;
   }
}*/

// Set a single height for all rows
void GLTableView::setRowHeights (size_t num, size_t newVal)
{
	SET_SCOPE;
	rowStart.resize(num+1);
	for (size_t i=0; i < rowStart.size(); i++) {
		rowStart[i] = i*newVal + mInnerMargin;
	}
	RETURN;
}

////////////////////////////////////////////////////////////////////////////////

void GLTableView::findSelectedRow (int y)
{
   size_t colCount, rowCount;
   dataSource->getDimensions(ID, rowCount, colCount);

   y -= top-5;
   selectedRow = 0;
   while (selectedRow < (int)rowCount && y > (int)rowStart[selectedRow+1])
      selectedRow++;

   if (vScrollbar != NULL)
      selectedRow += vScrollbar->getStart();
}

////////////////////////////////////////////////////////////////////////////////
// Mouse events

GLWindow* GLTableView::buttonStateChanged (int button, bool down, 
   int x, int y)
{ 
   findSelectedRow (y);
   if (button == 1 && !down)
      sendMessage (listener, TableItemSelected);
   if (button == 3 && down)
      dataSource->showContextMenu(this, x, y);
   return this;
}

void GLTableView::mouseMoved (int /*x*/, int y)
{
   //findSelectedRow (y);
}

////////////////////////////////////////////////////////////////////////////////
// Keyboard Events

void GLTableView::keypressed (BGKey key, bool /*shiftHeld*/, bool /*ctrlHeld*/, bool /*down*/, bool /*repeat*/)
{
    if (key == 13) {
        sendMessage (listener, TableItemSelected);
    }
}

/*void GLTableView::specialKeyPressed (char key, bool shiftHeld, bool ctrlHeld)
{
   size_t colCount, rowCount;
   dataSource->getDimensions(ID, rowCount, colCount);

   if (key == 101 && selectedRow > 0) {
      selectedRow--;
   } else if (key == 103 && selectedRow < ((int) rowCount)-2) {
      selectedRow++;
   } else {
      BGString msg = "Special key pressed: ";
      msg.appendInt(key);
      msg.printLn();
   }
}*/

////////////////////////////////////////////////////////////////////////////////
// Render

size_t GLTableView::getRowStart(size_t row)
{
	if (row >= rowStart.size()) {
		if (rowStart.size() > 0) {
			row = rowStart.size()-1;
		} else {
			return 0;
		}
	}

	return rowStart[row];
}

void GLTableView::render(int wx, int wy, int w, int h)
{
	SET_SCOPE;
	glDisable (GL_TEXTURE_2D);
	glDisable (GL_DEPTH_TEST);
	prepareToRender(wx, wy, w, h);

	int textTop = (height - 17) / 2;
	glTranslated (-1.0, 1.0, 0.0);
	glScaled (2/((double) getWidth()), -2/((double) getHeight()), 1.0);

	// Render texts
	if (dataSource != NULL) {
		size_t colCount, rowCount;
		dataSource->getDimensions(ID, rowCount, colCount);
		if (rowCount == 0) {
			RETURN;
		}

		// Create or destroy scrollbar
		size_t maxRowHeight = rowCount * getRowStart(1);
		size_t row = 0, startY = 0;
		const size_t sbw = 20;
		if ((int) maxRowHeight > height && vScrollbar == NULL) {
			vScrollbar = new GLScrollbar (parent);
			vScrollbar->setGeometry(left+width-sbw, top, sbw, height);
			vScrollbar->setMax(rowCount);
			vScrollbar->setSize(rowCount * getRowStart(1) / height);
			//add (vScrollbar);
			parent->add (vScrollbar);
		} else if (vScrollbar != NULL) {
			if ((int) maxRowHeight < height) {
				safeDelete(vScrollbar);
			} else {
				row = vScrollbar->getStart();
				startY = getRowStart(row);
				vScrollbar->setGeometry(left+width-sbw, top, sbw, height);
				vScrollbar->setMax(rowCount);
				vScrollbar->setSize(rowCount * getRowStart(1) / height);
			}
		}

		size_t y = getRowStart(row)-startY + mInnerMargin;
		while (row < rowCount && (int) y + mInnerMargin < height && row < rowStart.size()) {
			// Highlight selection
			y = getRowStart(row)-startY;
			if (row == selectedRow) {
				int nextY = y;
				if (row < rowStart.size()-1)
					nextY = getRowStart(row+1)-startY;
				else
					nextY = height;
				selectionColor.glColor();
				glBegin(GL_POLYGON);
				glVertex2i(mInnerMargin, y+1);
				glVertex2i(width-mInnerMargin, y+1);
				glVertex2i(width-mInnerMargin, nextY);
				glVertex2i(mInnerMargin, nextY);
				glEnd ();
			}

			// Render cell text
			for (size_t col = 0; col < colCount; col++) {
				int x = 0;
				if (col < colStart.size()) {
					x = colStart[col];
				}
				BGCPString str;
				dataSource->getString (ID, row, col, str);
				textColor.glColor();
				Font* font = getDefaultFont();
				font->renderText (str, x, y+2, width);
			}
			row++;
		}

		if (vScrollbar != NULL) {
			vScrollbar->setMax(rowCount);
			vScrollbar->render(wx, wy, w, h);
		}
	}
	RETURN;
}

int GLTableView::getSelectedRow() const
{
	return selectedRow;
}

void GLTableView::setSelectedRow(int newVal)
{
	selectedRow = newVal;
}

void GLTableView::clearSelection()
{
	selectedRow = -1;
}

// GLStringTableView: specialized for rendering a table of strings
// Constructor
GLStringTableView::GLStringTableView (GLWindow* _parent, int _left, int _top, 
			int _width, int _height, int _ID)
: GLTableView(_parent, _left, _top, _width, _height, _ID)
{
}

void GLStringTableView::setString(int row, int col, const BGCPString& str)
{
	SET_SCOPE;
	while ((int)mTableStrings.size() <= row) {
		mTableStrings.push_back(std::vector<BGCPString>());
	}

	while ((int)mTableStrings[row].size() <= col) {
		mTableStrings[row].push_back(BGCPString());
	}

	mTableStrings[row][col] = str;
	RETURN;
}

void GLStringTableView::getDimensions (size_t senderID, size_t& rowCount, size_t& colCount)
{
	SET_SCOPE;
	colCount = 1;
	rowCount = mTableStrings.size();

	for (size_t i = 0; i < mTableStrings.size(); ++i) {
		if (mTableStrings[i].size() > colCount) {
			colCount = mTableStrings[i].size();
		}
	}

	RETURN;
}

// Get string for a cell
bool GLStringTableView::getString (size_t senderID, int row, int col, BGCPString& str)
{
	SET_SCOPE;
	if ((size_t) row >= mTableStrings.size()) {
		str.clear();
		RETURN false;
	}

	std::vector<BGCPString>& rowStrings = mTableStrings[row];
	if ((size_t) col >= rowStrings.size()) {
		str.clear();
		RETURN false;
	}

	str = rowStrings[col];
	RETURN true;
}

// Request context menu
void GLStringTableView::showContextMenu(VirtualClass* /*sender*/, int /*x*/, int /*y*/)
{
}
