#ifndef VDR_PERL_OSDBASE_H
#define VDR_PERL_OSDBASE_H

#include "callback.h"
#include "common.h"
#include <vdr/osdbase.h>
#include <stdio.h>

// --- cPerlOsdMenuBase -------------------------------------------------------

class cPerlOsdItem;
class cPerlOsdMenu;
class cPerlMenuText;
class cPerlMenuEditStrItem;
class cPerlMenuEditStraItem;

template<class T>
class cPerlOsdMenuBase: public T, public cPerlCallbackBase {
private:
	char *mButtons[4];

protected:
	virtual eOSState ProcessKey(eKeys Key);

	void SetCols(const char *Opts);
	void Clear(const char *Opts);
	void AddSubMenu(const char *Opts);
	void HasSubMenu(const char *Opts);
	void SetHelp(const char *Opts);
	void Add(const char *Opts);
	void Display(const char *Opts);

public:
	cPerlOsdMenuBase(FILE *Pipe, const char *Name, const char *Title);
	cPerlOsdMenuBase(FILE *Pipe, const char *Name, const char *Title, 
	                 const char *Text);
	virtual ~cPerlOsdMenuBase();
	
	virtual void Method(const char *Opts);
};

template<class T>
cPerlOsdMenuBase<T>::cPerlOsdMenuBase(FILE *Pipe, const char *Name, 
                                      const char *Title):
		T(Title),
		cPerlCallbackBase(Pipe, Name)
{
	memset(mButtons, 0, sizeof(mButtons));
}

template<class T>
cPerlOsdMenuBase<T>::cPerlOsdMenuBase(FILE *Pipe, const char *Name, 
                                      const char *Title, const char *Text):
		T(Title, Text),
		cPerlCallbackBase(Pipe, Name)
{
	memset(mButtons, 0, sizeof(mButtons));
}

template<class T>
cPerlOsdMenuBase<T>::~cPerlOsdMenuBase()
{
	for (uint i = 0; i < sizeof(mButtons) / sizeof(mButtons[0]); ++i)
		free(mButtons[i]);
}

template<class T>
eOSState cPerlOsdMenuBase<T>::ProcessKey(eKeys Key)
{
	eOSState state = T::ProcessKey(Key);

	SendAction("M %s=%p ProcessKey %d %d", Name(), this, Key, state);
	ReadAction("m");

	return state;
}

template<class T>
void cPerlOsdMenuBase<T>::Method(const char *Opts)
{
	BEGIN_METHOD_MAP0(Opts,
		MAP_METHOD(SetCols)
		MAP_METHOD(Clear)
		MAP_METHOD(AddSubMenu)
		MAP_METHOD(HasSubMenu)
		MAP_METHOD(SetHelp)
		MAP_METHOD(Add)
		MAP_METHOD(Display)
	);
}

template<class T>
void cPerlOsdMenuBase<T>::SetCols(const char *Opts) 
{
	char **cols = SplitParameters(Opts, 5, false);
	T::SetCols(cols[0] ? atoi(cols[0]) : 0, cols[1] ? atoi(cols[1]) : 0,
	           cols[2] ? atoi(cols[2]) : 0, cols[3] ? atoi(cols[3]) : 0,
	           cols[4] ? atoi(cols[4]) : 0);
	FreeParameters(cols, 5);
}

template<class T>
void cPerlOsdMenuBase<T>::Clear(const char *Opts) 
{
	T::Clear();
}

template<class T>
void cPerlOsdMenuBase<T>::HasSubMenu(const char *Opts)
{
	SendAction("m %d", T::HasSubMenu());
}

template<class T>
void cPerlOsdMenuBase<T>::SetHelp(const char *Opts) 
{
	char **buttons = SplitParameters(Opts, 4, false);

	T::SetHelp(buttons[0], buttons[1], buttons[2], buttons[3]);

	for (int i = 0; i < 4; ++i)
		free(mButtons[i]);
	memcpy(mButtons, buttons, sizeof(mButtons));
	free(buttons[4]); // just to be sure
}

template<class T>
void cPerlOsdMenuBase<T>::Add(const char *Opts) 
{
	if      (strn2cmp(Opts, "OsdItem=") == 0)
		T::Add((cPerlOsdItem*)strtoul(Opts + strlenc("OsdItem="), NULL, 0));
	else if (strn2cmp(Opts, "MenuEditStrItem=") == 0)
		T::Add((cPerlMenuEditStrItem*)strtoul(Opts + 
		       strlenc("MenuEditStrItem="), NULL, 0));
	else if (strn2cmp(Opts, "MenuEditStraItem=") == 0)
		T::Add((cPerlMenuEditStraItem*)strtoul(Opts + 
		       strlenc("MenuEditStraItem="), NULL, 0));
}

template<class T>
void cPerlOsdMenuBase<T>::Display(const char *Opts) 
{
	T::Display();
}

// --- cPerlOsdMenu -----------------------------------------------------------

class cPerlOsdMenu: public cPerlOsdMenuBase<cOsdMenu> {
public:
	cPerlOsdMenu(FILE *Pipe, const char *Title);
};

// --- cPerlOsdMenuBase reverse inlines ---------------------------------------

#include "menu.h"
#include "menuitems.h"

template<class T>
void cPerlOsdMenuBase<T>::AddSubMenu(const char *Opts)
{
	char **params = SplitParameters(Opts, 1);

	if      (strn2cmp(params[0], "OsdMenu=") == 0) {
		cPerlOsdMenu *obj = (cPerlOsdMenu*)strtoul(params[0] + 
		                    strlenc("OsdMenu="), NULL, 0);
		T::AddSubMenu(obj);
	}
	else if (strn2cmp(params[0], "MenuText=") == 0) {
		cPerlMenuText *obj = (cPerlMenuText*)strtoul(params[0] +
		                     strlenc("MenuText="), NULL, 0);
		T::AddSubMenu(obj);
	}

	FreeParameters(params, 1);
}

// --- cPerlOsdItem -----------------------------------------------------------

class cPerlOsdItem: public cOsdItem, public cPerlCallbackBase {
protected:
	void SetSelectable(const char *Opts);

public:
	cPerlOsdItem(FILE *Pipe, const char *Text = NULL, 
	             eOSState State = osUnknown, const char *Name = "OsdItem");

	virtual void Method(const char *Opts);
};

#endif // VDR_PERL_OSDBASE_H
