/**************************************************************

selection.h (C-Munipack project)
Table of selected stars
Copyright (C) 2008 David Motl, dmotl@volny.cz

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 2
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

**************************************************************/

#ifndef MUNIWIN_HELPER_CLASSES_H
#define MUNIWIN_HELPER_CLASSES_H

#include <gtk/gtk.h>
#include <cmunipack.h>

#include "callback.h"
#include "cmpack_widgets.h"

#define MAX_TAG_SIZE 1023

// Cell index
struct tIndex {
	int col, row;
	tIndex():col(-1), row(-1) {}
	tIndex(int c, int r):col(c), row(r) {}
};

inline bool operator==(const tIndex &a, const tIndex &b) { return a.col==b.col && a.row==b.row; }
inline bool operator!=(const tIndex &a, const tIndex &b) { return !operator==(a,b); }

// Date time formats
enum tDateFormat {
	JULIAN_DATE,
	GREGORIAN_DATE
};

// File types
enum tFileType
{
	TYPE_UNKNOWN	= 0,
	TYPE_IMAGE		= (1<<0),
	TYPE_PHOT		= (1<<1),
	TYPE_CAT		= (1<<2),
	TYPE_TABLE		= (1<<3),
	TYPE_VARFIND	= (1<<4),
	TYPE_PROJECT    = (1<<5),
	TYPE_PROFILE	= (1<<6),
	TYPE_ALL		= 0xFFFF
};

// Autodetect file type
tFileType FileType(const char *path);

// Project type to recent group
const gchar *FileTypeRecentGroup(tFileType type);

// String representing table type
const gchar *TableTypeToStr(CmpackTableType type);

//
// Console - receives messages from C-Munipack library
//
class CConsole
{
public:
	// Constructor
	CConsole(void) {}

	// Destructor
	virtual ~CConsole(void) {}
	
	// Callback function that calls instance's OnPrint
	static void OutputProc(const char *text, void *user_data);

	// Calls OnPrint
	void Print(const char *text);
	
protected:
	// Must be overriden, message processing
	virtual void OnPrint(const char *text) = 0;
};


//
// Selection of stars
//
class CSelection
{
public:
	enum tSelectionMode 
	{
		ANY,			// Unlimited number of objects of this type is allowed (default)
		SINGLE,			// Only one object of this type is allowed
		NONE			// No objects of this type is allowed
	};
	
	// Constructor(s)
	CSelection();
	CSelection(const gchar *string);
	CSelection(const CSelection &other);
	
	// Destructor
	virtual ~CSelection();

	// Clear data
	void Clear();

	// Begin/end update
	void BeginUpdate(void);
	void EndUpdate(void);

	// Assignment operator
	CSelection &operator=(const CSelection &other);

	// Convert the selection to a string
	gchar *toString(void) const;

	// Set selection mode. These functions are used to set cardinality of 
	// each selection type. 
	void setSelectionModes(tSelectionMode mode);
	void setSelectionMode(CmpackSelectionType type, tSelectionMode mode);
	tSelectionMode selectionMode(CmpackSelectionType type) const { return m_Modes[type]; }

	// Select an object, remove from selection
	void Select(int id, CmpackSelectionType type);

	// Get number of selected stars (total)
	int Count(void) const { return m_Count; }

	// Get object id by index (this is used to enumerate all objects)
	int GetId(int i) const;

	// Get number of stars of specified type
	int CountStars(CmpackSelectionType type) const;

	// Get identifiers of selected star
	int GetStarList(CmpackSelectionType type, int *list, int maxcount) const;
	char *GetStarList(CmpackSelectionType type) const;

	// Set list of selected stars
	void SetStarList(CmpackSelectionType type, const int *list, int count);
	void SetStarList(CmpackSelectionType type, const char *string);

	// Get selection type for given object. If the object
	// is not selected, return CMPACK_SELECT_NONE.
	CmpackSelectionType Type(int starId) const;

	// Get color for given star depending on its selection type.
	// Returns CMPACK_COLOR_DEFAULT if the object is not selected.
	CmpackColor Color(int starId) const;

	// Get sort key. The returned value is an integer number that 
	// is used to sort a table of objects by their type.
	gulong SortKey(int starId) const;

