#pragma once
#include <comdef.h>
#include <comutil.h>
#include <vector>
#include <set>

// CaplExcel

#ifdef APL_EXCEL_IMPL
	#define CLASS_DECL_APL_EXCEL  _declspec(dllexport)
#else
	#define CLASS_DECL_APL_EXCEL  _declspec(dllimport)

#ifdef _DEBUG
#ifdef UNICODE
#pragma comment (lib,"apl_excel_ud.lib")
#else
#pragma comment (lib,"apl_excelD.lib")
#endif
#else
#ifdef UNICODE
#pragma comment (lib,"apl_excel_u.lib")
#else
#pragma comment (lib,"apl_excel.lib")
#endif
#endif

#endif

class CaplExcelFile;
class CaplExcelSheet;

/*
class CLASS_DECL_APL_EXCEL CaplExcel 
{
public:
	CaplExcel();
	virtual ~CaplExcel();

	CaplExcelFile *OpenFile(LPCSTR fname, bool bReadOnly=false); // Открывает файл

	void SetVisible(bool bVisible); // Если true - делает окно Excel видимым (удобно для отладкаи) По умолчанию - false;

protected:
	void *m_pExcel;
};
*/

//******************************************************************
class CLASS_DECL_APL_EXCEL CaplExcelFile // Создается из CaplExcel::OpenFile
{
	friend CaplExcelSheet;
public:
	CaplExcelFile();
	//CaplExcelFile(void *excel,void *book);  // вручную не создавать! 
	~CaplExcelFile();

	static bool SetVisible(bool bVisible); // Если true - делает окно Excel видимым (удобно для отладкаи) По умолчанию - false; Возвращает true - если успешно, и false при ошибке

	bool Create();
	bool Open(LPCTSTR fname, bool bReadOnly=false, CString *err = 0); // Открывает файл
	bool Save();	 // Сохраняет файл
	bool SaveAs(LPCTSTR fname);
	bool Close();	 // Закрывает файл. 

	//CaplExcelSheet *OpenSheet(int sheet_num, bool bActivate=true);       // Открывает лист по номеру ( с 1)
	//CaplExcelSheet *OpenSheet(LPCSTR sheet_name, bool bActivate=true);   // Открываетй лист по имени

	//CaplExcelSheet *AddSheet(LPCSTR sheet_name, bool bActivate=true, int iNum=-1);    // Добавляет новый лист bNum =- номер нового листа (0- в начало, -1- в конец)

	static bool InitExcel();

	bool IsValid() { return m_bValid; }
	void SetValid(bool bValid) { m_bValid = bValid;  }

protected:

	bool m_bValid;
	void *m_pWorkBook;
};


//******************************************************************
class CLASS_DECL_APL_EXCEL CaplExcelSheet 
{
public:
	CaplExcelSheet(); 
	~CaplExcelSheet();	

	enum aplXlChartType	// Это копия из excel9.tlh, чтобы не тянуть его за собой 
	{
		xlPie = 5,
		xlColumnClustered = 51,
		xlColumnStacked = 52,
		xlBarClustered = 57,		
		xlXYScatterSmoothNoMarkers = 73,
		xlXYScatterLines = 74
	};

	enum aplXlDataLabelPosition
	{
		xlLabelPositionCenter = -4108,
		xlLabelPositionNone = -1,
		xlLabelPositionAbove = 0,
		xlLabelPositionBelow = 1,
		xlLabelPositionLeft = -4131,
		xlLabelPositionRight = -4152,
		xlLabelPositionOutsideEnd = 2,
		xlLabelPositionInsideEnd = 3,
		xlLabelPositionInsideBase = 4,
		xlLabelPositionBestFit = 5,
		xlLabelPositionMixed = 6,
		xlLabelPositionCustom = 7
	};

	enum aplXlLegendPosition
	{
		aplXlLegendPositionBottom = -4107,
		aplXlLegendPositionRight = -4152
	};

	enum aplXlDirection  // Это копия из excel9.tlh, чтобы не тянуть его за собой 
	{
		aplxlDown = -4121,
		aplxlToLeft = -4159,
		aplxlToRight = -4161,
		aplxlUp = -4162
	};

	enum aplXlWindowView
	{
		aplxlNormalView = 1,
		aplxlPageBreakPreview = 2,
		aplxlPageLayoutView = 3
	};

