/*
 * Copyright 2011, Mason Giles. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright notice and this
 *     list of conditions.
 *  2. Redistributions in binary form must reproduce the above copyright notice and this
 *     list of conditions in the documentation and/or other materials provided with the
 *     distribution.
 */

/*
###########
## USAGE ##
###########

All methods and members prefixed with underscores may be implementation-specific, and
 should not be used.

class myWindow : public MGWindow {
private:
	MGListBox* lb;
	void onResize();
public:
	myWindow();
	~myWindow();
};

myWindow::myWindow() : MGWindow( (void*)(IDI_APPICON), _T("myApp") ) {
	lb = new MGListBox(this, false); // etc..
	
	onResize.assign(this, MGCB(myWindow::onResize));
	onResize.invoke();

	setVisible(true);
}

int main() {
	myWindow* app = new myWindow();
	while ( app->isActive() ) app->doEvents();
	delete app;
	return 0;
};


##########
## TODO ##
##########

- fully chained syntax for menu items
- deletion semantics (and associated delete's for MGEventHandlers and MG_Data's!)
- checkboxes, radio buttons
- distinguish between visible / invisible controls (timers, hotkeys)
	- move generic new/deletes of d into parent
	- automatically give IDs to widgets? MGObject?
- prefix Data structures' names with an underscore to scare off external users
- return this* from functions to allow chaining methods ->()->()->()
- macro-based resizing framework

*/

#pragma once
#include "stdafx.h"
#include "Util.h" // for TCHAR/TSTRING

// MG HELPERS
// ``````````
enum MGObjectTypes {
	MGCT_UNKNOWN = 0,
	MGCT_WINDOW,
	MGCT_MENUITEM,
	MGCT_EDIT,
	MGCT_BUTTON,
	MGCT_RICHEDIT,
	MGCT_LISTBOX,
	MGCT_PROGRESSBAR,
	MGCT_LABEL,
	MGCT_GROUP,
	MGCT_COMBOBOX,
	MGCT_TIMER,
	MGCT_HOTKEY,
	MGCT_HOTKEYCONTROL
};
typedef unsigned char MGObjectType;

struct MGKeyCombination {
	int  virtkey; // MGVK_*
	bool alt;
	bool ctrl;
	bool shift;
};

struct MGWindowData;
struct MGControlData;
struct MGEventData;
class  MGEventHandler;
class  MGFontManager;
class  MGMenuItem;
struct MGMenuItemData;
struct MGMenuItemRelation;
class  MGWindow;
struct MGActionData;
class  MGAction;
struct MGListBoxData;

typedef void(MGWindow::*MGCallback)(void);
#define MGCB(x) static_cast<MGCallback>(&x)

class MGObject {
public:
	MGObjectType _controlType;
};

// MGACTION
// ````````
class MGAction {
private:
	MGActionData* d;
public:
	union {
		long asLong;
		MGKeyCombination asKC;
	} data;
	MGAction();
	~MGAction();
	void assign(MGWindow* object, MGCallback function);
	void invoke();
};

// MGCONTROL
// `````````
class MGControl : public MGObject {
protected:
	MGControlData *d;

public:
	void resize(int x, int y, int width, int height);
	void resize(int width, int height);
	int  getX();
	int  getY();
	int  getWidth();
	int  getHeight();
	int  getInnerWidth();
	virtual int getInnerHeight();

	virtual void focus();

	void setVisible(bool bVisible);
	void removeStyles();
	void removeBorder();
	virtual bool _parseEvent(MGEventData *ev);
};


// MGWINDOW
// ````````
class MGWindow : public MGControl {
private:
	MGEventHandler *e;
	MGWindowData *w;

public:
	// Constructor: hIcon is platform-specific.
    //     MSW:  null, or (void*)(an embedded resource ID)
	//     FLTK: null, or a const pointer to 16x16 bitmap data (fixme?).
	MGWindow(void* hIcon, TCHAR* caption);
	~MGWindow();
	void _prepareChild(MGControlData *pCtrlData, MGControl *pCtrl);
	void _prepareChild(MGMenuItemData *pMenuData, MGMenuItem *pMenu);
	bool _parseEvent(MGEventData *ev);
	