	// Get caption for given object. A captions is used on a chart.
	// Depending on the current selection mode, the objects are 
	// labelled as var, var #2, comp, comp #2, check #1, check #2, ...
	// The function returns pointer to an internal memory buffer, the
	// buffer is valid as far as the selection is not modified.
	// Returns NULL if the object is not selected.
	const gchar *Caption(int starId);

	// Get label for given object. A label is used in a table header.
	// Depending on the current selection mode, the objects are 
	// labelled as V, V2, C, C2, K1, K2, ...
	// The function returns pointer to an internal memory buffer, the
	// buffer is valid as far as the selection is not modified.
	// Returns NULL if the object is not selected.
	const gchar *Label(int starId);

	// Get description for given object. A description is used in
	// object inspector tool and as a remark in tables of objects.
	// Depending on the current selection mode, the objects are 
	// labelled as Variable star, Comparison star, Check star, ...
	// The function returns pointer to an internal memory buffer, the
	// buffer is valid as far as the selection is not modified.
	// Returns NULL if the object is not selected.
	const gchar *Description(int starId);

protected:
	struct tStarRec
	{
		int id;
		CmpackSelectionType type;
		int index;
		gchar *label, *caption, *description;
	};

	struct tReindexInfo
	{
		int			var_index;
		int			comp_index;
		int			chk_index;
	};

	struct tLabelOther
	{
		int			id;
		gchar		*label;
		tLabelOther	*next;
	};

	int m_Count, m_Capacity;
	tStarRec *m_List;
	bool m_Changed;
	int m_LockCount;
	tSelectionMode m_Modes[CMPACK_SELECT_COUNT];
	tLabelOther *m_LabelsOther;

	// Append object to the end of the list
	void Append(int id, CmpackSelectionType type);

	// Remove object from the list
	void RemoveAt(int index);

	// Remove object from the list
	void Remove(int id);

	// Update item in the list
	void Update(int index, int id, CmpackSelectionType type);

	// Search star by its id
	int Search(int id) const;

	// Refresh selection indices
	void Reindex(void);

	// Callback emission is postponed if the object is locked
	void Changed(void);

	// Remove all objects of specified type
	void Unselect(CmpackSelectionType type);

	// Update label for a selected object
	void UpdateLabel(int index, const gchar *buf);

	// Update label for un-selected object
	void UpdateLabelOther(int objId, const gchar *buf);

	// Get label for un-selected object
	gchar *GetLabelOther(int objId);

	// Update caption for a selected object
	void UpdateCaption(int index, const gchar *buf);

	// Update description for a selected object
	void UpdateDescription(int index, const gchar *buf);

	// Comparison operators
	friend bool operator==(const CSelection&, const CSelection&);
};

bool operator==(const CSelection &a, const CSelection &b);
inline bool operator!=(const CSelection &a, const CSelection &b) { return !operator==(a,b); }

//
// List of selections
//
class CSelectionList
{
public:
	// Constructor(s)
	CSelectionList(void):m_List(NULL) {}
	CSelectionList(const CSelectionList &other);

	// Destructor
	virtual ~CSelectionList();

	// Clear data
	void Clear();

	// Add a new selection (makes deep copy of value)
	void Set(const gchar *name, const CSelection &value);

	// Remove a selection from the list (the internal copy is deleted)
	void Remove(const gchar *name);
	void RemoveAt(int index);

	// Get number of records
	int Count(void) const;

	// Get record by index
	const gchar *Name(int index) const;
	CSelection At(int index) const;

	// Get first/last record
	int IndexOf(const gchar *name) const;
	int IndexOf(const CSelection &value) const;

	// Find selection by name
	CSelection Value(const gchar *name) const;

	// Assignment operator (makes deep copy)
	CSelectionList &operator=(const CSelectionList&);

private:
	struct tData {
		gchar *name;
		CSelection *data;
		tData *next;
	};

	tData *m_List;

	tData *findData(const gchar *name) const;
	tData *findInsertPos(const gchar *name) const;
};


