/**
 * osd_userinterface.c: OSD userinterface
 * 
 * Can display textbased or graphical OSD, depends on user setting.
 * 
 * Copyright: See COPYING file that comes with this distribution
 *
 * Author: Matthias Feistel <hitman_47@users.sourceforge.net>, (C) 2006, 2007
 */

#include <sstream>
#include <list>

#include "settings.h"
#include "osd_userinterface.h"
#include "image_bitmap.h"

#include <iostream>
cUserInterface::cUserInterface( void )
{
	SetDescription( PLUGIN_NAME"-OsdRefresh" );
	osd = 0;
	OsdTools = 0;
	WebServiceConnector = new cWebServiceConnector();
	UserInterface_SearchActions = 0;
	
	// Osd_Tools:
	m_Osd_Width = cSettings::GetInstance().Osd_ParameterMap[ "Osd_Width" ];
	m_Osd_Height = cSettings::GetInstance().Osd_ParameterMap[ "Osd_Height" ];
	m_Osd_AreaDepth = cSettings::GetInstance().Osd_ParameterMap[ "Osd_AreaDepth" ];
	m_Osd_ColourDepth = cSettings::GetInstance().Osd_ParameterMap[ "Osd_ColourDepth" ];
	m_Osd_Centre_x = cSettings::GetInstance().Osd_ParameterMap[ "Osd_Centre_x" ];
	m_Osd_Centre_y = cSettings::GetInstance().Osd_ParameterMap[ "Osd_Centre_y" ];
	m_Osd_BitmapSize_x = cSettings::GetInstance().Osd_ParameterMap[ "Osd_BitmapSize_x" ];
	m_Osd_BitmapSize_y = cSettings::GetInstance().Osd_ParameterMap[ "Osd_BitmapSize_y" ];
	m_Osd_ImgAlpha = cSettings::GetInstance().Osd_ParameterMap[ "Osd_ImgAlpha" ];
	m_Osd_BtnWidth = cSettings::GetInstance().Osd_ParameterMap[ "Osd_BtnWidth" ];
	m_Osd_FontHeight = cSettings::GetInstance().Osd_ParameterMap[ "Osd_FontHeight" ];
}

cUserInterface::~cUserInterface( )
{
	Cancel( MAXIMUM_THREADKILLTIME );
	
	if( OsdTools )
	{
		delete OsdTools;
	}
	OsdTools = 0;
	
	if( osd )
	{
		delete osd;
	}
	osd = 0;
	
	if( WebServiceConnector )
	{
		delete WebServiceConnector;
		WebServiceConnector = 0;
	}
	
	if( UserInterface_SearchActions )
	{
		delete UserInterface_SearchActions;
		UserInterface_SearchActions = 0;
	}
	
}

void cUserInterface::Show( void )
{
	if( osd )
	{
		delete osd;
	}
	osd = 0;
	
	
	//aus vdr-weather
	osd = cOsdProvider::NewOsd ( ( (Setup.OSDWidth - m_Osd_Width) / 2)
			+ Setup.OSDLeft+ 0, //diese  0  ist der X-Offset
			((Setup.OSDHeight - m_Osd_Height) / 2) + Setup.OSDTop );
	
	if( osd )
	{
		tArea
				Area[] =
				{
				/// Area fuer Titelzeile
						{ 0, 0, m_Osd_Width - 1, (m_Osd_Centre_y
								- m_Osd_BitmapSize_y / 2) - 1, 2 },
						/// Area fuer Albumcover
						{ 0, (m_Osd_Centre_y - m_Osd_BitmapSize_y / 2),
								m_Osd_BitmapSize_x - 1, (m_Osd_Centre_y
										+ m_Osd_BitmapSize_y / 2),
								m_Osd_AreaDepth * 2 },
						/// Area fuer Metadata
						{ m_Osd_BitmapSize_x, m_Osd_Centre_y
								- m_Osd_BitmapSize_y / 2, m_Osd_Width - 1,
								m_Osd_BitmapSize_y + m_Osd_BitmapSize_y / 2, 2 },
						/// Area fuer SetHelp buttons
						{ 0, (m_Osd_Centre_y + m_Osd_BitmapSize_y / 2) + 1,
								m_Osd_Width - 1, m_Osd_Height - 1, 4 } };
		
		eOsdError result = osd->CanHandleAreas( Area, sizeof(Area) / sizeof(tArea));
		if( result == oeOk )
		{
			osd->SetAreas(Area, sizeof(Area) / sizeof(tArea));
		}
		else
		{
			const char *errormsg = 0;
			switch( result )
			{
				case oeTooManyAreas:
				errormsg = "Too many OSD areas";
				break;
				case oeTooManyColors:
				errormsg = "Too many colors";
				break;
				case oeBppNotSupported:
				errormsg = "Depth not supported";
				break;
				case oeAreasOverlap:
				errormsg = "Areas are overlapped";
				break;
				case oeWrongAlignment:
				errormsg = "Areas not correctly aligned"; /// Beachte: bei FF-Karten wird die Datei dvbosd.c anstatt osd.c benutzt; andere Berechnungsformel
				break;
				case oeOutOfMemory:
				errormsg = "OSD memory overflow";
				break;
				case oeUnknown:
				errormsg = "Unknown OSD error";
				break;
				default:
				break;
			}
			esyslog("lastfm: ERROR! OSD open failed! Can't handle areas (%d)-%s\n", result, errormsg);

		}

		// Rechteck zeichnen, das das OSD transparent macht
		osd->DrawRectangle(m_Osd_Width/4-1-1,
		0,
		3 * m_Osd_Width/4-1,
		m_Osd_Height - 1,
		clrTransparent);

		// Eigene OSD-Funktionen (DrawButton, BevelButton etc.)
		if(OsdTools)
		{
			delete OsdTools;
		}
		OsdTools = 0;
		OsdTools = new cOsdTools(osd);

		// Fenstertitel (oben links)
		OsdTools->DrawButton(0, 0, "Lastfm", clrBlack);

		// Helpbuttons zeichnen
		OsdTools->SetHelpButtons();

		osd->Flush();
	}

	cCondWait::SleepMs(250); // Etwas Zeit einplanen, damit in BuildMenu die Daten bereitstehen und angezeigt werden k�nnen
	BuildMenu();

	// Thread starten, der die MetaDaten aktualisiert, wie vom Benutzer im Setup angegeben
	if( atoi( cSettings::GetInstance().ParameterMap[ "Osd_Refreshinterval" ].c_str() ) )
	{
		Start( );
	}

}

