/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* librvngabw
 * Version: MPL 2.0 / LGPLv2.1+
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Major Contributor(s):
 * Copyright (C) 2002-2003 William Lachance (wrlach@gmail.com)
 * Copyright (C) 2004 Fridrich Strba (fridrich.strba@bluewin.ch)
 *
 * For minor contributions see the git repository.
 *
 * Alternatively, the contents of this file may be used under the terms
 * of the GNU Lesser General Public License Version 2.1 or later
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
 * applicable instead of those above.
 *
 * For further information visit http://libwpd.sourceforge.net
 */

/* "This product is not manufactured, approved, or supported by
 * Corel Corporation or Corel Corporation Limited."
 */

#ifndef _TABLE_HXX_
#define _TABLE_HXX_

#include <iterator>
#include <map>
#include <set>
#include <vector>

#include <librvngabw/librvngabw.hxx>

// for std::shared_ptr
#include "FilterInternal.hxx"
#include "DocumentElement.hxx"

namespace librvngabw
{
class TableManager;

//! internal structure used to store a cell content
struct TableCell
{
	//! constructor
	TableCell() : m_position(0,0), m_span(1,1), m_repeated(1,1), m_cellPropList(), m_content()
	{
	}
	//! copy constructor (used to copy empty cell)
	TableCell(TableCell const &orig) : m_position(orig.m_position), m_span(orig.m_span), m_repeated(orig.m_repeated),
		m_cellPropList(orig.m_cellPropList), m_content()
	{
		if (orig.m_content.empty())
			return;
		RVNGABW_DEBUG_MSG(("TableCell::TableCell: must not be called if cell has content, the content is ignored\n"));
	}
	//! copy constructor (used to copy empty cell)
	TableCell &operator=(TableCell const &orig)
	{
		if (this==&orig)
			return *this;
		m_position=orig.m_position;
		m_span=orig.m_span;
		m_repeated=orig.m_repeated;
		m_cellPropList=orig.m_cellPropList;
		// append the content of orig and clear orig
		librvngabw::spliceOrigDest(const_cast<TableCell &>(orig).m_content, m_content);
		return *this;
	}
	//! reset the cell to an empty cell
	void clear()
	{
		m_span=m_repeated=RVNGABWVec2i(1,1);
		m_cellPropList.clear();
		m_content.clear();
	}
	//! returns true is the cell contains no data
	bool isEmpty() const
	{
		return m_content.empty();
	}
	//! adds a cell to the table content
	void addTo(DocumentElementVector &tableContent, int &xId, RVNGABWVec2i const &decal=RVNGABWVec2i(0,0)) const;
	//! the cell position
	RVNGABWVec2i m_position;
	//! the cell span
	RVNGABWVec2i m_span;
	//! the cell repeated
	RVNGABWVec2i m_repeated;
	//! the cell property list, must first be added after updating attach
	mutable librevenge::RVNGPropertyList m_cellPropList;
	//! the cell content
	DocumentElementVector m_content;
};

//! internal structure used to store row data: mainly the list of sent cell
struct TableRow
{
	//! constructor
	explicit TableRow(int repeated=1) : m_repeated(repeated), m_height(0), m_fillColumnSet()
	{
	}
	//! the number of time a row is repeated
	int m_repeated;
	//! the row height in inch
	double m_height;
	//! a set to keep the trace of sent columns
	std::set<RVNGABWVec2i> m_fillColumnSet;
};

/** basic class used to store a table */
class Table
{
public:
	//! constructor
	Table(TableManager &manager, const librevenge::RVNGPropertyList &xPropList, int &xId, bool isSpreadsheet);
	//! destructor
	virtual ~Table();
	//! return the table storage
	DocumentElementVector *getStorage()
	{
		return m_isCellOpened ? &m_cell.m_content : &m_tableContent;
	}
	//! add table content to storage
	void write(DocumentElementVector &storage) const;
	//! check that the table is complete, if not insert empty cell
	void checkTable();

	//! open a row
	bool openRow(const librevenge::RVNGPropertyList &propList);
	//! close a row
	bool closeRow();
	//! returns true if a row is opened
	bool isRowOpened() const
	{
		return m_isRowOpened;
	}
	//! open a cell
	bool openCell(const librevenge::RVNGPropertyList &propList);
	//! close a cell
	bool closeCell();
	//! insert a covered cell
	bool insertCoveredCell(const librevenge::RVNGPropertyList &propList);
	//! returns true if a cell is opened
	bool isCellOpened() const
	{
		return m_isCellOpened;
	}
protected:
	//! insert a list of cell
	void addCellInserted(RVNGABWVec2i const &cellPos,
	                     RVNGABWVec2i const &span);
	//! split row if needed
	void splitRow(int row);
private:
	//! the table manager
	TableManager &m_manager;
	//! the table proplist
	librevenge::RVNGPropertyList m_propList;
	//! the table id
	int &m_xId;
	//! a flag to know if the table is converted from a spreadsheet
	bool m_isSpreadsheet;
	//! the table content
	DocumentElementVector m_tableContent;
	//! the current row
	TableRow m_row;
	//! the current cell
	TableCell m_cell;
	//! the actual row
	int m_actualRow;
	//! the actual column
	int m_actualColumn;
	//! flag to know if a row is opened
	bool m_isRowOpened;
	//! flag to know if a cell is opened
	bool m_isCellOpened;
	//! the last column/row filled in the table
	RVNGABWVec2i m_lastFillPosition;
	//! list of cell inserted for each row row
	std::map<int, TableRow> m_rowDataMap;
	//! list of delayed empty cell(spreadsheet)
	std::vector<TableCell> m_delayedCellList;
	// disable copying
	Table(const Table &);
	Table &operator=(const Table &);
};

class ParagraphStyleManager;
class SpanStyleManager;

/** class which stores all the tables
 */
class TableManager
{
public:
	//! constructor
	TableManager(SpanStyleManager &spanManager, ParagraphStyleManager &paragraphManager);
	//! destructor
	virtual ~TableManager();
	//! clean all data
	void clean();
	//! returns true if a table is opened
	bool isTableOpened() const
	{
		return !m_tableOpenedList.empty();
	}
	//! returns the actual table
	std::shared_ptr<Table> getActualTable()
	{
		if (m_tableOpenedList.empty()) return std::shared_ptr<Table>();
		return m_tableOpenedList.back();
	}
	//! returns the actual table
	std::shared_ptr<Table const> getActualTable() const
	{
		if (m_tableOpenedList.empty()) return std::shared_ptr<Table const>();
		return m_tableOpenedList.back();
	}
	//! open a table and update the list of elements
	std::shared_ptr<Table> openTable(const librevenge::RVNGPropertyList &xPropList, int &xId, bool isSpreadsheet=false);
	//! close a table
	bool closeTable();

	//! returns the span manager
	SpanStyleManager &getSpanManager()
	{
		return m_spanManager;
	}

	//! returns the paragraph manager
	ParagraphStyleManager &getParagraphManager()
	{
		return m_paragraphManager;
	}

private:
	//! the span manager
	SpanStyleManager &m_spanManager;
	//! the paragraph manager
	ParagraphStyleManager &m_paragraphManager;
	//! the list of opened table
	std::vector<std::shared_ptr<Table> > m_tableOpenedList;

	// disable copying
	TableManager(const TableManager &);
	TableManager &operator=(const TableManager &);
};
}

#endif

/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