//
// List of object tags
//
class CTags
{
public:
	struct tData
	{
		int id;
		gchar *tag;
	};

public:
	// Constructor(s)
	CTags(void):m_Capacity(0), m_Count(0), m_List(NULL) {}
	CTags(const CTags &orig);
	
	// Destructor
	virtual ~CTags();

	// Clear data
	void clear();

	// Assignment operator (makes deep copy)
	CTags &operator=(const CTags&);

	// Set a tag
	void insert(int id, const gchar *tag);

	// Get caption by id
	const gchar *caption(int id) const;

	// Returns true if an object has got a tag
	bool contains(int id) const { return indexOf(id) >= 0; }

	// Remove a tag
	void remove(int id);

	// Remove an item by index
	void removeAt(int index);

	// Number of tags
	int count(void) const { return m_Count; }

	// Get item by index
	tData &operator[](int index);
	const tData &operator[](int index) const;

	// Get index by identifier
	int indexOf(int id) const;
	
private:
	int m_Count, m_Capacity;
	tData *m_List;
};


//
// File information
//
class CFileInfo
{
public:
	// Constructors
	CFileInfo();
	CFileInfo(const gchar *filepath);
	CFileInfo(const CFileInfo &orig);

	// Destructor
	virtual ~CFileInfo();

	// Assignment operator
	CFileInfo &operator=(const CFileInfo &orig);

	// Clear data
	void Clear(void);

	// Is the data valid?
	bool Valid() const
	{ return m_FullPath!=NULL; }

	// Comparison operators
	int Compare(const CFileInfo &other) const;

	// File path
	const gchar *FullPath() const
	{ return m_FullPath; }

	// Load from project file
	void Load(GKeyFile *cfg, const gchar *section, const gchar *prefix);

	// Save to project file
	void Save(GKeyFile *cfg, const gchar *section, const gchar *prefix) const;

private:
	gchar		*m_FullPath;
	time_t		m_MTime;
};


//
// Frame information
//
class CFrameInfo
{
public:
	// Constructors
	CFrameInfo();
	CFrameInfo(const CFrameInfo &orig);

	// Destructor
	virtual ~CFrameInfo();

	// Assignment operator
	CFrameInfo &operator=(const CFrameInfo &orig);

	// Clear data
	void Clear(void);

	// Initialize data
	bool Init(const gchar *filepath, GError **error = 0);

	// Is the data valid?
	bool Valid() const
	{ return m_FullPath!=NULL; }

	// File path
	const gchar *FullPath() const
	{ return m_FullPath; }

	// Julian date
	double JulDat() const
	{ return m_JulDat; }

	// Exposure time
	double ExpTime() const
	{ return m_ExpTime; }

	// CCD temperature
	double CCDTemp() const
	{ return m_CCDTemp; }

	// Color filter name
	const gchar *Filter() const
	{ return m_Filter; }
	
	// Object designation
	const gchar *Object() const
	{ return m_Object; }

	// Number of subframes averaged
	int AvgFrames() const
	{ return m_AvgFrames; }

	// Number of subframes summed
	int SumFrames() const
	{ return m_SumFrames; }

	// File format
	CmpackFormat Format() const
	{ return m_Format; }

private:
	gchar	*m_FullPath;
	gchar	*m_Filter, *m_Object;
	double	m_JulDat, m_ExpTime, m_CCDTemp;
	int		m_AvgFrames, m_SumFrames;
	CmpackFormat m_Format;
};


//
// Object coordinates
//
class CObjectCoords
{
public:
	// Constructor(s)
	CObjectCoords(void):m_Name(NULL), m_RA(NULL), m_Dec(NULL), m_Source(NULL), m_Remarks(NULL) {}
	CObjectCoords(const CmpackObjCoords *data);
	CObjectCoords(const CObjectCoords &orig);
	
	// Destructor
	virtual ~CObjectCoords();

	// assignment operator
	CObjectCoords &operator=(const CObjectCoords &orig);

	// Set object designation
	void SetName(const char *name);

	// Set object coordinates
	void SetRA(const char *ra);
	void SetDec(const char *dec);

	// Set source
	void SetSource(const char *source);

	// Set comment
	void SetRemarks(const char *remarks);

	// Clear data
	void Clear();