void cUserInterface::BuildMenu( void )
{
	meta = WebServiceConnector->Meta( );
	cCondWait::SleepMs( 250 ); // Etwas Zeit einplanen, damit in BuildMenu die Daten bereitstehen und angezeigt werden k�nnen

	cSettings::GetInstance().Osd_Albumcoverurl = meta.mp_MetadataMap[ "albumcover_large" ];
	
	// Titelanzeige (oben rechts)
	// artist
	OsdTools->DrawTextfield( m_Osd_Centre_x, m_Osd_Centre_y - 80,
			meta.mp_MetadataMap[ "artist" ].c_str( ), clrWhite );
	// track
	OsdTools->DrawTextfield( m_Osd_Centre_x, m_Osd_Centre_y - 40, meta.mp_MetadataMap[ "track" ].c_str( ),
			clrWhite );
	// album
	OsdTools->DrawTextfield( m_Osd_Centre_x, m_Osd_Centre_y, meta.mp_MetadataMap[ "album" ].c_str( ),
			clrWhite );
	// station
	OsdTools->DrawTextfield( m_Osd_Centre_x, m_Osd_Centre_y + 40,
			meta.mp_MetadataMap[ "station" ].c_str( ), clrWhite );
	
	
	// albumcover links, mittig
#ifdef LASTFM_HAVE_MAGICK
	if( cSettings::GetInstance().Osd_Albumcoverurl.compare("") &&
			atoi( cSettings::GetInstance().ParameterMap[ "Albumcovermode" ].c_str() ) )
	{
		cMP3Bitmap *bmp;

		if( ( bmp = cMP3Bitmap::Load( cSettings::GetInstance().Osd_Albumcoverurl,
				m_Osd_ImgAlpha,
				m_Osd_BitmapSize_x-1,
				m_Osd_BitmapSize_y-1,
				m_Osd_ColourDepth ) ) != 0 )
		{	

			if( osd->GetBitmap( 1 ) )
			osd->GetBitmap( 1 )->Reset();

			osd->DrawBitmap( 0,
					m_Osd_Centre_y - m_Osd_BitmapSize_y / 2,
					bmp->Get() );
		}

	}
#endif // LASTFM_HAVE_MAGICK
	osd->Flush( );
}

