#include "xineOsd.h"
#include "Control.h"
#include "Player.h"
#include "timecounter.h"

#include <vdr/remote.h>
#include <vdr/plugin.h>
#include <vdr/tools.h>

//id3tags : using TagLib
#include <fileref.h>
#include <tag.h>


#include "xineOsdInfoMenu.h"

#define PROGRESSBARLEN 30

namespace Reel
{
	namespace XineMediaplayer
	{
		
		cTimeMs osdTimeOut; // refresh Osd after x secs

		////////////////////////////////////////////////////////////////////////////////
		//////// Static variables
		////////////////////////////////////////////////////////////////////////////////
		char			*cXineOsd::mrl		 = NULL  ;
		bool 			cXineOsd::isPlaylist 	 = false ;   
        int             cXineOsd::browserInstance     = -1 ;
		bool 			cXineOsd::isNew 	 = true  ;
                bool                    cXineOsd::closeOsd       = false ;
                bool                    cXineOsd::firstOpen      = false ;
		
		Control			*cXineOsd::control	 = NULL	 ;
		bool			cXineOsd::isPlaying	 = false ;
		bool			cXineOsd::isOpen	 = false ;
		time_t 			cXineOsd::lastVolShownTime = 0   ;
                int                     cXineOsd::k_blue         = FILEBROWSER_PLUGIN;
                int                     cXineOsd::kPausePressed  = 0;
                std::string             cXineOsd::openError      = "";

                std::vector<std::string> 		cXineOsd::playListEntries 	 ; // the playlist
                std::vector<std::string> 		cXineOsd::nameListEntries 	 ; // the display names of each playlist entry

		////////////////////////////////////////////////////////////////////////////////
		/////// Utility functions
		////////////////////////////////////////////////////////////////////////////////

		std::string MakeProgressBarStr(float percent, int length)
		{
			// makes [|||||    ] for given percent and length of string

			if (percent <0 || percent > 100 || length <=0 ) return "";

			int p = (int)(percent/100*length);
			std::string str = "[";
			str.insert( str.size(), length, ' '); // fill with ' '
			str.replace( 1, p, p, '|'); // replace with appropriate '|'
			str.append("]");

			return str;
		}

		/*
		   Hour(if > 0) : Min : Sec format from Index
		   */
		std::string HMS(int Index, bool WithHour = false)
		{
			char buffer[16];
#if VDRVERSNUM < 10704
			int s = (Index / FRAMESPERSEC);
#else			
			int s = (Index / DEFAULTFRAMESPERSECOND);
#endif
			int m = s / 60 % 60;
			int h = s / 3600;
			s %= 60;

			if ( h > 0 || WithHour ) // show Hour ?
				snprintf(buffer, sizeof(buffer), "%d:%02d:%02d" , h, m, s);
			else
				snprintf(buffer, sizeof(buffer), "%02d:%02d" ,  m, s);
			return std::string(buffer);
		}
		////////////////////////////////////////////////////////////////////////////////
		// ID3 info : wrapper functions for taglib
		////////////////////////////////////////////////////////////////////////////////	

		////////////////////////////////////////////////////////////////////////////////	
		bool Id3Info(std::string songPath, std::string& title,std::string& artist, std::string&album )
		{
			TagLib::FileRef f( songPath.c_str() );
			if(f.isNull() || !f.tag()) return false;

			TagLib::Tag *tag = f.tag();
			title 	= tag->title().stripWhiteSpace().toCString(false)  ; // unicode = false
			artist 	= tag->artist().stripWhiteSpace().toCString(false) ;
			album  	= tag->album().stripWhiteSpace().toCString(false)  ;

			return true;
		}

		
		////////////////////////////////////////////////////////////////////////////////
		// class cXineOsd
		////////////////////////////////////////////////////////////////////////////////