	void doEvents();
	bool isActive();
	void close();
	void focus();
	
	void setResizable(bool resizable);
	void showStatusBar(bool show);
	void setStatusBar(const TCHAR* text);
	void setCaption(const TCHAR* caption);

	void addChildMenu(MGMenuItem* menuItem);
	void updateMenu();	
	
	void dlgAlert(TCHAR* message);
	bool dlgConfirm(TCHAR* prompt);
	bool dlgFolderSelect(TCHAR* title, TSTRING &result);

	int getInnerHeight();

	MGAction onResize;
	MGAction onClosing;
};

// MGMENUITEM
// ``````````
// Call MGWindow::updateMenus() to reflect changes
class MGMenuItem : public MGObject {
private:
	MGMenuItemData* m;
	void _initialise(MGWindow* parent, TCHAR* text);

public:
	MGMenuItem(MGWindow*   parent, TCHAR* text);
	MGMenuItem(MGMenuItem* parent, TCHAR* text);
	MGMenuItem(MGWindow*   parent, TCHAR* text, MGCallback onClick);	
	MGMenuItem(MGMenuItem* parent, TCHAR* text, MGCallback onClick);
	
	~MGMenuItem();
	void _updateSelf(MGMenuItemRelation* relation);

	// setChecked, setEnabled require the menu to be created to work; so call
	// them after MGWindow::updateMenu() has been called for the first time
	void setChecked(bool isChecked);
	void setEnabled(bool isEnabled);

	// Shortcut keys and child menus are created on MGWindow::updateMenu()
	MGMenuItem* setShortcut(bool alt, bool ctrl, bool shift, int virtkey);	
	void addChildMenu(MGMenuItem* menuItem);
	void addChildMenuSeparator();

	MGAction onClick;
};

// MGEDIT
// ``````
class MGEdit : public MGControl {
private:
	MGEventHandler *e;

public:
	MGEdit(MGWindow* parent, bool bMultiline);	
	bool _parseEvent(MGEventData *ev);

	void textSet(TCHAR* szText);
	void textAppend(TCHAR* szText);
	void textGet(TCHAR* outBuff, int charcount);
	int  textLength();
	void selRange(int start, int end);
	void selEnd();
		
	MGAction onKey;
};

// MGRICHEDIT
// ``````````
class MGRichEdit : public MGControl {
public:
	MGRichEdit(MGWindow* parent, bool bReadOnly);

	void Clear();
	void setReadOnly(bool bReadOnly);	
	int  TextLength();
	void SelTextGet(TCHAR* outbuff, int charcount);
	void SelTextSet(TCHAR* szText);
	void SelFontSet(long rgb, bool bBold, bool bIta, bool bUL);
	void SelFontSet(long rgb, bool bBold, bool bIta, bool bUL,
		const TCHAR* szFont, long size);
	void SelSetRange(long lMin, long lMax);
	void SelSetToEnd();
	int  SelGetStart();
	int  SelGetEnd();
};

// MGBUTTON
// ````````
class MGButton : public MGControl {
public:	
	MGButton(MGWindow* parent, TCHAR* szCaption);

	MGAction onClick;
};

// MGLISTBOX
// `````````
class MGListBox : public MGControl {
private:
	MGListBoxData *z;
public:
	MGListBox(MGWindow* parent, bool bAutoSort, bool bAllowColors);
	~MGListBox();
	void _parseRequest(MGEventData *ev);

