#pragma once
/************************************************************************
    MeOS - Orienteering Software
    Copyright (C) 2009-2011 Melin Software HB
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    Melin Software HB - software@melin.nu - www.melin.nu
    Stigbergsvägen 7, SE-75242 UPPSALA, Sweden
    
************************************************************************/

#include <vector>
#include <map>
#include <set>
#include "oBase.h"
#include "inthashmap.h"

#define TableXMargin 40
#define TableYMargin 30

enum CellType {cellEdit, cellSelection, cellAction};
enum KeyCommandCode;

struct TableUpdateInfo {
  bool doAdd;
  oBase *object;
  int id;
  TableUpdateInfo() : doAdd(false), object(0), id(0) {}
};


class TableCell
{
  string contents;
  RECT absPos;
  DWORD id;
  oBase *owner;
  bool canEdit;
  CellType type;

  friend class TableRow;
  friend class Table;
  friend int tblSelectionCB(gdioutput *gdi, int type, void *data); 
};

class TableRow
{
protected:
	vector<TableCell> cells;
  int id;

	string *SortString;
	int sInt;

  int ypos;
  int height;
  oBase *ob;
  
public:
  oBase *getObject() const {return ob;}
	bool operator<(const TableRow &r){return *SortString<*r.SortString;}
	static bool cmpint(const TableRow &r1, const TableRow &r2) {return r1.sInt<r2.sInt;}

	TableRow(int elem, oBase *object): sInt(0)
	{
		cells.resize(elem);
		SortString=&cells[0].contents;
    ob = object;
    id = -1;
	}

	TableRow(const TableRow &t)
	{
		cells=t.cells;
    SortString=&cells[0].contents;
    ob = t.ob;
    id = t.id;
	}
	friend class Table;
};

class gdioutput;

struct RowInfo
{	
	char name[64];
	mutable int width;
  int baseWidth;
	bool isnumeric;
  bool formatRight;
  RECT title;
  RECT condition;
  string filter;
};

struct TableSortIndex
{
  TableSortIndex() {}
  TableSortIndex(int ix, const string &k) : index(ix), key(k) {}
  int index;
  //int ypos;
  string key;
  bool operator<(const TableSortIndex &t) const {return key<t.key;}
  bool operator<=(const TableSortIndex &t) const {return key<=t.key;}
};

class Table
{
protected:
  bool doAutoSelectColumns;

  int t_xpos;
  int t_ypos;

  bool commandLock;
  string tableName;
  string internalName;
	vector<RowInfo> Titles;
  vector<int> xpos;
	unsigned nTitles;
	int PrevSort;
	mutable int rowHeight;
  int baseRowHeight;
	vector<TableRow> Data;
  size_t dataPointer; // Insertation pointer
  vector<TableSortIndex> sortIndex;
  vector<int> columns;
  inthashmap idToRow;
  //highlight
  int highRow;
  int highCol;

  // Selected columns. For drag/drop and sort
  int colSelected;
  int startX;
  int startY;

  //Edit selection
  int editRow;
  int editCol;

  // Selected rectangle
  int upperRow;
  int lowerRow;
  int upperCol;
  int lowerCol;

  int upperRowOld;
  int lowerRowOld;
  int upperColOld;
  int lowerColOld;

  bool startSelect;

  HWND hEdit;

  //For moving objects
  HDC hdcCompatible;
  HBITMAP hbmStored;
  int lastX;
  int lastY;

  // For cell seletion
  HDC hdcCompatibleCell;
  HBITMAP hbmStoredCell;
  RECT lastCell;
  bool partialCell;

  //static bool filterMatchString(const string &c, const char *filt);
  void highlightCell(HDC hDC, gdioutput &gdi, const TableCell &cell, DWORD color, int dx, int dy);

  void moveCell(HDC hDC, gdioutput &gdi, const TableCell &cell, int dx, int dy);
  void startMoveCell(HDC hDC, const TableCell &cell);
  void stopMoveCell(HDC hDC, const TableCell &cell, int dx, int dy);
  void restoreCell(HDC hDC, const TableCell &cell);

  void moveColumn(int src, int target);

  int tableWidth;
  int tableHeight;
  bool drawFilterLabel;
  int currentSortColumn;

  void initEmpty();

  int getColumn(int x, bool limit = false) const;
  int getRow(int y, bool limit = false) const;

  void redrawCell(gdioutput &gdi, HDC hDC, int c, int r);

  //Draw coordinates
  int table_xp;
  int table_yp;

  oEvent *oe;

