#include "Theme.h"

#include "Settings.h"

#include <CtrlLib/CtrlLib.h>
#include <TabBar/TabBar.h>

#include "FishIDEEditor.h"

using namespace Upp;

struct ColorInfo
{
	const char *name;
	void (*setFunc)(Color c);
	Color (*getFunc)(void);
};

/*
EditField::Style& es = EditField::StyleDefault().Write();
*/


#define WS(T, VAR) [](Color c) { T::StyleDefault().Write().VAR = c; }
#define RS(T, VAR) [](void) -> Color { Value v = T::StyleDefault().VAR; try { return T::StyleDefault().VAR; } catch(...) { return (Color)Null; } }

#define WSS(T, VAR, STYLE) [](Color c) { T::STYLE().Write().VAR = c; }
#define RSS(T, VAR, STYLE) [](void) -> Color { return T::STYLE().VAR; }

#define CHFIELD(ctrl, name) { #ctrl"."#name, WS(ctrl, name), RS(ctrl, name) }
#define CHFIELDS(ctrl, name, style) { #ctrl"."#name"."#style, WSS(ctrl, name, style), RSS(ctrl, name, style) }

// bottom panes text colors
static Color	_messagesTextColor					= Black();
static Color	_errorMessagesTextColor				= Red();
static Color	_messagesBackgroundColor			= LtGray();
static Color	_serialPortInputTextColor			= Black();
static Color	_serialPortInputBackgroundColor		= LtGray();
static Color	_serialPortOutputTextColor  		= Black();
static Color	_serialPortOutputBackgroundColor  	= LtGray();

static Font		_defaultStdFont;

