// GridRow.cpp
// e.moon 5may99 (GridView layout manager)

#include "GridRow.h"
#include "GridView.h"

#include "debug_tools.h"

__USE_CORTEX_NAMESPACE

// ---------------------------------------------------------------- //
// ctor/dtor/accessors
// ---------------------------------------------------------------- //

GridRow::~GridRow() {}

GridRow::GridRow(
	float height,
	int32 flags,
	void* pCookie) :
	
	// initializers
	m_cells(s_defCellVectorSize),
	m_height(height),
	m_borderHeight(0.0),
	m_yOffset(0.0),
	m_flags(flags),
	m_pCookie(pCookie) {}
		
// ---------------------------------------------------------------- //
// private (GridView support) operations
// ---------------------------------------------------------------- //

// look for a cell *starting* in the provided column
BView* GridRow::cellAt(uint32 column) const {
//	D_PRINTF("cellAt(%ld): %ld cells\n", column, m_cells.size());
	return (column < m_cells.size()) ?
		m_cells[column].pCell : 0;
}

// see if there's room for a cell with a given position & column-span
bool GridRow::canAddCell(
	BView* pCell,
	uint32 column,
	uint32 columnSpan) const {

	// validate
	if(!columnSpan)
		return false;

	// see if the cell would overlap any existing cells:
	if(m_cells.size() > column) {

		// walk forward from target column to last column spanned, making
		// sure they're not already covered
		for(uint32 offset = column; offset < column+columnSpan; offset++)
			if(m_cells[offset].pCell)
				// column used
				return false;
		
		// walk backward from column to first existing cell; see if
		// it covers the target column
		// +++++ untested and weird 11may99 e.moon
		cell_vector::const_reverse_iterator ritLast;
		for(ritLast = cell_vector::const_reverse_iterator(m_cells.begin() + column);
			ritLast != m_cells.rend();
			ritLast++)
			if((*ritLast).pCell)
				break;

		if(ritLast != m_cells.rend() &&
			((m_cells.rend() - ritLast)-1) + (*ritLast).columnSpan > column)
			// column covered by leftward cell
			return false;			
	}

	// no overlaps	
	return true;
}

status_t GridRow::addCell(
	BView* pCell,
	uint32 column,
	uint32 columnSpan,
	uint32 rowSpan) {
	
	// validate (call canAddCell() yourself if you don't want
	// assertions blowing up in yer face)

	// Note: rowSpan is allowed to be 0; this indicates that the cell
	// spans multiple rows, and that this NOT the first one (the 'origin row').
	// 4may99 e.moon
	
	ASSERT(columnSpan > 0);
	ASSERT(canAddCell(pCell, column, columnSpan));

	// make sure there's room
	if(m_cells.size() < column + columnSpan)
		m_cells.resize(column + columnSpan);

	// add it
	m_cells[column] = CellEntry(pCell, columnSpan, rowSpan);
	
//	PRINT(( // +++++ ?
//		"GridRow::addCell(%ld): span [%ld,%ld]\n",
//		column, m_cells[column].columnSpan, m_cells[column].rowSpan));

	return B_OK;
}

status_t GridRow::removeCell(uint32 column, BView** poCell) {
	// validate
	ASSERT(column < m_cells.size());
	
	if(!m_cells[column].pCell) {
		PRINT((
			"GridRow::removeCell(%ld) failed: span [%ld,%ld]\n",
				column, m_cells[column].columnSpan, m_cells[column].rowSpan));
		return B_ERROR;
	}
		
	*poCell = m_cells[column].pCell;

	// initialize (zero) the entry
	m_cells[column] = CellEntry();
	
	return B_OK;
}

// find a cell, if it overlaps the given column
// returns true if found, false if no cell overlaps the given column

bool GridRow::hitTest(
	uint32 column, BView** poCell, uint32* poOriginColumn,
	uint32* poColumnSpan) const {
	
	if(column >= m_cells.size())
		return false; // out of range
	
	// scan leftward to the first occupied cell
	for(int n = column; n; n--) {
		if(m_cells[n].pCell) {
			if(m_cells[n].columnSpan + n > column) {
				*poCell = m_cells[n].pCell;
				*poOriginColumn = n;
				if(poColumnSpan)
					*poColumnSpan = m_cells[n].columnSpan;
				return true;
			} else {
				// found a cell, but it doesn't overlap; my job is done
				break;
			}
		}
	}
				
	return false;		
}

// returns the number of rows spanned by the cell at the given column,
// 0 if this slot is overlapped by a cell in a row above this one,
// -1 if no cell overlaps the given column at this row	
int32 GridRow::rowSpan(uint32 column) const {
	if(column >= m_cells.size())
		return -1;
	return (!m_cells[column].pCell) ? (int32)-1 : m_cells[column].rowSpan;
}

// returns the number of columns spanned by the cell at the given
// column (which must be the first column spanned by that cell), or
// 0 if no cell has its origin in the given column

uint32 GridRow::columnSpan(uint32 column) const {
	if(column >= m_cells.size() || !m_cells[column].pCell)
		return 0;
	return m_cells[column].columnSpan;
}

// add a column to the row, shifting following cell entries
// appropriately
status_t GridRow::insertColumn(uint32 column) {

	if(column >= m_cells.size())
		m_cells.resize(column + 1);

	// insert an empty entry	
	m_cells.insert(m_cells.begin() + column, CellEntry());

	return B_OK;
}

// remove a column from the row
status_t GridRow::deleteColumn(uint32 column) {
	ASSERT(column < m_cells.size());
	
	// clean up cell view
	BView* pCell = m_cells[column].pCell;
	if(pCell) {
		if(pCell->Parent())
			pCell->RemoveSelf();
		delete pCell;
	}
	
	// erase cell entry
	m_cells.erase(m_cells.begin() + column);

	return B_OK;
}

void GridRow::deleteViews() {
	// delete all cells
	for(cell_vector::iterator it = m_cells.begin();
		it != m_cells.end(); it++) {
		BView* pCell = (*it).pCell;
		if(pCell) {
			if(pCell->Parent())
				pCell->RemoveSelf();
			delete pCell;
		}
	}
}


// END -- GridRow.cpp --
