// AsTableRow.cpp: implementation of the CAsTableRow class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "AsTableRow.h"
#include "AsBaseCell.h"
#include "AsGenericTextCell.h"
#include "AsCheckBoxCell.h"
#include "AsComboBoxCell.h"
#include "AsNumericTextCell.h"
#include "AsGradientCell.h"
#include "DrawUtils.h"

/*
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
*/

#define BORDER 1

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

int CAsTableRow::SplitterArea_ = 4;

CAsTableRow::CAsTableRow():
	numColumns_(0),
	parentWnd_(NULL),
	borderColor_(::GetSysColor(COLOR_3DSHADOW)),
	rowHeight_(21),
	fixedColumnsNumber_(0),
	firstNonFixedColIndex_(0)
{
	fontInfo_.SetFontBold(false);
	fontInfo_.SetFontName(_T("tahoma"));
	fontInfo_.SetFontSize(10);
}

CAsTableRow::~CAsTableRow()
{
	Clear();
}

void CAsTableRow::SetNumColumns(int numColumns)
{
	numColumns_ = numColumns;
}

int CAsTableRow::GetNumColumns() const
{
	return numColumns_;
}

void CAsTableRow::Draw(CDC* pDC)
{
	DrawBackground(pDC);
	DrawCells(pDC);
	DrawBorders(pDC);
}

void CAsTableRow::DrawBackground(CDC* pDC)
{
	CBrush b(RGB(255,100,100));
	
	pDC->FillRect(drawArea_, &b);
}

void CAsTableRow::DrawBorders(CDC* pDC)
{
	CRect cellDrawArea;
	CPen pen(PS_SOLID, 1, borderColor_);
	CPen* oldPen;
	CRect rc;
	CRect tmp;
	int lastRight;
	int i;
	CAsBaseCell* cell;

	parentWnd_->GetClientRect(rc);
	oldPen = pDC->SelectObject(&pen);

	for (i = 0; i < fixedColumnsNumber_ && i < cells_.size(); i++) {
		cellDrawArea = cells_[i]->GetDrawArea();
		pDC->MoveTo(cellDrawArea.right, cellDrawArea.top);
		pDC->LineTo(cellDrawArea.right, cellDrawArea.bottom);
	}

	for (i = firstNonFixedColIndex_; i < cells_.size(); i++) {
		cell = cells_[i];
		cellDrawArea = cells_[i]->GetDrawArea();
		if (!rc.PtInRect(cellDrawArea.TopLeft()))
			break;

		pDC->MoveTo(cellDrawArea.right, cellDrawArea.top);
		pDC->LineTo(cellDrawArea.right, cellDrawArea.bottom);

		if (cell->IsSelected()) {
			tmp = cell->GetDrawArea();
			tmp.InflateRect(1,1,0,0);
			DrawUtils::DrawRect(pDC, tmp, RGB(200,0,0));
		}
		lastRight = cellDrawArea.right;
	}

	pDC->SelectObject(oldPen);
}

void CAsTableRow::DrawCells(CDC* pDC)
{
	int i;
	int lastRight;
	CAsBaseCell* cell;
	CRect drawArea;
	CRect rc;

	parentWnd_->GetClientRect(rc);
	for (i = 0; i < fixedColumnsNumber_ && i < cells_.size(); i++) {
		cell = cells_[i];
		if (cell)
			cell->Draw(pDC);
	}
	lastRight = 0;
	for (i = firstNonFixedColIndex_; i < cells_.size(); i++) {
		cell = cells_[i];
		if (cell) {
			drawArea = cell->GetDrawArea();
			if (!rc.PtInRect(drawArea.TopLeft()) || drawArea.right < lastRight)
				break;
			//TRACE1("\tdrawing cell %d\n", i);
			cell->Draw(pDC);
		}
		lastRight = drawArea.right;
	}
}

void CAsTableRow::SetDrawArea(const CRect& drawArea, bool forceRecalc)
{
	if (drawArea_ == drawArea && !forceRecalc)
		return;
	
	drawArea_ = drawArea;
	if (drawArea_.Height() != rowHeight_ && drawArea_.Height() != 0)
		drawArea_.bottom = drawArea_.top + rowHeight_;
	
	
	int totalWidth = 0;
	int cellWidth;
	int i;
	CRect tmp;
	CRect rc;
	CRect cellDrawArea;

	parentWnd_->GetClientRect(rc);

	tmp = drawArea_;
	cellWidth = drawArea_.Width();

	// set draw area on fixed columns
	//
	for (i = 0; i < fixedColumnsNumber_ && i < cells_.size(); i++) {
		tmp.right = tmp.left + colWidthList_[i];
		cells_[i]->SetDrawArea(tmp);
			
		if (!rc.PtInRect(tmp.TopLeft()))
			break;
		
		tmp.left = tmp.right + BORDER;

		totalWidth += colWidthList_[i];
		totalWidth += BORDER;	
	}

	for (i = fixedColumnsNumber_; i < firstNonFixedColIndex_ && i < cells_.size(); i++)
		cells_[i]->SetDrawArea(CRect(0,0,0,0));

	for (i = firstNonFixedColIndex_; i < numColumns_ && i < cells_.size(); i++) {
		tmp.right = tmp.left + colWidthList_[i];
		cells_[i]->SetDrawArea(tmp);
			
		if (!rc.PtInRect(tmp.TopLeft()))
			break;
		
		tmp.left = tmp.right + BORDER;

		totalWidth += colWidthList_[i];
		totalWidth += BORDER;
		
		if (!cells_[i]->IsVisible())
			cells_[i]->SetVisible(true);
	}

	drawArea_.right = drawArea_.left + totalWidth - 1;
}

CRect CAsTableRow::GetDrawArea() const
{
	return drawArea_;
}