static const ColorInfo colors[] =
{
	// NAME									DEFAULT COLOR						SETTER						GETTER
/*
	{"Black"				, SBlack_Write,					SBlack				},
	{"Gray"					, SGray_Write,					SGray				},
	{"LtGray"				, SLtGray_Write,				SLtGray				},
	{"WhiteGray"			, SWhiteGray_Write,				SWhiteGray			},
	{"White"				, SWhite_Write,					SWhite				},
	{"Red"					, SRed_Write,					SRed				},
	{"Green"				, SGreen_Write,					SGreen				},
	{"Brown"				, SBrown_Write,					SBrown				},
	{"Blue"					, SBlue_Write,					SBlue				},
	{"Magenta"				, SMagenta_Write,				SMagenta			},
	{"Cyan"					, SCyan_Write,					SCyan				},
	{"Yellow"				, SYellow_Write,				SYellow				},
	{"LtRed"				, SLtRed_Write,					SLtRed				},
	{"LtGreen"				, SLtGreen_Write,				SLtGreen			},
	{"LtYellow"				, SLtYellow_Write,				SLtYellow			},
	{"LtBlue"				, SLtBlue_Write,				SLtBlue				},
	{"LtMagenta"			, SLtMagenta_Write,				SLtMagenta			},
	{"LtCyan"				, SLtCyan_Write,				SLtCyan				},
*/

	{ "MessagesTextColor"				, [](Color c) { _messagesTextColor = c;					}, [](void) { return _messagesTextColor;				}},
	{ "ErrorMessagesTextColor"			, [](Color c) { _errorMessagesTextColor = c;			}, [](void) { return _errorMessagesTextColor;			}},
	{ "MessagesBackgroundColor"			, [](Color c) { _messagesBackgroundColor = c;			}, [](void) { return _messagesBackgroundColor;			}},
	{ "SerialPortInputTextColor"		, [](Color c) { _serialPortInputTextColor = c;			}, [](void) { return _serialPortInputTextColor;			}},
	{ "SerialPortInputBackgroundColor"	, [](Color c) { _serialPortInputBackgroundColor = c;	}, [](void) { return _serialPortInputBackgroundColor;	}},
	{ "SerialPortOutputTextColor"		, [](Color c) { _serialPortOutputTextColor = c;			}, [](void) { return _serialPortOutputTextColor;		}},
	{ "SerialPortOutputBackgroundColor"	, [](Color c) { _serialPortOutputBackgroundColor = c;	}, [](void) { return _serialPortOutputBackgroundColor;	}},

	{"ColorPaper"			, SColorPaper_Write,			SColorPaper			},
	{"ColorFace"			, SColorFace_Write,				SColorFace			},
	{"ColorText"			, SColorText_Write,				SColorText			},
	{"ColorHighlight"		, SColorHighlight_Write,		SColorHighlight		},
	{"ColorHighlightText"	, SColorHighlightText_Write,	SColorHighlightText	},
	{"ColorMenu"			, SColorMenu_Write,				SColorMenu			},
	{"ColorMenuText"		, SColorMenuText_Write,			SColorMenuText		},
	{"ColorInfo"			, SColorInfo_Write,				SColorInfo			},
	{"ColorInfoText"		, SColorInfoText_Write,			SColorInfoText		},
	{"ColorDisabled"		, SColorDisabled_Write,			SColorDisabled		},
	{"ColorLight"			, SColorLight_Write,			SColorLight			},
	{"ColorShadow"			, SColorShadow_Write,			SColorShadow		},
	{"ColorMark"			, SColorMark_Write,				SColorMark			},
	{"ColorMenuMark"		, SColorMenuMark_Write,			SColorMenuMark		},
	{"ColorLtFace"			, SColorLtFace_Write,			SColorLtFace		},
	{"ColorDkShadow"		, SColorDkShadow_Write,			SColorDkShadow		},
	{"ColorLabel"			, SColorLabel_Write,			SColorLabel			},

	// top window
	CHFIELD(TopWindow, background),

	// separator
	CHFIELD(SeparatorCtrl, l1),
	CHFIELD(SeparatorCtrl, l2),

	// buttons
	CHFIELDS(Button, monocolor[0], StyleNormal),
	CHFIELDS(Button, monocolor[1], StyleNormal),
	CHFIELDS(Button, monocolor[2], StyleNormal),
	CHFIELDS(Button, monocolor[3], StyleNormal),
	CHFIELDS(Button, textcolor[0], StyleNormal),
	CHFIELDS(Button, textcolor[1], StyleNormal),
	CHFIELDS(Button, textcolor[2], StyleNormal),
	CHFIELDS(Button, textcolor[3], StyleNormal),
	
	CHFIELDS(Button, monocolor[0], StyleOk),
	CHFIELDS(Button, monocolor[1], StyleOk),
	CHFIELDS(Button, monocolor[2], StyleOk),
	CHFIELDS(Button, monocolor[3], StyleOk),
	CHFIELDS(Button, textcolor[0], StyleOk),
	CHFIELDS(Button, textcolor[1], StyleOk),
	CHFIELDS(Button, textcolor[2], StyleOk),
	CHFIELDS(Button, textcolor[3], StyleOk),
	
	CHFIELDS(Button, monocolor[0], StyleEdge),
	CHFIELDS(Button, monocolor[1], StyleEdge),
	CHFIELDS(Button, monocolor[2], StyleEdge),
	CHFIELDS(Button, monocolor[3], StyleEdge),
	CHFIELDS(Button, textcolor[0], StyleEdge),
	CHFIELDS(Button, textcolor[1], StyleEdge),
	CHFIELDS(Button, textcolor[2], StyleEdge),
	CHFIELDS(Button, textcolor[3], StyleEdge),
	
	CHFIELDS(Button, monocolor[0], StyleLeftEdge),
	CHFIELDS(Button, monocolor[1], StyleLeftEdge),
	CHFIELDS(Button, monocolor[2], StyleLeftEdge),
	CHFIELDS(Button, monocolor[3], StyleLeftEdge),
	CHFIELDS(Button, textcolor[0], StyleLeftEdge),
	CHFIELDS(Button, textcolor[1], StyleLeftEdge),
	CHFIELDS(Button, textcolor[2], StyleLeftEdge),
	CHFIELDS(Button, textcolor[3], StyleLeftEdge),
	
	CHFIELDS(Button, monocolor[0], StyleScroll),
	CHFIELDS(Button, monocolor[1], StyleScroll),
	CHFIELDS(Button, monocolor[2], StyleScroll),
	CHFIELDS(Button, monocolor[3], StyleScroll),
	CHFIELDS(Button, textcolor[0], StyleScroll),
	CHFIELDS(Button, textcolor[1], StyleScroll),
	CHFIELDS(Button, textcolor[2], StyleScroll),
	CHFIELDS(Button, textcolor[3], StyleScroll),
	
	// SpinButtons
	CHFIELD(SpinButtons, inc.monocolor[0]),
	CHFIELD(SpinButtons, inc.monocolor[1]),
	CHFIELD(SpinButtons, inc.monocolor[2]),
	CHFIELD(SpinButtons, inc.monocolor[3]),
	CHFIELD(SpinButtons, inc.textcolor[0]),
	CHFIELD(SpinButtons, inc.textcolor[1]),
	CHFIELD(SpinButtons, inc.textcolor[2]),
	CHFIELD(SpinButtons, inc.textcolor[3]),
	
	CHFIELD(SpinButtons, dec.monocolor[0]),
	CHFIELD(SpinButtons, dec.monocolor[1]),
	CHFIELD(SpinButtons, dec.monocolor[2]),
	CHFIELD(SpinButtons, dec.monocolor[3]),
	CHFIELD(SpinButtons, dec.textcolor[0]),
	CHFIELD(SpinButtons, dec.textcolor[1]),
	CHFIELD(SpinButtons, dec.textcolor[2]),
	CHFIELD(SpinButtons, dec.textcolor[3]),

	// buttonoption
	CHFIELD(ButtonOption, textcolor[0]),
	CHFIELD(ButtonOption, textcolor[1]),
	CHFIELD(ButtonOption, textcolor[2]),
	CHFIELD(ButtonOption, textcolor[3]),
	
	CHFIELDS(ButtonOption, textcolor[0], StyleFlat),
	CHFIELDS(ButtonOption, textcolor[1], StyleFlat),
	CHFIELDS(ButtonOption, textcolor[2], StyleFlat),
	CHFIELDS(ButtonOption, textcolor[3], StyleFlat),
	
	// splitter
	CHFIELD(Splitter, vert[0]),
	CHFIELD(Splitter, vert[1]),
	CHFIELD(Splitter, horz[0]),
	CHFIELD(Splitter, horz[1]),
	
	// toolbutton
	CHFIELD(ToolButton, textcolor[0]),
	CHFIELD(ToolButton, textcolor[1]),
	CHFIELD(ToolButton, textcolor[2]),
	CHFIELD(ToolButton, textcolor[3]),
	CHFIELD(ToolButton, textcolor[4]),
	CHFIELD(ToolButton, textcolor[5]),
	
	CHFIELDS(ToolButton, textcolor[0], StyleSolid),
	CHFIELDS(ToolButton, textcolor[1], StyleSolid),
	CHFIELDS(ToolButton, textcolor[2], StyleSolid),
	CHFIELDS(ToolButton, textcolor[3], StyleSolid),
	CHFIELDS(ToolButton, textcolor[4], StyleSolid),
	CHFIELDS(ToolButton, textcolor[5], StyleSolid),
	
	// toolbar
	CHFIELD(ToolBar, buttonstyle.textcolor[0]),
	CHFIELD(ToolBar, buttonstyle.textcolor[1]),
	CHFIELD(ToolBar, buttonstyle.textcolor[2]),
	CHFIELD(ToolBar, buttonstyle.textcolor[3]),
	CHFIELD(ToolBar, buttonstyle.textcolor[4]),
	CHFIELD(ToolBar, buttonstyle.textcolor[5]),
	CHFIELD(ToolBar, breaksep.l1),
	CHFIELD(ToolBar, breaksep.l2),
	CHFIELD(ToolBar, separator.l1),
	CHFIELD(ToolBar, separator.l2),

	// clock
	CHFIELD(Clock, header),
	CHFIELD(Clock, bgmain),
	CHFIELD(Clock, fgmain),
	CHFIELD(Clock, arrowhl),
	CHFIELD(Clock, arrowhour),
	CHFIELD(Clock, arrowminute),
	CHFIELD(Clock, arrowsecond),

	// calendar
	CHFIELD(Calendar, header),
	CHFIELD(Calendar, bgmain),
	CHFIELD(Calendar, bgtoday),
	CHFIELD(Calendar, bgselect),
	CHFIELD(Calendar, fgmain),
	CHFIELD(Calendar, fgtoday),
	CHFIELD(Calendar, fgselect),
	CHFIELD(Calendar, outofmonth),
	CHFIELD(Calendar, curdate),
	CHFIELD(Calendar, today),
	CHFIELD(Calendar, selecttoday),
	CHFIELD(Calendar, cursorday),
	CHFIELD(Calendar, selectday),
	CHFIELD(Calendar, line),
	CHFIELD(Calendar, dayname),
	CHFIELD(Calendar, week),
	
	// MultiButton
	CHFIELD(MultiButton, monocolor[0]),
	CHFIELD(MultiButton, monocolor[1]),
	CHFIELD(MultiButton, monocolor[2]),
	CHFIELD(MultiButton, monocolor[3]),
	CHFIELD(MultiButton, fmonocolor[0]),
	CHFIELD(MultiButton, fmonocolor[1]),
	CHFIELD(MultiButton, fmonocolor[2]),
	CHFIELD(MultiButton, fmonocolor[3]),
	CHFIELD(MultiButton, sep1),
	CHFIELD(MultiButton, sep2),
	CHFIELD(MultiButton, error),

	// menubar
	CHFIELD(MenuBar, topitem[0]),
	CHFIELD(MenuBar, topitem[1]),
//	CHFIELD(MenuBar, topitem[2]),
	CHFIELD(MenuBar, topitemtext[0]),
	CHFIELD(MenuBar, topitemtext[1]),
	CHFIELD(MenuBar, topitemtext[2]),
	CHFIELD(MenuBar, topbar),
	CHFIELD(MenuBar, arealook),
//	CHFIELD(MenuBar, look),
	CHFIELD(MenuBar, breaksep.l1),
	CHFIELD(MenuBar, breaksep.l2),
	CHFIELD(MenuBar, separator.l1),
	CHFIELD(MenuBar, separator.l2),
//	CHFIELD(MenuBar, popupbody),
	CHFIELD(MenuBar, popupiconbar),

	// statusbar
	CHFIELD(StatusBar, look),

	// TabCtrl
	CHFIELD(TabCtrl, text_color[0]),
	CHFIELD(TabCtrl, text_color[1]),
	CHFIELD(TabCtrl, text_color[2]),
	CHFIELD(TabCtrl, text_color[3]),

	// EditField theming
	CHFIELD(EditField, paper),
	CHFIELD(EditField, disabled),
	CHFIELD(EditField, focus),
	CHFIELD(EditField, invalid),
	CHFIELD(EditField, text),
	CHFIELD(EditField, textdisabled),
	CHFIELD(EditField, selected),
	CHFIELD(EditField, selectedtext),
	CHFIELD(EditField, selected0),
	CHFIELD(EditField, selectedtext0),

	// TabBar
	CHFIELD(TabBar, text_color[0]),
	CHFIELD(TabBar, text_color[1]),
	CHFIELD(TabBar, text_color[2]),
	CHFIELD(TabBar, text_color[3]),

	// TabBarCtrl
	CHFIELD(TabBarCtrl, background),
};

