/*
* Image plugin to VDR (C++)
*
* (C) 2004-2005 Andreas Brachold <anbr at users.berlios.de>
* based on (C) 2003 Kai Tobias Burwieck <kai -at- burwieck.net>
*
* This code is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*/

#include <time.h>

#include "control-image.h"
#include "data-image.h"
#include "setup-image.h"
#include "config.h"
#include "i18n.h"
#include "list.h"
#include "bitmap.h"

#include <vdr/status.h>
#include <vdr/tools.h>
#include <vdr/plugin.h>
#include <vdr/osd.h>

//////////////////////////////////////////////////////////////////////////////
/** C-tor Create for control-object to control the image-player
*/
cImageControl::cImageControl()
:cOsdObject()
,pBrowser(NULL)
,pCommands(NULL)
{
  m_tStarted = time(NULL);
  m_bSlideShowActiv = ImageSetup.m_bSlideShow;

  coversize = 0;
  w1=120;
  h1=120;
  fh=27;

  visible=musicplays=false;
  CanLoadCover=false;
  osd = NULL;
}


void cImageControl::SetSlideShow(cSlideShow * pNewSlideShow)
{
  theSlideShow.Assign(pNewSlideShow);
}

//////////////////////////////////////////////////////////////////////////////
/** D-tor Destroy control-object to control the image-player
*/
cImageControl::~cImageControl()
{
  // Free OSD Data
  theSlideShow.Shutdown();
  if(pBrowser)  { delete pBrowser;  pBrowser=NULL; }
  if(pCommands) { delete pCommands; pCommands=NULL; }
  
  Hide();
}

void cImageControl::Hide()
{
  if(osd) { delete osd; osd=NULL; }
  visible = false;
}


void cImageControl::Show()
{
  if (FileName()== NULL ) {
    esyslog("picselshow : Oops can't load coverpicture : %s\n",FileName());
    }
  else {
    cImageBitmap *bmp;

    int bmpcolors=16;
    int w=119;
    int h=119;
    int depth=8;

    coversize=ImageSetup.Coversize;
    switch(coversize) {
          case 0:
	    bmpcolors = 253;
	    w1 = 120;
	    h1 = 120;
            depth = 8;
            w     = w1 -1;
            h     = h1 -1;
	  break;  
	  case 1:  
	    bmpcolors = 253;
	    w1 = 274;
	    h1 = 274;
            depth = 8;
            w     = w1 -1;
            h     = h1 -1;
	  break;
	  case 2:
	    bmpcolors = 253;
	    w1 = 300;
	    h1 = 256;
            depth = 8;
            w     = w1 -1;
            h     = h1 -1;
	  break;
	  case 3:
	    bmpcolors = 253;
	    w1 = ImageSetup.max_osdwidth;
	    h1 = ImageSetup.max_osdheight;
            depth = 8;
            w     = w1 -1;
            h     = h1 -1;
	  break;
    }	        

    if((bmp = cImageBitmap::Load(FileName(), 255, h, w1 , bmpcolors)) !=NULL) {
      Hide();
    
      CanLoadCover=true;

      osd=cOsdProvider::NewOsd(ImageSetup.max_osdleft, ImageSetup.max_osdtop);
      if(!osd) return;

      tArea Area[] = {{ 0, 0, w, h, depth}, };
      eOsdError result = osd->CanHandleAreas(Area, sizeof(Area) / sizeof(Area));
      if (result == oeOk) {
        osd->SetAreas(Area, sizeof(Area) / sizeof(Area));
        }
      else {
        const char *errormsg = NULL;
        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"; break;
          case oeOutOfMemory:
            errormsg = "OSD memory overflow"; break;
          case oeUnknown:
            errormsg = "Unknown OSD error"; break;
          default:
            break;
        } 	

        esyslog("picselshow: ERROR! OSD open failed! Can't handle areas (%d)-%s\n", result, errormsg);
        if(osd) { delete osd; osd=0; }
        return;
      }  
 
      if(CanLoadCover) {

        osd->GetBitmap(0)->SetColor(1, tColor(0xFF000000));
        osd->GetBitmap(0)->SetColor(0, tColor(0xFFFFFFFF));
        osd->DrawRectangle(0, 0, w -1, h -1, osd->GetBitmap(0)->Color(0));

        osd->DrawBitmap( 0, 1, bmp->Get(), clrTransparent, clrTransparent, true);

        CanLoadCover = false;
        m_tStarted = time(NULL);

        if(ImageSetup.EnableFade) FadeIn(); else Flush();
      }

    }
    else delete bmp;

  }              

}