		cXineOsd::cXineOsd() : cOsdMenu("Mediaplayer",  7, 15)
		{
			//LOC; printf( "%s\n", mrl );
                        //DEBUG_VAR(mrl);
			//DEBUG_VAR( isPlaylist );
			//DEBUG_VAR( playListEntries.size() );

			lastVolShownTime = 0;

			//if empty playlist then try adding mrl if possible:
			if ( playListEntries.size()<= 0 && mrl != NULL )
			{
                            playListEntries.push_back(mrl);
                            cXineOsd::isNew = 1;
			}

                        if (0 && cXineOsd::isNew == 1 )
                        {
                            if ( playListEntries.size() > 0 )
                                control->SetPlayList(playListEntries);
                            else
                            {
                                printf("%s:%d %s ERROR: playlist is NEW and empty\
                                        : size = %i\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, playListEntries.size() );
                            }
                        }

			//currentlySelected = -1; // none-selected
			control->PlayerPositionInfo(currentlyPlaying, index, total);
			currentlySelected = currentlyPlaying;

			xineStatus = new cXineStatus; // for volume
                        
                        FunctionMode = 0;
                        
			ShowEntries();
			cXineOsd::isOpen         = true ;
		}

		////////////////////////////////////////////////////////////////////////////////
		// ~cXineOsd
		cXineOsd::~cXineOsd()
		{
			//playListEntries.clear();
			delete xineStatus;
			cXineOsd::isOpen         = false ;
		}

		////////////////////////////////////////////////////////////////////////////////
		void cXineOsd::ClearStaticVariables()
		{
			kPausePressed	= 0    ;
			mrl 		= NULL ;
			isPlaylist 	= false;
			playListEntries.clear();
			nameListEntries.clear();
			control 	= NULL ;
			isPlaying 	= false;
			isOpen 		= false;
                        isNew           = true ;
                        k_blue          = NO_PLUGIN;
                        openError       = "";
		}

		////////////////////////////////////////////////////////////////////////////////
		void cXineOsd::UpdateEntries() // update OSD
		{
			if( !isPlaying ) // close OSD ?
			{
				cRemote::Put(kBack);
				return;
			}
                        if ( !openError.length() && cXineOsd::firstOpen && cXineOsd::control->IsPlayingVideo())
                        {
                            cXineOsd::closeOsd = true;
                            cXineOsd::firstOpen = false;
                            return ;
                        }

			int tmpSelected = Current(); // prevent select-'highlight' from disappering
			if (tmpSelected > currentlyPlaying) tmpSelected -= 2;
			
                        int prevCurrent=currentlyPlaying;
                        int index_tmp = index, total_tmp = total;
                        
			/**** get the position in the playlist ****/
			control->PlayerPositionInfo(currentlyPlaying, index, total);
                        if (index_tmp == index && currentlyPlaying == prevCurrent  && total == total_tmp) return ;

			char playedTime[64];
			sprintf(playedTime, "%10s",HMS(index).c_str() ); // right indented towards progress bar
			std::string str;
			str += playedTime;
			str +=  std::string(" \t") + MakeProgressBarStr( total > 0 ? index*100.0/total:50 , PROGRESSBARLEN);

                        // if stream/ if total <=0 donot show total, just the elapsed time should be sufficient
                        if (total > 0)
                            str += std::string("\t") + HMS(total);

			Del( prevCurrent + 1); // remove old progress bar
			Del( prevCurrent + 1); // remove blank line

			Add( new cOsdItem(  str.c_str(), osUnknown, false )  ,false, Get(currentlyPlaying)); //add new progress bar after currentPlaying
			Add( new cOsdItem("", osUnknown, false), false, Get(currentlyPlaying+1) ) ; // add new blank line 


			/** donot loose the currently selected item by the user when switching to a new 'song'**/
			if ( tmpSelected == prevCurrent && currentlyPlaying > prevCurrent )
				SetCurrent( Get(currentlyPlaying) ); // to prevent highlight from disappering 
			else if (tmpSelected > currentlyPlaying) 
				SetCurrent(Get(tmpSelected+2));
			else 
				SetCurrent(Get(tmpSelected));

			if (prevCurrent != currentlyPlaying ) // update title
			{
				std::string title, dummy;
				std::string setTitle_str = "Mediaplayer";
				Id3Info( playListEntries[currentlyPlaying], title, dummy, dummy);
				if (!title.empty() ) setTitle_str += ": " + title;
				SetTitle(setTitle_str.c_str() );
			}

			Display();

			//some times control never reaches here, 
			//so call ShowPlayMode() after calling this function
			
                        //ShowPlayMode();
		}


		////////////////////////////////////////////////////////////////////////////////
		void cXineOsd::ShowEntries()
		{
			if( !isPlaying ) // close OSD ?
			{
				cRemote::Put(kBack);
				return;
			}

			Clear(); // clear osd

			/**** get the position in the playlist ****/
			control->PlayerPositionInfo(currentlyPlaying, index, total);


			std::vector<std::string>::iterator it = playListEntries.begin();
			size_t found;
			int i=0;

			// add playlist entries to screen
			for( ; it!=playListEntries.end(); ++i, ++it)
			{
				found = it->find_last_of('/'); 
				std::string str = it->substr(found+1); // TODO checks on found

				// remove '_' from file names
				unsigned int j = 0;
				while( j < str.size() )
				{
					if (str[j] == '_') str[j] = ' ';
					j++;
				}
				
				std::string Name;

				
                                
                                if (nameListEntries.size() ==  playListEntries.size() && !nameListEntries[i].empty()  )
                                    Name = nameListEntries[i];
                                else
                                {
                                  std::string title, artist, album;
                                    
                                if ( title.empty() ) 
                                {
                                    // try id3 tag
                                    Id3Info(it->c_str(), title, artist, album );
                                    printf("Got from Id3 info %s\n", title.c_str() );
                                }

				if (!title.empty() ) 	
				{
					Name  = title ; 

					//if (!album.empty() ) 	Name += "-" + album;
					if (!artist.empty() ) 	Name += " - " + artist;
				}
                                
				if (Name.empty()) // show filename
				{
					//TODO : remove .mp3/.wav extn
					Name = str;
				}
                                
                                // update the title in the list, if there are 'blanks' they are filled up
                                if ( !Name.empty()  && nameListEntries.size() ==  playListEntries.size() )
                                      nameListEntries[i] = Name.substr(0,100);
                                }
				// add number 
				char num[8]; sprintf(num,"%d.  ",i+1);
				Name = num + Name.substr(0,100);
				// display 'Song'/file  name
				Add( new cOsdItem( Name.c_str() , osUnknown, true ) ); 

				// currently playing
				if (currentlyPlaying == i) // currentlyplaying counts from 0...playlist.size()-1
				{
					char playedTime[64];
					sprintf(playedTime, "%10s",HMS(index).c_str() );
					std::string str;
					str += playedTime;
					str +=  std::string(" \t") + MakeProgressBarStr(total>0?index*100.0/total:0, PROGRESSBARLEN);
					str += std::string("\t") + HMS(total);

					Add( new cOsdItem(  str.c_str() , osUnknown, false ) ); 
					Add( new cOsdItem(  "" , osUnknown, false ) ); 
				}

			}

			if(currentlySelected < 0)
				currentlySelected = currentlyPlaying;

			// donot select the progressBar and the empty line that follow the currently playing song
			if ( currentlyPlaying < currentlySelected )
				SetCurrent( Get( currentlySelected + 2) );
			else
				SetCurrent( Get( currentlySelected ) );

			// update title

			std::string title, dummy;
			std::string setTitle_str = "Mediaplayer";
			Id3Info( playListEntries[currentlyPlaying], title, dummy, dummy);

			if (!title.empty() ) 
				setTitle_str += ": " + title;
			SetTitle(setTitle_str.c_str() );

                        SetHelpButtons();
                        ShowPlayMode(true);
			Display();
                        cXineOsd::isNew = false;
		}
                ////////////////////////////////////////////////////////////////////////////////
                void cXineOsd::ShowPlayMode(bool force)
                {
                  static int _mode = -1 ;
		  if (kPausePressed)
		  {
			  //when un-paused should replace the status message
			  _mode = -1; 

			  //check if player is really paused
			  if (cXineOsd::control->IsPaused()) 
			  {
				  SetStatus(tr("Paused"));
				  return ;
			  } 
			  else 
			  {
                                // xine can be un-paused by atleast by pressing
                                // kPause and kPlay and choosing to play another file

                                // reset kPausePressed if xine is playing
			  	kPausePressed = 0;
				SetStatus(NULL);
			  }
		  }
		else 
		/*clear status if Paused had been displayed, 
		 * else it is not cleared when playing internet radio*/
			if (_mode == -1)
				SetStatus(NULL);

                  if ( !CanHaveDifferentModes() ) return;

                  
                  int mode_tmp = cXineOsd::control->GetPlayMode() ;
                  
                  // Mode not changed
                  if ( !force && _mode == mode_tmp) return;
                  else
                  _mode = mode_tmp;
                      
                  switch( _mode )
                  {
                    case PLAY_NORMAL:
                      SetStatus(tr("Normal"));
                      break;
                    
                    //case PLAY_REPEAT_SINGLE:
                    case PLAY_REPEAT_PLAYLIST:
                      SetStatus(tr( "Repeat") );
                      break;
                      
                    case PLAY_RANDOM:
                      SetStatus( tr("Shuffle") );
                      break;
                      
                    default:
                      SetStatus(NULL);
                  }

                  if(openError.length())
                     Skins.QueueMessage(mtError, openError.c_str(), Setup.OSDMessageTime*2); // Show little longer...
                }

                ////////////////////////////////////////////////////////////////////////////////
                void cXineOsd::SetHelpButtons()
                {
                  if ( FunctionMode == 1 )
                  {
                    if ( playListEntries.size() > 2)
                        SetHelp( NULL, tr("Shuffle") ,tr("Repeat"),tr("Normal") );
                    else 
                        SetHelp(NULL, NULL, tr("Repeat"),tr("Normal") );
                    
                    return;
                  }
                    switch(cXineOsd::k_blue)
                    {
                        case FILEBROWSER_PLUGIN:
                            //SetHelp( NULL, "<<", ">>", tr("overview") );
                            SetHelp( tr("Overview"), NULL, tr("Info"), tr("Function") );
                            break;

                        case SHOUTCAST_PLUGIN:
                          //SetHelp( tr("shoutcast"), NULL, NULL, NULL );
                          SetHelp( tr("shoutcast"), NULL, NULL, NULL );
                            break;

                        default:
                            SetHelp( NULL, NULL, NULL, NULL );
                            break;
                    }

                } 
                ////////////////////////////////////////////////////////////////////////////////
                bool cXineOsd::CanHaveDifferentModes()
                {
                  switch(cXineOsd::k_blue)
                  {
                    case FILEBROWSER_PLUGIN:
                      return true;
                      break;
                    case SHOUTCAST_PLUGIN:
                      return false;
                      break;

                    default:
                      esyslog("(%s:%d) cXineOsd::k_blue not set to a known value", __FILE__, __LINE__);
                      break;
                  }
                  return false;
                }
                ////////////////////////////////////////////////////////////////////////////////
                void cXineOsd::CallExt_Plugin()
                {
                    switch(cXineOsd::k_blue)
                    {
                        case FILEBROWSER_PLUGIN:
                            CallFilebrowser(xine_browse);
                            break;
                        case SHOUTCAST_PLUGIN:
                            cRemote::CallPlugin("shoutcast");
                            break;

                        case MMFILES_PLUGIN:
                            cRemote::CallPlugin("mmfiles");
                        default:
                            esyslog("(%s:%d) cXineOsd::k_blue not set to a known value", __FILE__, __LINE__);
                            break;
                    }
                }
               
                ////////////////////////////////////////////////////////////////////////////////
                void CallFilebrowser(eXineControl xineCtrl)
                { 
                    printf("Xine: CallFilebrowser, browserInstance = %d\n", cXineOsd::browserInstance);        
                    struct
                    {
                        int instance;
                    } instanceData = { 
                                    cXineOsd::browserInstance
                                };
                    cPluginManager::CallAllServices("Filebrowser set instance", &instanceData);

                    enum browserCaller
                    {
                        normal, autorestart, svdrp, mediaplayer, xine
                    };
                    struct
                    {
                        browserCaller caller;
                        int instance;
                    } callerData = { 
                                    xine,
                                    cXineOsd::browserInstance
                                };
                    cPluginManager::CallAllServices("Filebrowser set caller", &callerData);

                    struct 
                    {
                        eXineControl ctrl; 
                        int instance;
                    } ctrlData = 
                                { 
                                    xineCtrl,
                                    cXineOsd::browserInstance
                                };
                    cPluginManager::CallAllServices("Filebrowser xine control", &ctrlData);

                    cRemote::CallPlugin("filebrowser");
                }


		////////////////////////////////////////////////////////////////////////////////
		eOSState cXineOsd::ProcessKey(eKeys Key)
		{
			eOSState state = osContinue;

                        if (cXineOsd::closeOsd)
                        {
                            cXineOsd::closeOsd = false;
                            cXineOsd::firstOpen = false;
                            printf("\033[0;91m Closing Osd\033[0m\n");
                            return osBack;
                            //return osEnd; This seems to prepare a crash in vdr during next use of cXineOsd
                        }
                        
                        if ( HasSubMenu() )
                        {
                            if (Key == kBack)
                                CloseSubMenu();

                            return osContinue;
                        }
                        // time counter for mode: should be used to clear mode when exceeding 4 secs
                        static cTimeCounter timeCounter_mode;

			///Display current Volume in MessageBar
			if (lastVolShownTime > 0 && time(0) - lastVolShownTime >= 2) // clear
			{
				SetStatus(NULL); // clear volume status
				lastVolShownTime = 0;
                                ShowPlayMode(true);
			}
			else if ( time(0) - lastVolShownTime <=1 ) 
			{
				int isMute = cDevice::PrimaryDevice()->IsMute();
				std::string vol = tr("Volume") + std::string(": ") + MakeProgressBarStr( (!isMute)*cDevice::PrimaryDevice()->CurrentVolume()*100.0/255, 30  );
				SetStatus(vol.c_str() );
				lastVolShownTime = time(0);
			}

			currentlySelected = Current();
			if (currentlySelected > currentlyPlaying ) 
				currentlySelected -= 2;  // account for the two additional line in OSD //progress & blank
			
                        
                        if (FunctionMode == 1)
                        {
                          switch(Key)
                        {
                          case kBack:
                            break;

                          case kGreen: 
                             // Shuffle/Random
                            if (playListEntries.size() > 2 )
                              cXineOsd::control->SetPlayMode(PLAY_RANDOM);
                             break;

                          case kBlue:
                              cXineOsd::control->SetPlayMode(PLAY_NORMAL); // XXX Set Mode Normal
                              break;

                          case kYellow:
                              // Repeat
                              //cXineOsd::control->SetPlayMode(PLAY_REPEAT_SINGLE);
                              cXineOsd::control->SetPlayMode(PLAY_REPEAT_PLAYLIST);
                              break;

                          case kRed:
                              
                          case kNone:
                            // Else Player freezes after completing the current mrl!
                            if(isPlaying)
                              control->ProcessKey(Key);
                            break;
                            
                          default:
                            
                            break;

                        }
                        
                        /*Timeout for showing function modes */
                        if ( Key != kNone || timeCounter_mode.Seconds() > 7)
                        {
                          FunctionMode = 0;
                          SetHelpButtons();
                          Key = kNone;
                        }
                        
                       }
                        else
                        if (FunctionMode == 0)
			switch(Key)
			{
				case kInfo : // trap kInfo key
					break;
				case kOk: 
					if (currentlySelected == currentlyPlaying) // close OSD
					{
// Directy quit						cRemote::Put(kBack);
						state = osBack;
						break;
					}

                                        Skins.Message(mtStatus, tr("Loading..."));
                                        // slow in playing next cd track, so some visual cue to the user

					// should play the correct song... 
					if (!control->PlayFileNumber( currentlySelected ) )
						Skins.Message(mtError, tr("Could not play file"));
					break;

				case kRed: 
					CallExt_Plugin();
					break;

				case kBlue: 
                                  // different modes only for normal playlist, not for internet radio
                                  if ( CanHaveDifferentModes() )
                                  {
					FunctionMode = 1;
					SetHelpButtons();
                                        // start time out
                                        timeCounter_mode.CleanStart();
                                  }

					break;

                                case kGreen: // overrides... <<
                                    break;

                                case kYellow: // overrides... >>
                                    if (cXineOsd::k_blue == FILEBROWSER_PLUGIN)
                                        AddSubMenu(new cFileInfoMenu(playListEntries.at(currentlySelected)));

                                    break;

				case kBack: 
                                  
					//fall thro'
				case kUp:
				case kUp|k_Repeat://prev
					//fall thro'
				case kDown:
				case kDown|k_Repeat: // next

					state = cOsdMenu::ProcessKey(Key);
					break;

				case kChanUp: 
				case kChanUp|k_Repeat: // page up
					state = cOsdMenu::ProcessKey(kLeft); 
					break;

				case kChanDn:
				case kChanDn|k_Repeat: // page down
					state = cOsdMenu::ProcessKey(kRight); 
					break;

				default:
					if (Key == kPause)  
					{ 
						kPausePressed = 1; 
						// also set inside XineLib.c, in the case Osd is closed
					}
                          
                            // fall thro'
				case kLeft:
				case kRight:
					if(isPlaying)
					{
                                            if ( osEnd == control->ProcessKey(Key) ) 
					    	cControl::Shutdown(); // now shuts down with kStop
					    else 
					    	if (Key == kLeft || Key == kRight) // player requested to skip
					    		osdTimeOut.Set(2000);  // give player time to react on the jump, so delay osd refresh
					}
                              
					break;
			}


			if (cXineOsd::isNew)
                            ShowEntries(); // refresh
                        else
                            if (osdTimeOut.TimedOut()) 
			    	{ 
					UpdateEntries(); 
					ShowPlayMode();
					osdTimeOut.Set(1000); // refresh every 1 second
				}

			// return to current song after 7 secs
			static time_t lastUserInputTime = time(0);
			if (Key != kNone )
				lastUserInputTime = time(0);
			else // Key == kNone
				if (time(0) - lastUserInputTime > 7 ) // return to current file
					SetCurrent(Get(currentlyPlaying));


			return state;
		}

		////////////////////////////////////////////////////////////////////////////////
		//End class cXineOsd
		////////////////////////////////////////////////////////////////////////////////


		/////////////////////////////////////////////////////////////////////////////////////////////
		// class cXineVolStatus
		/////////////////////////////////////////////////////////////////////////////////////////////

		void cXineStatus::SetVolume(int Vol, bool abs)
		{
			printf("(%s:%d) :%s(%d/%d)\n", __FILE__,__LINE__,__PRETTY_FUNCTION__, Vol, abs);
			cXineOsd::lastVolShownTime = time(0);
		}

	} //namespace Xinemediaplayer
} // namespace Reel