static const int numColors = sizeof(colors) / sizeof(ColorInfo);

void Theme::HL::Xmlize(XmlIO xml)
{
	xml
		("Color"		, _color		)
		("Bold"			, _bold			)
		("Italic"		, _italic		)
		("Underline"	, _underline	)
	;
}

void Theme::FontInfo::Xmlize(XmlIO xml)
{
	xml
		("Name"				, _name				)
		("Height"			, _height			)
		("Bold"				, _bold				)
		("Italic"			, _italic			)
		("NonAntiAliased"	, _nonAntiAliased	)
	;
		
}

// constructor
Theme::Theme()
{
	// set defaults values
	Defaults();
}

// copy constructor
Theme::Theme(Theme const &t)
{
	operator=(t);
}

// pick constructor
Theme::Theme(Theme &&t)
{
	operator=(t);
}

// destructor
Theme::~Theme()
{
}

// assignment
Theme &Theme::operator=(Theme const &t)
{
	_colors					<<= t._colors;
	_hl						<<= t._hl;
	_scopeHL				= t._scopeHL;
	_bracesHL				= t._bracesHL;
	_conditionalsInfo		= t._conditionalsInfo;
	_conditionalTracing		= t._conditionalTracing;
	_thousandsSeparator		= t._thousandsSeparator;
	_currentLine			= t._currentLine;

	_ideFont				= t._ideFont;
	_editorFont				= t._editorFont;
	_consoleFont			= t._consoleFont;
	_serialPortFont			= t._serialPortFont;

	WhenApply				= t.WhenApply;
	return *this;
}