  void clearCellSelection(gdioutput *gdi);
  void clearSelectionBitmap(gdioutput *gdi, HDC hDC);
  void restoreSelection(gdioutput &gdi, HDC hDC);

  void drawSelection(gdioutput &gdi, HDC hDC, bool forceDraw);
  TableCell &getCell(int row, int col) const; //Index as displayed
  void scrollToCell(gdioutput &gdi, int row, int col);

  bool destroyEditControl(gdioutput &gdi);
  

  void getExportData(int col1, int col2, int row1, int row2, 
                     string &html, string &txt) const; 

  
  // Delete rows in selected range. Return number of rows that could not be removed
  int deleteRows(int row1, int row2);

  void getRowRange(int &rowLo, int &rowHi) const;
  void getColRange(int &colLo, int &colHi) const;
  int ownerCounter;
  DWORD tableProp;

  int selectionRow;
  int selectionCol;

  void getRowRect(int row, RECT &rc) const;
public:

  const string& getTableName() const {return tableName;}
  bool hasAutoSelect() const {return  doAutoSelectColumns;}

  void updateDimension(gdioutput &gdi);
  void selection(gdioutput &gdi, const string &text, int data);

  enum {
    CAN_PASTE = 1,
    CAN_INSERT = 2,
    CAN_DELETE = 4,
  };

  bool canPaste() const {return (tableProp & CAN_PASTE) != 0;}
  bool canInsert() const {return (tableProp & CAN_INSERT) != 0;}
  bool canDelete() const {return (tableProp & CAN_DELETE) != 0;}
  void setTableProp(DWORD w) {tableProp = w;}

  void hide(gdioutput &gdi); //Ensure no edit contol is visible
  void addOwnership() {ownerCounter++;}
  void releaseOwnership();

  void autoAdjust(gdioutput &gdi); // Adjust column widths
  void autoSelectColumns();

  void insertRow(gdioutput &gdi); // Insert a new row in the table
  bool deleteSelection(gdioutput &gdi);
  void setPosition(int x, int y) {t_xpos = x, t_ypos = y;}
  void exportClipboard(gdioutput &gdi);
  void importClipboard(gdioutput &gdi);
  

  bool hasEditControl() {return hEdit!=0;}
  vector< pair<string, bool> > getColumns() const;
  void selectColumns(const set<int> &sel);

  oEvent *getEvent() const {return oe;}
	void getDimension(gdioutput &gdi, int &dx, int &dy, bool filteredResult) const;
	void draw(gdioutput &gdi, HDC hDC, int dx, int dy, 
            const RECT &screen);

  void print(gdioutput &gdi, HDC hDC, int dx, int dy);
  
  //Returns true if capture is taken
  bool mouseMove(gdioutput &gdi, int x, int y);
  bool mouseLeftDown(gdioutput &gdi, int x, int y);
  bool mouseLeftUp(gdioutput &gdi, int x, int y);
  bool mouseLeftDblClick(gdioutput &gdi, int x, int y);

  bool editCell(gdioutput &gdi, int row, int col);

  bool keyCommand(gdioutput &gdi, KeyCommandCode code);
	void sort(int col);
  void filter(int col, const string &filt, bool forceFilter=false);

	int addColumn(const string &Title, int width, bool isnum, bool formatRight = false);

  void reserve(size_t siz);

	void addRow(int rowId, oBase *object);
	void set(int column, oBase &owner, int id, const string &data, 
           bool canEdit=true, CellType type=cellEdit);

  //Reload a row from data
  void reloadRow(int rowId);

  bool UpDown(gdioutput &gdi, int direction);
  bool tabFocus(gdioutput &gdi, int direction);
  bool enter(gdioutput &gdi);
  void escape(gdioutput &gdi);
  bool inputChange(gdioutput &gdi, HWND hEdit);
  void resetColumns();
  void update();

	Table(oEvent *oe_, int rowHeight, 
        const string &name, const string &tname);
	~Table(void);
};


enum {TID_CLASSNAME, TID_COURSE, TID_NUM, TID_ID, TID_MODIFIED,
TID_RUNNER, TID_CLUB, TID_START, TID_TIME,
TID_FINISH, TID_STATUS, TID_RUNNINGTIME, TID_PLACE, 
TID_CARD, TID_TEAM, TID_LEG, TID_CONTROL, TID_CODES, TID_FEE, TID_PAID,
TID_INPUTTIME, TID_INPUTSTATUS, TID_INPUTPOINTS, TID_INPUTPLACE};