eOSState cUserInterface::ProcessKey( eKeys Key )
{
	if( !osd && Key == kBack )
	{
		// back was pressed in sub-menu -> if it has no sub-menu itself
		// then close it and show graphical mainmenu osd again
		if( UserInterface_SearchActions )
		{
			if( !strcmp ( cSettings::GetInstance().GetOsd_Path( ), "submenu" ) )
			{
				DELETENULL ( UserInterface_SearchActions );
				Show ( );
				return osContinue;
			}
		}
	}
	
	if( UserInterface_SearchActions )
	{
		// give the control to our sub-menu
		return UserInterface_SearchActions->ProcessKey( Key );
	}
	
	
	// process other events
	eOSState state = cOsdObject::ProcessKey ( Key );
	
	
	// OSD-Wechsel zw. OsdObject und OsdMenu; basiert auf vdr-pim von Achim Tuffentsammer (sundin?)
	if( (strcmp ( cSettings::GetInstance().GetOsd_Path( ), "submenu" ) == 0) && UserInterface_SearchActions )
	{
		// give the control to our sub-menu
		return UserInterface_SearchActions->ProcessKey( Key );
	}
	
	if( state == osUnknown )
	{
		
		if( osd )
		{
			switch( Key & ~k_Repeat )
			{
				case kRed:
					if( WebServiceConnector->Control( "love" ) != 0 )
					{
						///                    Skins.Message(mtError, tr("Control(\"love\") was not successful!"));
					}
					else
						OsdTools->BevelButton( 0, m_Osd_Height - 20, tr ( "Love" ), clrRed );
					break;
				case kGreen:
					if( WebServiceConnector->Control( "skip" ) != 0 )
					{
						///                    Skins.Message(mtError, tr("Control(\"skip\") was not successful!"));
					}
					else
					{
						OsdTools->BevelButton( 0 + m_Osd_BtnWidth, m_Osd_Height - 20, tr ( "Skip" ),
								clrGreen );
						BuildMenu ( );
					}
					break;
				case kYellow:
					if( WebServiceConnector->Control( "ban" ) != 0 )
					{
						///                    Skins.Message(mtError, tr("Control(\"ban\") was not successful!"));
					}
					else
					{
						OsdTools->BevelButton( 0 + m_Osd_BtnWidth + m_Osd_BtnWidth, m_Osd_Height - 20, tr ( "Ban" ),
								clrYellow );
						BuildMenu ( );
					}
					break;
				case kBlue:
					cSettings::GetInstance().CurrentProfilename.clear();
					Cancel( MAXIMUM_THREADKILLTIME );
					cControl::Shutdown ( );
					return osEnd;
					
				case k5:
					Cancel( MAXIMUM_THREADKILLTIME );
					cSettings::GetInstance().SetShow_SearchActions_Menu( true );
					cSettings::GetInstance().SetOsd_Path( "submenu" );
					osd->Flush( );
					if( osd )
						delete osd;
					osd=0;
					
					UserInterface_SearchActions = new cUserInterface_Submenu();
					
					break;
				case k0:
					BuildMenu ( );
					break;
				case kBack:
					Show ( );
					break;
					
				default:
					break;
			}
			state = osContinue;
		} // osd
	}
	return state;
}

// --- Thread -----------------------
void cUserInterface::Action( void )
{
	int c = 0; // summarize waitseconds up to our Osd_Refreshinterval, so
	// we have only waitstates of 10ms and Cancel is not that hard
	int c_max = atoi( cSettings::GetInstance().ParameterMap[ "Osd_Refreshinterval" ].c_str( ) ) * 1000;
	int mswaitstate = 10;
	
	while( Running ( ) )
	{
		cCondWait::SleepMs( mswaitstate );
		
		if( c_max <= c )
		{
			BuildMenu ( );
			
			c = 0;
		}
		
		c = c + mswaitstate;
	}
	
}

/////////////////////////////////////////////////////////////////////
// textbased OSD mainmenu:
cUserInterfaceTextbased::cUserInterfaceTextbased( void ) :
	cOsdMenu( tr ( "lastfm mainmenu" ), 12, 40), cThread("LASTFM: osd refresh")
{
	WebServiceConnector = new cWebServiceConnector();
	
	BuildMenu( ); // OSD-Elemente aufbauen

	// Thread starten, der die MetaDaten aktualisiert, wie vom Benutzer im Setup angegeben
	if( atoi( cSettings::GetInstance().ParameterMap[ "Osd_Refreshinterval" ].c_str( ) ) )
	{
		Start ( );
	}
	
}

cUserInterfaceTextbased::~cUserInterfaceTextbased( )
{
	Cancel( MAXIMUM_THREADKILLTIME );
	
	if( WebServiceConnector )
		delete WebServiceConnector;
	
	WebServiceConnector = 0;
}