Theme &Theme::operator=(Theme &&t)
{
	_colors					= pick(t._colors);
	_hl						= pick(t._hl);
	_scopeHL				= t._scopeHL;
	_bracesHL				= t._bracesHL;
	_conditionalsInfo		= t._conditionalsInfo;
	_conditionalTracing		= t._conditionalTracing;
	_thousandsSeparator		= t._thousandsSeparator;
	_currentLine			= t._currentLine;


	_ideFont				= t._ideFont;
	_editorFont				= t._editorFont;
	_consoleFont			= t._consoleFont;
	_serialPortFont			= t._serialPortFont;

	WhenApply				= t.WhenApply;
	return *this;
}

void Theme::AddHlStyle(int idx, Color col, bool bold, bool italic, bool underline)
{
	_hl.Add(CodeEditor::GetHlName(idx), HL(col, bold, italic, underline));
}

// set default values
void Theme::Defaults()
{
	// on creation we use current theme values as default
	// (so on app start we use system default ones)
	_colors.Clear();
	for(int i = 0; i < numColors; i++)
	{
		ColorInfo const &info = colors[i];
		_colors.Add(info.name, info.getFunc());
	}

	_hl.Clear();
	AddHlStyle(HighlightSetup::INK_COMMENT, Green, false, true);
	AddHlStyle(HighlightSetup::PAPER_COMMENT_WORD, Yellow, false, false);
	AddHlStyle(HighlightSetup::INK_COMMENT_WORD, Blue, true, true);
	AddHlStyle(HighlightSetup::INK_CONST_STRING, Red);

	AddHlStyle(HighlightSetup::INK_CONST_STRINGOP, LtBlue);
	AddHlStyle(HighlightSetup::INK_CONST_INT, Red);
	AddHlStyle(HighlightSetup::INK_CONST_FLOAT, Magenta);
	AddHlStyle(HighlightSetup::INK_CONST_HEX, Blue);
	AddHlStyle(HighlightSetup::INK_CONST_OCT, Blue);

	AddHlStyle(HighlightSetup::INK_OPERATOR, LtBlue);
	AddHlStyle(HighlightSetup::INK_KEYWORD, LtBlue, true);
	AddHlStyle(HighlightSetup::INK_UPP, Cyan);
	AddHlStyle(HighlightSetup::PAPER_LNG, Color(255, 255, 224));
	AddHlStyle(HighlightSetup::INK_ERROR, LtRed);
	AddHlStyle(HighlightSetup::INK_PAR0, Black);
	AddHlStyle(HighlightSetup::INK_PAR1, Green);
	AddHlStyle(HighlightSetup::INK_PAR2, Magenta);
	AddHlStyle(HighlightSetup::INK_PAR3, Brown);

	AddHlStyle(HighlightSetup::INK_UPPER, Black);
	AddHlStyle(HighlightSetup::INK_SQLBASE, Black);
	AddHlStyle(HighlightSetup::INK_SQLFUNC, Black);
	AddHlStyle(HighlightSetup::INK_SQLBOOL, Black);
	AddHlStyle(HighlightSetup::INK_UPPMACROS, Cyan);
	AddHlStyle(HighlightSetup::INK_UPPLOGS, Green);
	
	AddHlStyle(HighlightSetup::INK_DIFF_FILE_INFO, Black, true);
	AddHlStyle(HighlightSetup::INK_DIFF_HEADER, Color(28, 127, 200));
	AddHlStyle(HighlightSetup::INK_DIFF_ADDED, Color(28, 42, 255));
	AddHlStyle(HighlightSetup::INK_DIFF_REMOVED, Color(255, 0, 0));
	AddHlStyle(HighlightSetup::INK_DIFF_COMMENT, Green);

	AddHlStyle(HighlightSetup::PAPER_BLOCK1, Blend(LtBlue, White, 240));
	AddHlStyle(HighlightSetup::PAPER_BLOCK2, Blend(LtGreen, White, 240));
	AddHlStyle(HighlightSetup::PAPER_BLOCK3, Blend(LtYellow, White, 240));
	AddHlStyle(HighlightSetup::PAPER_BLOCK4, Blend(LtMagenta, White, 240));

	AddHlStyle(HighlightSetup::INK_MACRO, Magenta);
	AddHlStyle(HighlightSetup::PAPER_MACRO, Color(255, 255, 230));
	AddHlStyle(HighlightSetup::PAPER_IFDEF, Color(230, 255, 255));
	AddHlStyle(HighlightSetup::INK_IFDEF, Color(170, 170, 170));

	AddHlStyle(HighlightSetup::PAPER_BRACKET0, LtYellow);
	AddHlStyle(HighlightSetup::PAPER_BRACKET, Yellow, true);

	AddHlStyle(HighlightSetup::INK_NORMAL, SColorText);
	AddHlStyle(HighlightSetup::INK_DISABLED, SColorDisabled);
	AddHlStyle(HighlightSetup::INK_SELECTED, SColorHighlightText);
	AddHlStyle(HighlightSetup::PAPER_NORMAL, SColorPaper);
	AddHlStyle(HighlightSetup::PAPER_READONLY, SColorFace);
	AddHlStyle(HighlightSetup::PAPER_SELECTED, SColorHighlight);
	
	AddHlStyle(HighlightSetup::PAPER_SELWORD, Yellow);

	AddHlStyle(HighlightSetup::PAPER_ERROR, Blend(White(), LtRed(), 50));
	AddHlStyle(HighlightSetup::PAPER_WARNING, Blend(White(), Yellow(), 50));

	AddHlStyle(HighlightSetup::SHOW_LINE, Color(199, 247, 198));
	
	AddHlStyle(HighlightSetup::WHITESPACE, Blend(SColorLight, SColorHighlight));
	AddHlStyle(HighlightSetup::WARN_WHITESPACE, Blend(SColorLight, SRed));
	
	_scopeHL				= 1;
	_bracesHL				= 1;
	_conditionalsInfo		= 1;
	_conditionalTracing		= false;
	_thousandsSeparator		= true;
	_currentLine			= true;
	
	_ideFont._name				= StdFont().GetFaceName();
	_ideFont._height			= StdFont().GetHeight();
	_ideFont._bold				= false;
	_ideFont._italic			= false;
	_ideFont._nonAntiAliased	= false;

	_editorFont._name			= CourierZ().GetFaceName();
	_editorFont._height			= CourierZ(12).GetHeight();
	_editorFont._bold			= false;
	_editorFont._italic			= false;
	_editorFont._nonAntiAliased	= false;

	_consoleFont				= _ideFont;
	_serialPortFont				= _ideFont;

	// bottom pane text colors
	_messagesTextColor			= Black();
	_errorMessagesTextColor		= Red();
	_serialPortInputTextColor	= Black();
	_serialPortOutputTextColor	= Black();
}