	enum aplXlColontilePosition
	{
		aplXlPosLeft,
		aplXlPosCenter,
		aplXlPosRight
	};

	struct SRangeFormula
	{
		int _row1;
		int _col1;
		int _row2;
		int _col2;
	};

	struct STableStyle
	{
		int _row1;
		int _col1;
		int _row2;
		int _col2;

		int _headerRows;
		int _footerRows;

		unsigned long _headerColor;
		unsigned long _footerColor;
		unsigned long _headerFontColor;
		unsigned long _footerFontColor;

		unsigned long _line1Color;
		unsigned long _line2Color;
	};

	class CChartInfo
	{
	public:
		int _rowStart;	
		int _colStart;
		int _rowEnd;
		int _colEnd;

		aplXlChartType				_chartType;
		aplXlLegendPosition 		_legendPosition;
		CRect						_chartRect;
		CString						_chartTitle;
		aplXlDataLabelPosition		_labelPosition;
		double						_dMinXValue;
		double						_dMaxXValue;
		bool						_bXMaxValueIsAuto;
		bool						_bXMinValueIsAuto;
		
		std::vector<CString> _seriesNames;
		std::vector<std::vector<SRangeFormula>> _seriesXValues;
		std::vector<std::vector<SRangeFormula>> _seriesYValues;

		CChartInfo()
		{
			_rowStart = 1;	
			_colStart = 1;
			_rowEnd = 1;
			_colEnd = 1;

			_labelPosition = xlLabelPositionNone;
			_legendPosition = aplXlLegendPositionBottom;
			_chartRect = CRect(0, 0, 200, 100);			
			_dMinXValue=0;
			_dMaxXValue=0;
			_bXMaxValueIsAuto= true;
			_bXMinValueIsAuto= true;
		}
	};	
	static unsigned long createRGB(int r, int g, int b);

	//param sheet_name- имя листа. Должно не превышать 32 символа (по крайней мере, для Office 2010 это так)
	bool AddSheet(CaplExcelFile *file, LPCTSTR sheet_name, bool bActivate=true, int iNum=-1);    // Добавляет новый лист bNum =- номер нового листа (0- в начало, -1- в конец)
	bool OpenSheet(CaplExcelFile *file, int sheet_num, bool bActivate=true);       // Открывает лист по номеру ( с 1)
	bool OpenSheet(CaplExcelFile *file, LPCTSTR sheet_name, bool bActivate=true);   // Открываетй лист по имени

	bool DeleteSheet(CaplExcelFile *file, int iNum);
	bool HideSheet(CaplExcelFile *file, int iNum);

	int GetSheetsCount();

	void Close(); // закрывает лист

	bool Activate(); //Делает текущим
	bool SetName(LPCSTR newName); // Переназывает лист
	bool GetName(CString &sName); // Получает текущее имя листа
	bool CopyFrom(CaplExcelSheet *sheet);    // Создает копию листа. newName - именем нового листа

	int GetRowsCount();     //Количество строк в текущем листе
	int GetColunmsCount();  //Количество столбцов в текущем листе

	bool SetThroughRows(LPCTSTR rows);			// устанавливает сквозные строки. пример "$1:$2" - строки 1 и 2 будут повторяться при печати	
	bool SetThroughColumns(LPCTSTR columns);	// устанавливает сквозные столбцы. пример "$B:$B" - колонка B будет повторяться при печати	

	bool SetCellWordBreak(int row, int col);	// перенесо по словам для ячейки
	bool SetColontileHeader(LPCTSTR text, aplXlColontilePosition pos = aplXlPosCenter);		// установка верхнего колонтитула
	bool SetColontileFooter(LPCTSTR text, aplXlColontilePosition pos = aplXlPosCenter);		// установка нижнего колонтитула

	bool SetFreezeArea(LPCSTR range); // Устанавливает диапазон, в котором запрещена прокрутка

	bool GetVPageBreaks(std::vector<int> &breaks); // Читает вертикальные разрывы страницы для печати (номера столбцов)

	int GetColumnBN(LPCSTR name, int max_col=100); // Возвращает номер столбца по заголоку (в первой строчке) max_col - кол-во перебираемых столбцов