void cImageControl::FadeIn(void)
{
#if VDRVERSNUM >= 10500
  SetNeedsFastResponse(true);
#else
  needsFastResponse = true;
#endif
  int i;
  int alpha = 0;
  
  int fd = 15;
  int count = 0;
  
  while (!(fd==0)) {
    if(osd){
      count++;

      alpha = alpha + 16;
      if(alpha > 255) alpha = 0;

      if(coversize==3) {
        if(ImageTotal() > 0) {
          osd->DrawRectangle(0, 0, w1 -1, 1, osd->GetBitmap(0)->Color(1));

          osd->DrawRectangle(count, 0, count +1, 1, osd->GetBitmap(0)->Color(0));

          }
        else {
          osd->DrawText( 0, 0, tr("[ -OK- FOR BROWSER ]  [ -BLUE- START MUSIC ]") , osd->GetBitmap(0)->Color(0), osd->GetBitmap(0)->Color(1), cFont::GetFont(fontSml), w1, fh, taCenter);
          osd->DrawRectangle(count, 0, count +1, 1, osd->GetBitmap(0)->Color(0));

          }  
        }
      else {
        osd->DrawRectangle(0, 0, w1 -1, 1, osd->GetBitmap(0)->Color(1));
        osd->DrawRectangle(count, 0, count +1, 1, osd->GetBitmap(0)->Color(0));
      }


      for ( i = 1; i < 256; i++) {
        osd->GetBitmap(0)->SetColor(i, (((osd->GetBitmap(0)->Color(i))<<8)>>8) | (alpha << 24));
      }

      Flush();
    }

  fd--;
  }

#if VDRVERSNUM >= 10500
  SetNeedsFastResponse(false);
#else
  needsFastResponse = false;
#endif
}


void cImageControl::FadeOut(void)
{
#if VDRVERSNUM >= 10500
  SetNeedsFastResponse(true);
#else
  needsFastResponse = true;
#endif
  int i;
  int alpha = 255;
  int fd = 16;
  int count = 0;
  
  while (!(fd==0)) {
    if(osd){
      count++;
      alpha = alpha - 16;
      if(alpha < 0) alpha = 0;

      if(coversize==3) {
        if(ImageTotal() > 0) {
          osd->DrawRectangle(0, 0, w1 -1, 1, osd->GetBitmap(0)->Color(1));

          osd->DrawRectangle(count, 0, count +1, 1, osd->GetBitmap(0)->Color(0));

          }
        else {
          osd->DrawText( 0, 0, tr("[ -OK- FOR BROWSER ]  [ -BLUE- START MUSIC ]") , osd->GetBitmap(0)->Color(0), osd->GetBitmap(0)->Color(1), cFont::GetFont(fontSml), w1, fh, taCenter);
          osd->DrawRectangle(count, 0, count +1, 1, osd->GetBitmap(0)->Color(0));
          }  
        }
      else {
        osd->DrawRectangle(0, 0, w1 -1, 1, osd->GetBitmap(0)->Color(1));
        osd->DrawRectangle(count, 0, count +1, 1, osd->GetBitmap(0)->Color(0));
      }


      for ( i = 1; i < 256; i++) {
        osd->GetBitmap(0)->SetColor(i, (((osd->GetBitmap(0)->Color(i))<<8)>>8) | (alpha << 24));
      }

      Flush();
    }

  fd--;
  }

#if VDRVERSNUM >= 10500
  SetNeedsFastResponse(false);
#else
  needsFastResponse = false;
#endif
}



void cImageControl::Flush(void)
{
  if(osd) osd->Flush();
}  


//////////////////////////////////////////////////////////////////////////////
/** VDR-Callback entry for Key was processed
@return eOSState
@param eKeys Key - the processed Keycode
*/
eOSState cImageControl::ProcessKey(eKeys Key)
{
  // Check for next image


  if(pBrowser) {
  
    eOSState eOSRet = pBrowser->ProcessKey(Key);

    switch(eOSRet) {
      case osBack:
         delete pBrowser;
	 pBrowser = NULL;
         visible =false;
         Show();

         return osContinue;
      default: return eOSRet;
   
    }
  }
  else if(pCommands) {

    eOSState eOSRet = pCommands->ProcessKey(Key);
    
    switch(eOSRet) {
      case osBack:
         delete pCommands;
	 pCommands = NULL;
         visible =false;
         Show();

         return osContinue;
      default: return eOSRet;
    }
    
  }    
  else {
 
    SlideImage();

    eOSState state = cOsdObject::ProcessKey(Key);

    if(state==osUnknown) {
      switch(Key) {

        //processed global keybindings
        // Toggle between Play/Pause
        case kPlay:     
        case kPause:   ToogleSlideShowActiv();
                       break;

        // Stop Plugin    
        case kBack:
        case kStop:     return osEnd;

  
      
         // Change time how long image is see
         case k1|k_Repeat:
         case k1:       DecSlideTime();
                        break;

         case k3|k_Repeat:
         case k3:     
                        IncSlideTime(); 
                        break;
  
         // Navigate between images
         case kLeft:
         case kGreen:
	                if( m_bSlideShowActiv && ImageTotal() >0) {
		          PrevImage(1);
		          CanLoadCover = true;
		          Show();
		        }
		        break;

         case kRight:
         case kYellow:
	                if( m_bSlideShowActiv && ImageTotal() >0) {
		          NextImage(1);
		          CanLoadCover = true;
		          Show();
		        }
		        break;
 
         case kOk: 
                        Hide();
                        pBrowser = new cMenuImageBrowse();
		        break;

         case kRed: 
                        Hide();
                        pCommands = new cMenuMP3Playlist();
		        break;

         case kBlue:   
                        if(ImageSetup.AutoMusic && !musicplays) {
                          PlayMusic();
	                  musicplays = true;
                        }	
	                break;
 
         case kDown:
	                if( m_bSlideShowActiv && ImageTotal() >0) {
		          PrevImage(3);
		          CanLoadCover = true;
		          Show();
		        }
		        break;

         case kUp:      if( m_bSlideShowActiv && ImageTotal() >0) {
		          NextImage(3);
		          CanLoadCover = true;
		          Show();
		        }
		        break;

//       case kNone: return osContinue;		    

         default: break;
      }

    state = osContinue;
    }

  return state;
  }


}