// apply theme
void Theme::Apply(void) const
{
	Font::SetStdFont(GetIdeFont());
	
	for(int i = 0; i < _colors.GetCount(); i++)
		colors[i].setFunc(_colors[i]);
	
	// refresh all app controls
	Vector<Ctrl *>tops = Ctrl::GetTopCtrls();
	for(int i = 0; i < tops.GetCount(); i++)
	{
		tops[i]->RefreshLayoutDeep();
		tops[i]->RefreshFrame();
	}
	
	// propagate theme apply
	WhenApply(this);
}

static void FixFont(String &faceName)
{
	// if font is available, just do nothing
	if(Font::FindFaceNameIndex(faceName) >= 0)
		return;
#ifdef WIN32
	if(faceName == "Courier New")
#else
	if(faceName == "monospace")
#endif
	{
		faceName == CourierZ().GetFaceName();
		return;
	}
	faceName = GetStdFont().GetFaceName();
}

// xmlize
void Theme::Xmlize(XmlIO xml)
{
	if(xml.IsLoading())
	{
		VectorMap<String, Color> colors;
		VectorMap<String, HL> hl;
		
		xml
			("Colors"				, colors)
			("SyntaxHighlight"		, hl	)
		;
		Defaults();
		for(int i = 0; i < colors.GetCount(); i++)
			_colors.GetAdd(colors.GetKey(i)) = colors[i];
	
		for(int i = 0; i < hl.GetCount(); i++)
			_hl.GetAdd(hl.GetKey(i)) = hl[i];
	}
	else
	{
		xml
			("Colors"				, _colors	)
			("SyntaxHighlight"		, _hl		)
		;
	}

	xml
		("ScopeHighlight"		, _scopeHL				)
		("BracesHighlight"		, _bracesHL				)
		("ConditionalsInfo"		, _conditionalsInfo		)
		("ConditionalTracing"	, _conditionalTracing	)
		("ThousandsSeparator"	,  _thousandsSeparator	)
		("CurrentLine"			, _currentLine			)

		("IdeFont"				, _ideFont				)
		("EditorFont"			, _editorFont			)
		("ConsoleFont"			, _consoleFont			)
		("SerialPortFont"		, _serialPortFont		)

		("MessagesTextColor"			, _messagesTextColor			)
		("ErrorMessagesTextColor"		, _errorMessagesTextColor		)
		("SerialPortInputTextColor"		, _serialPortInputTextColor		)
		("SerialPortOutputTextColor"	, _serialPortOutputTextColor	)
	;
	
	if(xml.IsLoading())
	{
		// fix theme fonts, upon loading -- they may change between platforms
		FixFont(_ideFont._name);
		FixFont(_editorFont._name);
		FixFont(_consoleFont._name);
		FixFont(_serialPortFont._name);
	}
}