	bool GetColumnWidthInPx(int ColNum, double &width);// Читает ширину столбца в пикселях
	bool GetRowHeightInPx(int rowNum, double &height);// Читает высоту строки в пикселях
	bool GetRangeHeightInPx(int rowFrom, int rowTo, double &height);// Читает высоту диапазона в пикселях
	bool GetRangeWidthInPx(int colFrom, int colTo, double &width);// Читает ширину диапазона в пикселях
	bool GetRowTopInPx(int rowNum, double &dTop);// Читает верхнюю границу строки в пикселях
	int GetRowUnderPx(int nPix, int nBeginRow = 1);	// Выдает 1-ю строку верхняя граница которой ниже nPix

	bool GetColumnWidth(int ColNum, double &width); // Читает ширину столбца
	bool SetColumnWidth(int ColNum, double  width); // Устанавливает ширину столбца
	bool SetRowHeight(int rowNum, double height);	// Устанавливает высоту строки
	bool SetRowsHeight(int rowNum1, int rowNum2, double height);// Устанавливает высоту строк
	bool GetRowHeight(int rowNum, double &height); // Читает высоту строки

	bool SetCell(LPCTSTR cell, LPCTSTR val);	   // Устанавливает значение ячейки по имени (например, "B7")
	bool SetCell(int row, LPCTSTR, LPCTSTR val); // Устанавливает значение ячейки по номеру строки и буквам столбца (например, "AC")
	bool SetCell(int row, int col, LPCTSTR val=NULL, LPCTSTR sFormat=NULL);// Устанавливает значение ячейки по номеру строки и номеру столбца 
	bool SetCell(int row, int col, _variant_t val, LPCTSTR sFormat=NULL);
protected:
	COleSafeArray* m_pBufferArr;
	COleSafeArray* m_pBufferFormatArr;
	COleSafeArray* m_pBufferMergeHorizArr;
	COleSafeArray* m_pBufferMergeVertArr;
	UINT m_iBufferRowStart;
	UINT m_iBufferColStart;
	UINT m_iBufferRowEnd;
	UINT m_iBufferColEnd;
public:
	static void FillSafeArray(_variant_t val, int iRow, int iCol, COleSafeArray* saValue, OLECHAR FAR* valFormat = NULL, COleSafeArray* saFormat = NULL) ;

	COleSafeArray* GetBufferArray() {return m_pBufferArr;};
	COleSafeArray* GetBufferFormatArray() {return m_pBufferFormatArr;};
	COleSafeArray* GetBufferMergeHorizArray() {return m_pBufferMergeHorizArr;};
	COleSafeArray* GetBufferMergeVertArray() {return m_pBufferMergeVertArr;};
	//установить режим записи данных в буфер функциями типа SetCell(...)
	void UseBufferArray(int rowStart=1, int colStart=1, int rowEnd=1000, int colEnd=100, bool bUse= true);
	bool SetBufferArray(int row1, int col1, int row2, int col2, COleSafeArray* pValArr, COleSafeArray* pFormatArr=NULL, COleSafeArray* pMergeHorizArr=NULL, COleSafeArray* pMergeVertArr=NULL);
	//"записать" данные из буфера в ячейки Excel
	bool SetCellsByBufferArray();
	//объединение ячеек на основе буфера
	void MergeCellsByBuffer();
	//очистка указателей на буферные массивы, чтобы можно быть пользоваться SetCell(...) для "непосредственной" записи
	void ClearBufferArrayPtrs();

	bool SetCellsByArray(int row1, int col1, int row2, int col2, COleVariant& arr);

	bool GetCellText(int row, int col, CString &val);
	bool GetCell(LPCTSTR cell, CString &val);		 // Читает значение ячейки по имени (например, "B7")
	bool GetCell(int row, LPCTSTR col, CString &val); // Читает значение ячейки по номеру строки и буквам столбца (например, "AC")
	bool GetCell(int row, int col, CString &val, _variant_t* varTval=NULL, bool bAsText = false);	 // Читает значение ячейки по номеру строки и номеру столбца 

	bool InsertRow(int RowNum); // Вставить строку RowNum - строка с новым номером (если RowNum=7, то бывшая строка 7  станет воьмой)
	bool InsertCol(int ColNum); // Вставить cтолбец СщдColNum - толбец с новым номером (если RowNum=7, то бывшый 7  станет 8)
	bool DeleteRow(int RowNum);
	bool DeleteCol(int ColNum);