	int   addItem(TCHAR* szItem);
	int   addItem(TCHAR* szItem, int pos);
	void  clear();
	int   findItem(TCHAR* szItem); // index or -1 on error
	int   getSelected();
	TSTRING getItemText(int idx);
	int   getItemCount();
	void* getItemData(int idx);
	void  setItemData(int idx, void* data);
	void  setItemColor(int idx, unsigned char r, unsigned char b, unsigned char g);
	void  setItemText(int idx, TCHAR* szItem);
	void  setSelected(int idx); // index or -1 for no selection
	void  removeItem( int idx);
	
	MGAction onChange;
	MGAction onDoubleClick;
};

// MGPROGRESSBAR
// `````````````
class MGProgressBar : public MGControl {
private:
	MGEventHandler *e;
public:
	MGProgressBar(MGWindow* parent, bool isHorizontal, bool isSmooth);
	bool _parseEvent(MGEventData *ev);

	void setValue(float fraction);
	float getValue();

	MGAction onMouseDown;
	MGAction onDrag;
};

// MGLABEL
// ```````
class MGLabel : public MGControl {
public:
	MGLabel(MGWindow* parent, bool alignLeft);

	void setText(TCHAR* text);
	void getText(TSTRING &text);
};

// MGGROUP
// ```````
class MGGroup : public MGControl {
public:
	MGGroup(MGWindow* parent);

	void setText(TCHAR* text);
};

// MGCOMBOBOX
// ``````````
class MGComboBox : public MGControl {
public:
	MGComboBox(MGWindow* parent, bool bAutoSort);
	
	void MGComboBox::allowTextEntry(bool bEnable);

	void addItem(TCHAR* text);
	void clear();
	int  getSelected(); // index or -1 for error
	void setSelected(int idx);
	TSTRING getSelectedText();

	MGAction onChange;
};

// MGTIMER
// ```````
class MGTimer : private MGControl {
public:
	MGTimer(MGWindow* parent, int interval_ms);
	~MGTimer();

	MGAction onTick;
};

// MGHOTKEY
// ````````
// Systemwide hotkeys. For application-only hotkeys, use menuitem shortcuts
class MGHotkey : private MGControl {
private:
	void _initialise(MGWindow* parent, bool alt, bool ctrl, bool shift, int virtkey);
public:
	MGHotkey(MGWindow* parent, MGKeyCombination kc);
	MGHotkey(MGWindow* parent, bool alt, bool ctrl, bool shift, int virtkey);
	MGHotkey(MGWindow* parent, bool alt, bool ctrl, bool shift, int virtkey, MGCallback onPress);
	~MGHotkey();

	MGAction onPress;
};

// MGHOTKEYCONTROL
// ```````````````
class MGHotkeyControl : public MGControl {
public:
	MGHotkeyControl(MGWindow* parent);
	~MGHotkeyControl();

	void setValue(MGKeyCombination val);	
	MGKeyCombination getValue();
};

// MGVIRTUALKEY
// ````````````
enum MGVirtualKeys {
	MGVK_TAB, MGVK_ENTER, MGVK_ESC, MGVK_SPACE,	MGVK_INS, MGVK_TILDE,	

	MGVK_0,	MGVK_1, MGVK_2, MGVK_3, MGVK_4, MGVK_5, MGVK_6, MGVK_7, MGVK_8, MGVK_9,
	MGVK_A, MGVK_B, MGVK_C, MGVK_D, MGVK_E, MGVK_F, MGVK_G, MGVK_H, MGVK_I, MGVK_J,
	MGVK_K, MGVK_L, MGVK_M, MGVK_N, MGVK_O, MGVK_P, MGVK_Q, MGVK_R, MGVK_S,	MGVK_T,
	MGVK_U, MGVK_V, MGVK_W, MGVK_X, MGVK_Y, MGVK_Z,

	MGVK_F1, MGVK_F2, MGVK_F3, MGVK_F4, MGVK_F5, MGVK_F6, MGVK_F7, MGVK_F8, MGVK_F9,
	MGVK_F10, MGVK_F11, MGVK_F12,

	MGVK_MAX_VIRTKEYS
};