	// Object designation
	const char *Name(void) const
	{ return m_Name; }

	// Object coordinates
	const char *RA(void) const
	{ return m_RA; }
	const char *Dec(void) const
	{ return m_Dec; }

	// Source
	const char *Source(void) const
	{ return m_Source; }

	// Remarks
	const char *Remarks(void) const
	{ return m_Remarks; }

	// Check validity of coordinates
	bool Valid(void) const;

protected:
	char	*m_Name;
	char	*m_RA, *m_Dec;
	char	*m_Source, *m_Remarks;

	// Comparison operators
	friend bool operator==(const CObjectCoords&, const CObjectCoords&);
};

// Comparison operators
bool operator==(const CObjectCoords &a, const CObjectCoords &b);
inline bool operator!=(const CObjectCoords &a, const CObjectCoords &b) { return !operator==(a,b); }


//
// Observer coordinates
//
class CLocation
{
public:
	// Constructor(s)
	CLocation():m_Name(NULL), m_Lon(NULL), m_Lat(NULL), m_Com(NULL) {}
	CLocation(const CmpackLocation *location);
	CLocation(const CLocation &orig);
	
	// Destructor
	virtual ~CLocation();

	// assignment operator
	CLocation &operator=(const CLocation &orig);

	// Set location name
	void SetName(const char *name);

	// Set observer's coordinates
	void SetLon(const char *lon);
	void SetLat(const char *lat);

	// Set comment
	void SetComment(const char *remarks);

	// Clear data
	void Clear();

	// Location name
	const char *Name(void) const
	{ return m_Name; }

	// Observer's coordinates
	const char *Lon(void) const
	{ return m_Lon; }
	const char *Lat(void) const
	{ return m_Lat; }

	// Remarks
	const char *Comment(void) const
	{ return m_Com; }

	// Check validity of coordinates
	bool Valid(void) const;

protected:
	char	*m_Name;
	char	*m_Lon, *m_Lat;
	char	*m_Com;

	// Comparison operators
	friend bool operator==(const CLocation&, const CLocation&);
};

// Comparison operators
bool operator==(const CLocation &a, const CLocation &b);
inline bool operator!=(const CLocation &a, const CLocation &b) { return !operator==(a,b); }


//
// Aperture parameters
//
class CAperture 
{
public:
	// Constructors
	CAperture():m_Id(0), m_Radius(0) {}
	CAperture(int id, double radius):m_Id(id), m_Radius(radius) {}

	// Destructor
	virtual ~CAperture(void) {}

	// Get identifier
	int Id(void) const
	{ return m_Id; }

	// Get radius in pixels
	double Radius(void) const
	{ return m_Radius; }

	// Is the aperture valid?
	bool Valid(void) const
	{ return m_Radius>0; }

private:
	int		m_Id;				// Aperture identifier
	double	m_Radius;			// Radius in pixels
};

//
// Table of apertures
//
class CApertures
{
public:
	// Constructor(s)
	CApertures():m_Count(0), m_Capacity(0), m_List(NULL) {}
	CApertures(const CApertures &orig);
	
	// Destructor
	virtual ~CApertures();

	// assignment operator
	CApertures &operator=(const CApertures &orig);

	// Get number of apertures
	int Count(void) const
	{ return m_Count; }

	// Returns true if the table is empty
	bool Empty(void) const
	{ return m_Count==0; }

	// Clear the table
	void Clear(void);

	// Add an aperture, if the aperture with the same id is 
	// in the table, it changes its parameters
	void Add(const CAperture &item);

	// Find aperture by id 
	int Find(int id) const;

	// Return index of smallest aperture
	int FindSmallest(void) const;

	// Get aperture by index
	const CAperture *Get(int index) const;

	// Get aperture id by index
	int GetId(int index) const;

	// Get aperture radius by index
	double GetRadius(int index) const;

protected:
	int			m_Count, m_Capacity;
	CAperture	**m_List;
};


//
// World coordinate system (WCS) data
//
class CWcs
{
public:
	enum tAxis { LNG, LAT, N_AXES };

	// Constructor
	CWcs(CmpackWcs *wcs = NULL);

	// Copy constructor (deep copy)
	CWcs(const CWcs&);