	bool Merge(LPCTSTR range, bool bConvertFromR1C1=false); // Объединяет ячейки range  вформате "B10:C12"
	bool Merge(int row1, int col1, int row2, int col2);

	bool Find(LPCTSTR val, std::set<std::pair<int, int>>& out_row_col);			// поиск значения по всем ячейкам (возвращает пары строка-столбец)
	bool Find(LPCTSTR val, int row1, int col1, int row2, int col2, std::set<std::pair<int, int>>& out_row_col); // поиск значения по диапазону ячеек
protected:
	bool Find(LPCTSTR val, void* pRange, std::set<std::pair<int, int>>& out_row_col);
public:

	bool SetVertiacalText(int row, int col);
	bool FitContentOnePage();
	bool SortRangeByColumnDescending(SRangeFormula &range);			// сортировка диапазона			
	bool FitColumnsInOnePage();										// вписать все столбцы на одну страницу (настройки печати)
	bool SetLandScapeOrientation();									// устанавливает горизонтальную ориентацию страницы
	bool SetPortraitOrientation();									// устанавливает вертикальную ориентацию страницы
	bool AutoFitColumns();											// изменяет ширину колонок под размер максимального текста
	bool MakeCellBorder(int row, int col, bool mediumWeight = false);	// задать границу для ячейки		
	bool MakeCellColor(int row, int col, int colorIndex);			// задать цвет ячейки	
	bool Select(LPCTSTR range, bool bConvertFromR1C1=false); 		// выделет ячейки
	bool AddIndentToCell(int row, int col, long indent);			// добавить отступ в ячейку
	bool SetCellFontSize(int row, int col, int size);				// устанавливает размер шрифта	
	bool SetWrapText(int row, int col);								// переносить по словам 
	bool SetWrapText(int row1, int col1, int row2, int col2);		// переносить по словам (задаем сразу большой диапазон
	bool SetCellBold(int row, int col);								// устанавливает жирный стиль шрифта
	bool SetCellBold(int row1, int col1, int row2, int col2);		// устанавливает жирный стиль шрифта
	bool SetCellUnderline(int row, int col);						// устанавливает подчеркнутый стиль шрифта
	bool SetCellUnderline(int row1, int col1, int row2, int col2);	// устанавливает подчеркнутый стиль шрифта
	bool MakeAlignCenter(int row, int col);							// выравнивает содержимое ячейки по центру	
	bool MakeAlignCenter(int row1, int col1, int row2, int col2);	// выравнивает содержимое интервала по центру	
	bool MakeVAlignCenter(int row, int col);						// выравнивает содержимое ячейки по центру	
	bool MakeVAlignCenter(int row1, int col1, int row2, int col2);	// выравнивает содержимое интервала по центру	
	bool MakeVAlignTop(int row, int col);							// выравнивает содержимое ячейки по верху
	bool MakeVAlignTop(int row1, int col1, int row2, int col2);		// выравнивает содержимое интервала по верху	
	bool MakeVAlignBottom(int row, int col);						// выравнивает содержимое ячейки по низу	
	bool MakeVAlignBottom(int row1, int col1, int row2, int col2);	// выравнивает содержимое интервала по низу	
	bool MakeAlignLeft(int row, int col);							// выравнивает содержимое ячейки по левому краю
	bool MakeAlignLeft(int row1, int col1, int row2, int col2);		// выравнивает содержимое ячейки по левому краю
	bool MakeAlignRight(int row, int col);							// выравнивает содержимое ячейки по правому краю
	bool MakeAlignRight(int row, int col, int row2, int col2);		// выравнивает содержимое ячейки по правому краю

	// получение и установка атрибута типа Text Align
	int GetCellTextHAlign(int row, int col);
	int SetCellTextHAlign(int row1, int col1, int row2, int col2, int align);
	int GetCellTextVAlign(int row, int col);
	int SetCellTextVAlign(int row1, int col1, int row2, int col2, int align);

	// функции проверки на какое-либо свойство ячейки
	bool IsCellBold(int row, int col);
	bool IsCellUnderLine(int row, int col);
	bool IsCellBorder(int row, int col);
	bool IsCellMerged(int row, int col);	

	long  GetCellBorder(int row, int col);
	bool  SetCellBorder(int row1, int col1, int row2, int col2, long value);