// get/set colors
VectorMap<String, Color> const &Theme::GetColors(void) const
{
	return _colors;
}

void Theme::SetColors(VectorMap<String, Color> const &colors)
{
	for(int i = 0; i < colors.GetCount(); i++)
	{
		int idx = _colors.Find(colors.GetKey(i));
		if(idx >= 0)
			_colors[idx] = colors[i];
	}
}

// get/set syntax highlight
VectorMap<String, Theme::HL> const &Theme::GetSyntax(void) const
{
	return _hl;
}

void Theme::SetSyntax(VectorMap<String, Theme::HL> const &hl)
{
	for(int i = 0; i < hl.GetCount(); i++)
	{
		int idx = _hl.Find(hl.GetKey(i));
		if(idx >= 0)
			_hl[idx] = hl[i];
	}
}

static Font GetFont(Theme::FontInfo const &fi)
{
	String name = fi._name;
	Font f;	
	if(name == "STDFONT")
		f.FaceName(_defaultStdFont.GetFaceName());
	else
		f.FaceName(name);
	f.Height(fi._height);
	if(fi._bold) f.Bold();
	if(fi._italic) f.Italic();
	if(fi._nonAntiAliased) f.NonAntiAliased();
	return f;
}

Font Theme::GetIdeFont(void) const
{
	return GetFont(_ideFont);
}