//////////////////////////////////////////////////////////////////////////////
/** Toogle between Play and Stop of the current SlideShow 
*/
void cImageControl::ToogleSlideShowActiv(void)
{
  m_bSlideShowActiv = !m_bSlideShowActiv;
}


//////////////////////////////////////////////////////////////////////////////
/** Check to get access for to viewed file
@return bool - false access was denied
*/
bool cImageControl::CheckAccess() const
{
  const char *szFileName = FileName();

  if(szFileName 
    && 0 == access(szFileName,F_OK))
  {
    return true;
  }        

  return false;
}


void cImageControl::OriginalImage(bool bCached)
{
  if(!CheckAccess())
  {
    esyslog("picselshow: Operation failed");
  }
}



void cImageControl::NextImage(int Step)
{
//  m_bSlideShowActiv = m_bSlideShowBefore;
  theSlideShow.NextImage(Step);
  OriginalImage(true);
//  m_tStarted = time(NULL);
}



void cImageControl::PrevImage(int Step)
{
//  m_bSlideShowActiv = m_bSlideShowBefore;
  theSlideShow.PrevImage(Step);
  OriginalImage(true);
//  m_tStarted = time(NULL);
}


void cImageControl::GotoImage(unsigned int Pict)
{
//  m_bSlideShowActiv = m_bSlideShowBefore;
  theSlideShow.GotoImage(Pict);
  OriginalImage(true);
//  m_tStarted = time(NULL);
}


int cImageControl::ImageTotal(void) const
{
  return theSlideShow.ImageTotal();
}


int cImageControl::ImageCurrent(void) const
{
  return theSlideShow.ImageCurrent();
}


const char* cImageControl::FileName(void) const
{
  std::string datei;
  datei = PluginConfig_Path().c_str();
  datei = datei + "/data/default.png";

  if(ImageTotal() > 0) {
    cImage* pImage = theSlideShow.GetImage();
    return pImage?pImage->Name():NULL;
    }
  else {
    return datei.c_str();
  }
}


void cImageControl::SlideImage()
{
  if( m_bSlideShowActiv && ImageTotal() >0 )
  {
    if(ImageSetup.m_nSSsec <= 
        (time(NULL) - m_tStarted))
    {
      CanLoadCover = true;

      if(ImageSetup.EnableFade) FadeOut();
      NextImage(1);
      Show();
    }
  }

}


void cImageControl::PlayMusic()
{
  std::string plugindir;
  plugindir = ImageSetup.MusicDir;
  plugindir = plugindir + "/";

  char *buffer;
    
  ServiceID="Music-Play-v1";
  ServiceAvailable=cPluginManager::CallFirstService(*ServiceID, NULL);

  if(!ServiceAvailable) {
    esyslog("picselshow: ERROR: could not reach Music-Play-v1");
    }
  else {

    MP3ServiceData ServiceData;
    asprintf(&buffer, "%s%s",plugindir.c_str(), "Slideshow.m3u" );
    ServiceData.data.filename=buffer;

    if(cPluginManager::CallFirstService(ServiceID, &ServiceData)) {
      if(!ServiceData.result) {
        esyslog("picselshow: ERROR: couldn't load playlist !");
      }
    }

    free(buffer);
    buffer=0;
  }

}



void cImageControl::IncSlideTime(void)
{
  if(ImageSetup.m_nSSsec < cImageSetup::m_cSSMax) {
	    ImageSetup.m_nSSsec++;
	}
}


void cImageControl::DecSlideTime(void)
{
  if(ImageSetup.m_nSSsec > cImageSetup::m_cSSMin) {
	    ImageSetup.m_nSSsec--;
	}
}