	// насколько ячейка объединена колонок и строк (важно начинать с первой)
	// найти ее можно при помощи IsCellMerged
	int HowMergedRows(int row, int col);
	int HowMergedColumns(int row, int col);

	/** например range = $A$1, повторяет первую строку*/
	bool RepeatRangeOnPrint(LPCTSTR range);

	bool SetCellFontSize(int row1, int col1, int row2, int col2, int size);				// устанавливает размер шрифта
	bool MakeCellBorder(int row1, int col1, int row2, int col2, bool mediumWeight = false);	// задать границу для ячейки		
	bool MakeCellColor(int row1, int col1, int row2, int col2, int colorIndex);

	// узнать размер шрифта в ячейке
	int GetCellFontSize(int row, int col);

	// получить цвет ячейки в hex
	unsigned long GetCellColorRGB(int row, int col);

	bool MakeCellColorRGB(int row1, int col1, int row2, int col2, int r, int g, int b);
	bool MakeTextColorRGB(int row1, int col1, int row2, int col2, int r, int g, int b);
	bool MakeTextColorRGB(int row, int col, int r, int g, int b);
	bool MakeCellColorRGB(int row, int col, int r, int g, int b);
	bool MakeTextColorRGB(int row, int col, COLORREF color);
	bool MakeCellColorRGB(int row, int col, COLORREF color);

	// ВНИМАТЕЛЬНЕЕ!! МОЖЕТ БЫТЬ ВЫЗВАНА ФУНКЦИЯ ВЫШЕ!!!! 
	bool MakeTextColorRGB(int row1, int col1, int row2, int col2, unsigned long colorInHex);
	bool MakeCellColorRGB(int row1, int col1, int row2, int col2, unsigned long colorInHex);


	bool AddChart(CChartInfo &info);								// добавить диаграмму сразу на основе диапазона данных
	bool AddChartEx(CChartInfo &info, CaplExcelSheet* chart_sheet = NULL);								// добавить диаграмму на основе нескольких серий данных
	bool AddImage(CRect &rect, CString &path);						// добавить изображение в ячейку
	
	bool CopyRange(LPCTSTR from, LPCTSTR to, aplXlDirection direction, bool bConvertFromR1C1=false); // Копирует диапазоны ячеек
	bool CopyRangeWithoutShift(LPCTSTR from, LPCTSTR to, bool bConvertFromR1C1=false); // Копирует диапазоны ячеек
	bool CopyRangeWithoutShift(int source_row1, int source_col1, int source_row2, int source_col2, int dest_row1, int dest_col1, int dest_row2, int dest_col2);  // Копирует диапазоны ячеек
	bool DeleteRange(LPCTSTR range,  aplXlDirection direction, bool bConvertFromR1C1=false); // Удаляет ячейки

	// применить цветовую схему для таблицы	(серо-синий)
	bool ApplyStyleBlueGray(int row1, int col1, int row2, int col2, int headerRows = 1, int footerRows = 1);

	// применить цветовую схему для таблицы	(оранжево-серый)
	bool ApplyStyleOrangeGray(int row1, int col1, int row2, int col2, int headerRows = 1, int footerRows = 1);	

	// применить цветовую схему для таблицы	(серый-серый)
	bool ApplyStyleGrayGray( int row1, int col1, int row2, int col2, int headerRows = 1, int footerRows = 1 );

	// применить цветовую схему для таблицы	(бело-зеленая)
	bool ApplyStyleGreen(int row1, int col1, int row2, int col2, int headerRows = 1, int footerRows = 1);

	// выводить тире вместо нулей в ячейках
	void SetDashInsteadZero(bool value = true) { m_dashInsteadZero = value; };

	// добавить горизонтальный разрыв листа
	bool AddPageBreak(int row);

	// добавить вертикальный разрыв листа
	bool AddPageBreakVert(int col);

	// установить масштаб листа
	bool SetPageZoom(int size);

	// установить режим просмотра листа (обычный, страничный, разметка страницы)
	bool SetViewMode(aplXlWindowView xlViewMode);

	// добавить комментарий к ячейке листа
	bool AddComment(int row, int col, LPCTSTR text);

protected:
	void *m_pWorkSheet;
	bool m_dashInsteadZero;

	bool SetOrientation(int orientation);
	bool ApplyStyleToTable(STableStyle &style);	

	bool IfZeroValue(CString value);
};