	// Destructor
	virtual ~CWcs();

	// Assignment operator (deep copy)
	CWcs &operator=(const CWcs &);

	// Valid WCS data
	bool Valid(void) const { return m_Handle!=NULL; }

	// Print data
	void Print(gchar **buf, int *len) const;

	// Get data set name (optional, NULL is valid value)
	const gchar *Name(void);

	// Get name of the axis
	const gchar *AxisName(tAxis axis);

	// 2D pixel-to-world transformation
	bool pixelToWorld(double x, double y, double &lng, double &lat);

	// 2D world-to-pixel transformation
	bool worldToPixel(double lng, double lat, double &x, double &y);

	// Print 2D coordinates in the following form: RA Dec = 00 00 00 +00 00 00
	void print(double lng, double lat, gchar *buf, int buflen, bool includeUnit = true);

	// Print 2D coordinate system, example: RA Dec
	void printUnits(gchar *buf, int buflen);

	// Print longitude / latitude only in following form: RA = lng or DEC = lat
	void printLongitude(double lng, gchar *buf, int buflen, bool includeUnit);
	void printLatitude(double lat, gchar *buf, int buflen, bool includeUnit);

protected:
	enum tCacheFlags {
		CF_NAME		= (1<<0),
		CF_AXIS     = (1<<1)
	};

	CmpackWcs			*m_Handle;
	unsigned			m_CacheFlags;
	gchar				*m_Name;
	CmpackWcsAxisParams m_Axis[N_AXES];

	// Refresh cached data
	void InvalidateCache(void);

	// Update axis parameters
	void UpdateAxes(void);
};

// Result of fitting
class CTrackingData
{
private:
	bool	m_valid;				/* True if the interpolation has been made */
	double	m_coeff[6];				/* Fit coefficients */
	double	m_jd0, m_x0, m_y0;		/* Zero point JD, X and Y */
	double	m_t0, m_t1;				/* End points (relative to JD0) */

public:
	CTrackingData();
	CTrackingData(int n, const double *jd, const double *x, const double *y);
	CTrackingData(double jd0, double x0, double y0, double t0, double t1, const double *coeff);

	bool valid(void) const { return m_valid; }

	void clear(void);

	// Load data from a file
	void load(GKeyFile *file);

	// Save data to a file
	void save(GKeyFile *file) const;

	// Get tracked object position for given JD
	void track(double jd, double &x, double &y);

	// Transform the coordinate system
	CTrackingData transformed(const CmpackMatrix &matrix) const;

	// Set time range (relative to JD0)
	void setRange(double t0, double t1) { m_t0 = t0; m_t1 = t1; }

	// Get time reference point (JD0)
	double jd0(void) const { return m_jd0; }

	// Get range of the trace
	double t0(void) const { return m_t0; }
	double t1(void) const { return m_t1; }

	// Origin
	double x0(void) const { return m_x0; }
	double y0(void) const { return m_y0; }

	// Coefficients
	const double *constData(void) const { return m_coeff; }
	double *data(void) { return m_coeff; }
};	


//
// List of objects
//
class CObjectList
{
public:
	struct tObject
	{
		int id;			/* Object identifier */
		double x, y;		/* Object coordinates */
	};

public:
	// Constructor
	CObjectList() : m_List(NULL), m_Count(0), m_Capacity(0) {}

	// Copy constructor
	CObjectList(const CObjectList &);

	// Destructor
	virtual ~CObjectList();

	// Assignment operator
	CObjectList &operator=(const CObjectList &);

	// Number of objects
	int count() const { return m_Count; }

	// Return true if the list is empty
	bool isEmpty() const { return m_Count == 0; }

	// Get object by index
	tObject &operator[](int index);
	const tObject &operator[](int index) const;

	// Index by object identifier
	int indexOf(int id) const;

	// True if an object has got an tag
	bool contains(int id) const { return indexOf(id) >= 0; }

	// Remove all items from the list
	void clear();
	
	// Insert a new object.
	void insert(double x, double y, int id);

	// Remove an object from the list
	void removeAt(int index);

private:
	int			m_Count, m_Capacity;
	tObject		*m_List;
};

#endif