void cUserInterfaceTextbased::BuildMenu( void )
{
	Clear ( );
	
	meta = WebServiceConnector->Meta( );
	cCondWait::SleepMs( 250 ); // let us harvest the metadata

	
	// Display the list
	/***** this one does not allow use of translation, because names are not
	 * available during compilation.
	 */
	/*
	
	std::list<std::string> strList;
	
	
	// Titelanzeige:
	// Create a list of items to display
	strList.push_back( "artist" );
	strList.push_back( "track" );
	strList.push_back( "album" );
	strList.push_back( "station" );
	
	
	 std::list<std::string>::iterator theIterator;
	 for( theIterator = strList.begin( ); theIterator != strList.end( ); theIterator++ )
	 {
	 std::stringstream sstream1;
	 std::string str1, str2 = *theIterator;
	 const char *seperator = "\t";
	 
	 sstream1 << trNOOP( str2.c_str( ) ) << seperator
	 << meta.mp_MetadataMap[ *theIterator ];
	 
	 getline( sstream1, str1 ); // feeding str1 with sstream1 // the '>>' cuts white spaces; still to investigate, but this way works
	 // ...the answer is using sstream1.str()
	 Add( new cOsdItem( gettext( str1.c_str() ) ) );
	 
	 }*/

	std::stringstream sstream1;
	const char *seperator = "\t";
	
	sstream1.clear( );  // clear resets bits only
	sstream1.str(""); // assign empty string before further inputs
	sstream1 << tr( "artist" ) << seperator <<meta.mp_MetadataMap[ "artist" ];
	Add( new cOsdItem( sstream1.str().c_str() ) );
	
	sstream1.clear( );
	sstream1.str("");
	sstream1 << tr( "track" ) << seperator <<meta.mp_MetadataMap[ "track" ];
	Add( new cOsdItem( sstream1.str().c_str() ) );
	
	sstream1.clear( );
	sstream1.str("");
	sstream1 << tr( "album" ) << seperator <<meta.mp_MetadataMap[ "album" ];
	Add( new cOsdItem( sstream1.str().c_str() ) );
	
	sstream1.clear( );
	sstream1.str("");
	sstream1 << tr( "station" ) << seperator <<meta.mp_MetadataMap[ "station" ];
	Add( new cOsdItem( sstream1.str().c_str() ) );
	
	
	// context buttons:
	SetHelp( tr( "Love" ), tr( "Skip" ), tr( "Ban" ), tr( "Exit" ) );
	
	Display();
}

eOSState cUserInterfaceTextbased::ProcessKey( eKeys Key )
{
	bool HadSubMenu = HasSubMenu ( );
	
	eOSState state = cOsdMenu::ProcessKey ( Key );
	HadSubMenu |= HasSubMenu ( );
	
	if( state == osUnknown )
	{
		switch( Key )
		{
			case k5:
				if( !HasSubMenu ( ) ) // only triggered in submenus
				{
					AddSubMenu ( new cUserInterface_Submenu() );
				}
				state = osContinue;
				break;
				
			case kRed:
				if( !HadSubMenu ) // only triggered in main menu
				{
					
					if( WebServiceConnector->Control( "love" ) != 0 )
					{
						Skins.Message( mtError,
								tr ( "Control(\"love\") was not successful!" ) );
					}
				}
				break;
				
			case kGreen:
				if( !HadSubMenu ) // only triggered in main menu
				{
					
					if( WebServiceConnector->Control( "skip" ) != 0 )
					{
						Skins.Message( mtError,
								tr ( "Control(\"skip\") was not successful!" ) );
					}
					
				}
				break;
				
			case kYellow:
				if( !HadSubMenu ) // only triggered in main menu
				{
					if( WebServiceConnector->Control( "ban" ) != 0 )
					{
						Skins.Message( mtError,
								tr ( "Control(\"ban\") was not successful!" ) );
					}
				}
				break;
				
			case kBlue:
				if( !HadSubMenu ) // only triggered in main menu
				{
					cSettings::GetInstance().CurrentProfilename.clear();
					Cancel( MAXIMUM_THREADKILLTIME );
					cControl::Shutdown ( );
					return osEnd;
				}
				break;
				
			case k0:
				if( !HadSubMenu ) // only triggered in main menu
				{
					BuildMenu ( );
					break;
				}
				
			default:
				break;
		}
	}
	
	return state;
}

// --- Thread ------------------------------------------------------------------
void cUserInterfaceTextbased::Action( void )
{
	int c = 0; // summarize waitseconds up to our Osd_Refreshinterval, so
	// we have only waitstates of 10ms, so Cancel() will not be that hard
	int c_max = atoi( cSettings::GetInstance().ParameterMap[ "Osd_Refreshinterval" ].c_str( ) ) * 1000;
	int mswaitstate = 10;
	
	while( Running ( ) )
	{
		cCondWait::SleepMs( mswaitstate );
		
		if( c >= c_max)
		{
			BuildMenu ( );

			c = 0;
		}
		
		c = c + mswaitstate;
	}
	
}

eOSState cUserInterfaceTextbased::OnBlue( void )
{
	eOSState state = osUnknown;
	
	if( !HasSubMenu ( ) )
	{
		return osUnknown;
	}
	
	return state;
}