CAsBaseCell* CAsTableRow::CreateCell(int index, enTableCellType cellType)
{
	if (index < 0 || index >= numColumns_)
		return NULL;
	
	CAsBaseCell* newCell = NULL;

	switch (cellType) {
		case enTableCellType_Caption:
			newCell = new CAsBaseCell;
			break;

		case enTableCellType_TextBox:
			newCell = new CAsGenericTextCell;
			break;

		case enTableCellType_NumTextBox:
			newCell = new CAsNumericTextCell;
			break;

		case enTableCellType_CheckBox:
			newCell = new CAsCheckBoxCell;
			break;

		case enTableCellType_ComboBox:
			newCell = new CAsComboBoxCell;
			break;

		case enTableCellType_Gradient:
			newCell = new CAsGradientCell;
			break;
	}

	if (newCell) {
		newCell->SetParentWindow(parentWnd_);
		newCell->SetFontInfo(fontInfo_);
		newCell->SetDrawArea(CRect(0, 0, colWidthList_[index], 0));
	}

	if (cells_.size() > index) {
		delete cells_[index];
		cells_[index] =  newCell;
	} else 
		cells_.push_back(newCell);

	SetDrawArea(drawArea_, true);

	return newCell;
}

CAsBaseCell* CAsTableRow::GetCell(int index)
{
	if (index < 0 || index >= cells_.size())
		return NULL;

	return cells_[index];
}

CAsBaseCell* CAsTableRow::GetFocusedCell(int& cellIndex)
{
	CAsBaseCell* cell = NULL;

	for (int i = fixedColumnsNumber_; i < cells_.size(); i++) {
		if (cells_[i]->HasFocus()) {
			cell = cells_[i];
			cellIndex = i;
			break;
		}
	}
	
	return cell;
}

CAsBaseCell* CAsTableRow::GetSelectedCell(int& cellIndex)
{
	CAsBaseCell* cell = NULL;

	for (int i = fixedColumnsNumber_; i < cells_.size(); i++) {
		if (cells_[i]->IsSelected()) {
			cell = cells_[i];
			cellIndex = i;
			break;
		}
	}
	
	return cell;
}

void CAsTableRow::Clear()
{
	for (int i = 0; i < cells_.size(); i++)
		delete cells_[i];

	cells_.clear();
}

void CAsTableRow::SetRowHeight(int height)
{
	rowHeight_ = height;
}

int CAsTableRow::GetRowHeight() const
{
	return rowHeight_;
}

void CAsTableRow::SetColumnWidthList(const vector<int>& widthList)
{
	colWidthList_.clear();

	for (int i = 0; i < widthList.size(); i++)
		colWidthList_.push_back(widthList[i]);
}

void CAsTableRow::SetColumnWidth(int index, int width)
{
	if (index < 0 || index >= colWidthList_.size())
		return;

	colWidthList_[index] = width;
}

void CAsTableRow::SetParentWnd(CWnd* parentWnd)
{
	parentWnd_ = parentWnd;
}

void CAsTableRow::SetFixedColumnsNumber(int number)
{
	fixedColumnsNumber_ = number;
	firstNonFixedColIndex_ = fixedColumnsNumber_;
}

int CAsTableRow::GetFixedColumnsNumber() const
{
	return fixedColumnsNumber_;
}

void CAsTableRow::SetFirstNonFixedColIndex(int index)
{
	firstNonFixedColIndex_ = index;
}

int CAsTableRow::GetFirstNonFixedColIndex() const
{
	return firstNonFixedColIndex_;
}

CAsTableRow::enTableRowHitTest CAsTableRow::HitTest(const CPoint& pt, int& index)
{
	int splitterPos = BORDER;
	CAsBaseCell* cell;
	int i;

	if (!drawArea_.PtInRect(pt))
		return enTableRowHitTest_None;

	for(i = 0; i < fixedColumnsNumber_ && i < colWidthList_.size(); i++) {
		
		index = i;
		splitterPos += colWidthList_[i];

		if (pt.x >= splitterPos - SplitterArea_ && pt.x <= splitterPos + SplitterArea_)
			return enTableRowHitTest_Splitter;

		cell = cells_[i];
		if (cell->HitTest(pt) && i < fixedColumnsNumber_)
			return enTableRowHitTest_FixedColumn;

		if (cell->HitTest(pt))
			return enTableRowHitTest_NonFixedColumn;

		splitterPos += BORDER;
	}

	for(i = firstNonFixedColIndex_; i < colWidthList_.size(); i++) {
		
		index = i;
		splitterPos += colWidthList_[i];

		if (pt.x >= splitterPos - SplitterArea_ && pt.x <= splitterPos + SplitterArea_)
			return enTableRowHitTest_Splitter;

		cell = cells_[i];
		if (cell->HitTest(pt) && i < fixedColumnsNumber_)
			return enTableRowHitTest_FixedColumn;

		if (cell->HitTest(pt))
			return enTableRowHitTest_NonFixedColumn;

		splitterPos += BORDER;
	}

	return enTableRowHitTest_None;
}

bool CAsTableRow::ProcessMessage(DWORD msg, DWORD lParam, DWORD wParam, const CPoint& pt, const CRect& parentRect, bool& needRedraw)
{
	bool processed;
	//enOptionRowHitTest hitTest;
	CAsBaseCell* cell;
	
	for (int i = 0; i < cells_.size(); i++) {
		cell = cells_[i];
		processed = cell->ProcessMessage(msg, lParam, wParam, pt, parentRect, needRedraw);
	}

	return processed;
}

void CAsTableRow::SetVisible(bool visible)
{
	visible_ = visible;
}

bool CAsTableRow::IsVisible() const
{
	return visible_;
}