Font Theme::GetEditorFont(void) const
{
	return GetFont(_editorFont);
}
Font Theme::GetConsoleFont(void) const
{
	return GetFont(_consoleFont);
}
Font Theme::GetSerialPortFontFont(void) const
{
	return GetFont(_serialPortFont);
}

// bottom pane text colors
Color Theme::GetMessagesTextColor(void) const				{ return _colors.Get("MessagesTextColor");					}
Color Theme::GetErrorMessagesTextColor(void) const			{ return _colors.Get("ErrorMessagesTextColor");				}
Color Theme::GetMessagesBackgroundColor(void) const			{ return _colors.Get("MessagesBackgroundColor");			}
Color Theme::GetSerialPortInputTextColor(void) const		{ return _colors.Get("SerialPortInputTextColor");			}
Color Theme::GetSerialPortInputBackgroundColor(void) const	{ return _colors.Get("SerialPortInputBackgroundColor");		}
Color Theme::GetSerialPortOutputTextColor(void) const		{ return _colors.Get("SerialPortOutputTextColor");			}
Color Theme::GetSerialPortOutputBackgroundColor(void) const	{ return _colors.Get("SerialPortOutputBackgroundColor");	}

/////////////////////////////////////////////////////////////////////

// constructor
ThemesClass::ThemesClass()
{
	_themes.Add(t_("Default")).WhenApply = THISBACK(applyCb);
	_current = 0;
	
	// store default std font
	_defaultStdFont = Font::GetStdFont();
}

// destructor
ThemesClass::~ThemesClass()
{
}

// get all available theme names
Vector<String> ThemesClass::GetNames(void) const
{
	return Vector<String>(_themes.GetKeys(), 1);
}

// get a theme
Theme const *ThemesClass::Get(String const &name) const
{
	int idx = _themes.Find(name);
	if(idx < 0)
		return NULL;
	return &_themes[idx];
}
Theme *ThemesClass::Get(String const &name)
{
	int idx = _themes.Find(name);
	if(idx < 0)
		return NULL;
	return &_themes[idx];
}

Theme const *ThemesClass::Get(int idx) const
{
	if(idx >= 0 && idx <= _themes.GetCount())
		return &_themes[idx];
	return NULL;
}

Theme *ThemesClass::Get(int idx)
{
	if(idx >= 0 && idx <= _themes.GetCount())
		return &_themes[idx];
	return NULL;
}

// add a new theme
Theme &ThemesClass::Add(String const &name, Theme const &base)
{
	int idx = _themes.Find(name);
	if(idx >= 0)
		return _themes[idx];
	Theme &t = _themes.Add(name, new Theme(base));
	t.WhenApply = THISBACK(applyCb);
	return t;
}

// append themes from a map
void ThemesClass::Append(ArrayMap<String, Theme> const &themes)
{
	for(int i = 0; i < themes.GetCount(); i++)
		if(_themes.Find(themes.GetKey(i)) < 0)
			_themes.Add(themes.GetKey(i), themes[i]);
}


// remove a theme
bool ThemesClass::Remove(String const &name)
{
	int idx = _themes.Find(name);
	if(idx <= 0)
		return false;
	if(_current == idx)
		_current = 0;
	_themes.Remove(idx);
	return true;
}

bool ThemesClass::Remove(int idx)
{
	if(idx <= 0 || idx >= _themes.GetCount())
		return false;
	_themes.Remove(idx);
	return true;
}

// get current theme
Theme const &ThemesClass::GetCurrent(void) const
{
	return _themes[_current];
}

// set current theme
void ThemesClass::SetCurrentTheme(String const &name)
{
	SetCurrentIndex(_themes.Find(name));
}

void ThemesClass::SetCurrentIndex(int idx)
{
	if(idx < 0 || idx >= _themes.GetCount())
		return;
	_current = idx;
}

// apply current theme
void ThemesClass::ApplyCurrent(void) const
{
	_themes[_current].Apply();
}
		
// rename theme
bool ThemesClass::Rename(String const &oldName, String const &newName)
{
	int oldIdx = _themes.Find(oldName);
	if(oldIdx < 0)
		return false;
	int newIdx = _themes.Find(newName);
	if(newIdx >= 0)
		return false;
	_themes.SetKey(oldIdx, newName);
	return true;
}
		
// xml support
void ThemesClass::Xmlize(XmlIO xml)
{
	xml
		("Themes"			, _themes)
		("CurrentTheme"		, _current)
	;
	if(xml.IsLoading())
	{
		// on loading, replace first theme with default one
		_themes.SetKey(0, t_("Default"));
		_themes.Set(0, new Theme);
		
		// connect apply callback to all themes
		for(int i = 0; i < _themes.GetCount(); i++)
			_themes[i].WhenApply = THISBACK(applyCb);
	}
}

ThemesClass &__GetThemes(void)
{
	static ThemesClass themes;
	return themes;
}

