/*
Copyright (C) 2006-2008 Christian Wieninger

This program 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 program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Or, point your browser to http://www.gnu.org/licenses/old-licenses/gpl-2.0.html

The author can be reached at cwieninger@gmx.de

The project's page is at http://winni.vdr-developer.org/noepgmenu
*/

#include <vector>
#include <map>
#include <string>
#include <vdr/plugin.h>
#include <vdr/device.h>
#include <vdr/i18n.h>
#include "i18n.h"

static const char *VERSION        = "0.0.5";
static const char *DESCRIPTION    = trNOOP("a menu for noEPG patch");
static const char *MAINMENUENTRY  = trNOOP("noEPG channels");

#define CHNUMWIDTH  (numdigits(Channels.MaxNumber()) + 1)

typedef enum { EPGfromProvider, noEPGfromProvider, EPGModeToggle } EPGMode;

// --- cConfigNoEPG ------------------------------------------------------
class cConfigNoEPG
{
  std::map<const cChannel*, EPGMode> channelModes;
public:
  int hidemenu;
 cConfigNoEPG(void) : hidemenu(0) {}
  
  void InitChannelSettings()
  {
    for (const cChannel *ch = Channels.First(); ch; ch = Channels.Next(ch)) 
      channelModes[ch] = allowedEPG(ch->GetChannelID()) ? EPGfromProvider:noEPGfromProvider;
  }

  bool allowedEPG(tChannelID chID) 
  {
    if (::Setup.noEPGMode == 1) 
      return strstr(::Setup.noEPGList,chID.ToString())!=NULL;
    else 
      return strstr(::Setup.noEPGList,chID.ToString())==NULL;
  }

  EPGMode GetChannelMode(const cChannel* channel)
  {
    std::map<const cChannel*, EPGMode>::iterator i = channelModes.find(channel);
    if (i != channelModes.end()) 
      return i->second;
    else
      return EPGfromProvider;
  }
  void SetChannelMode(const cChannel* channel, EPGMode mode)
  {
    channelModes[channel] = mode;
    std::string chList;
    for (const cChannel *ch = Channels.First(); ch; ch = Channels.Next(ch))
      if (!ch->GroupSep() && GetChannelMode(ch) == noEPGfromProvider)
	chList += std::string(chList.empty()?"":" ") + *ch->GetChannelID().ToString();
    if (::Setup.noEPGList) free(::Setup.noEPGList);
    ::Setup.noEPGList = strdup(chList.c_str());
    ::Setup.Save();
  }
};
cConfigNoEPG ConfigNoEPG;


// --- cMenuChannelItemNoEPG ----------------------------------------------
class cMenuChannelItemNoEPG : public cOsdItem {
private:
  const cChannel *channel;
public:
 cMenuChannelItemNoEPG(const cChannel *Channel) : channel(Channel) { ConfigNoEPG.InitChannelSettings(); Set(); }
  void Set();
  virtual int Compare(const cListObject &ListObject) const;
  const cChannel *Channel(void) { return channel; }
  };

void cMenuChannelItemNoEPG::Set()
{
  char *buffer = NULL;
  if (!channel->GroupSep()) 
    {
      asprintf(&buffer, "%d\t%s\t%s", channel->Number(), channel->Name(), 
	       ConfigNoEPG.GetChannelMode(channel) == EPGfromProvider ? tr("EPG"):tr("no EPG"));
    }
  else
     asprintf(&buffer, "---\t%s ----------------------------------------------------------------", channel->Name());
  SetText(buffer, false);
}

int cMenuChannelItemNoEPG::Compare(const cListObject &ListObject) const
{
  cMenuChannelItemNoEPG *p = (cMenuChannelItemNoEPG *)&ListObject;
  return channel->Number() - p->channel->Number();
}

// --- cMenuChannelsNoEPG ---------------------------------------------------------
class cMenuChannelsNoEPG : public cOsdMenu {
private:
  void Set(void);
  const cChannel *GetChannel(int Index);
  cChannel mainGroup;
protected:
  eOSState SetEPG(int index, EPGMode mode);
public:
  cMenuChannelsNoEPG(void);
  virtual eOSState ProcessKey(eKeys Key);
  };

cMenuChannelsNoEPG::cMenuChannelsNoEPG(void)
:cOsdMenu(tr("noEPG channels"), CHNUMWIDTH, 30)
{
  // create a main channel group
  mainGroup.Parse(tr(":Main channels"));
  Set();
  SetHelp(tr("Button$no EPG"), tr("Button$EPG"), tr("Button$Toggle"), NULL);
}

void cMenuChannelsNoEPG::Set(void)
{
  const cChannel *currentChannel = GetChannel(Current());
  Clear();
  if (Channels.First() && !Channels.First()->GroupSep()) // add a main group if there's none
    Add(new cMenuChannelItemNoEPG(&mainGroup));
  for (const cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
    if ( *channel->Name())
      Add(new cMenuChannelItemNoEPG(channel), channel == currentChannel);
}

const cChannel *cMenuChannelsNoEPG::GetChannel(int Index)
{
  cMenuChannelItemNoEPG *p = (cMenuChannelItemNoEPG *)Get(Index);
  return p ? p->Channel() : NULL;
}

eOSState cMenuChannelsNoEPG::SetEPG(int index, EPGMode mode)
{
  EPGMode newMode = mode;
  const cChannel* channel = GetChannel(index);
  if (!channel) return osContinue;
  std::vector<const cChannel*> channelList;
  bool singleUpdate = true;
  if (!channel->GroupSep())
    channelList.push_back(channel);
  else
    {
      singleUpdate = false;
      for(int c = index+1; c < Count()-1; c++)
	{
	  const cChannel* ch = GetChannel(c);
	  if (ch->GroupSep()) break;
	  channelList.push_back(ch);
	}
    }
  for(std::vector<const cChannel*>::iterator i = channelList.begin(); i != channelList.end(); ++i)
    {
      if (mode == EPGModeToggle)
	newMode = ConfigNoEPG.GetChannelMode(*i) == EPGfromProvider? noEPGfromProvider:EPGfromProvider;
      ConfigNoEPG.SetChannelMode(*i, newMode);
    }
  if (!singleUpdate)
    Set();
  else
    RefreshCurrent();
  Display();
  return osContinue;
}

eOSState cMenuChannelsNoEPG::ProcessKey(eKeys Key)
{
  eOSState state = cOsdMenu::ProcessKey(Key);

  switch (state) {
  default:
    if (state == osUnknown) {
      switch (Key) {
      case kOk:
      case kYellow:
	state = SetEPG(Current(), EPGModeToggle);
	break;
      case kRed:
      case kGreen:
	state = SetEPG(Current(), Key == kGreen?EPGfromProvider:noEPGfromProvider);
	break;
      default: break;
      }
    }
  }
  return state;
}

class cMenuSetupNoEPG : public cMenuSetupPage {
  private:
    virtual void Setup(void);
    cConfigNoEPG data;
  protected:
    virtual eOSState ProcessKey(eKeys Key);
    virtual void Store(void);
    void Set(void);
  public:
    cMenuSetupNoEPG(void);
};

cMenuSetupNoEPG::cMenuSetupNoEPG(void)
{
  Setup();
}

void cMenuSetupNoEPG::Setup(void)
{
  data = ConfigNoEPG;
  Set();
}

void cMenuSetupNoEPG::Set()
{
  int current = Current();
  Clear();
  Add(new cMenuEditBoolItem(tr("Hide main menu entry"),         &data.hidemenu,       trVDR("no"),      trVDR("yes")));
  SetCurrent(Get(current));
  Display();
}

void cMenuSetupNoEPG::Store(void)
{
    ConfigNoEPG = data;

    SetupStore("HideMenu",  ConfigNoEPG.hidemenu);
}

eOSState cMenuSetupNoEPG::ProcessKey(eKeys Key)
{
    return cMenuSetupPage::ProcessKey(Key);
}

class cPluginNoepgmenu : public cPlugin {
private:
public:
  cPluginNoepgmenu(void);
  virtual ~cPluginNoepgmenu();
  virtual const char *Version(void) { return VERSION; }
  virtual const char *Description(void) { return tr(DESCRIPTION); }
  virtual const char *CommandLineHelp(void);
  virtual bool ProcessArgs(int argc, char *argv[]);
  virtual bool Initialize(void);
  virtual bool Start(void);
  virtual void Stop(void);
  virtual void Housekeeping(void);
  virtual const char *MainMenuEntry(void) { 
    return (ConfigNoEPG.hidemenu ? NULL : tr(MAINMENUENTRY)); 
  }
  virtual cOsdObject *MainMenuAction(void);
  virtual cMenuSetupPage *SetupMenu(void);
  virtual bool SetupParse(const char *Name, const char *Value);
  virtual bool Service(const char *Id, void *Data = NULL);
  virtual const char **SVDRPHelpPages(void);
  virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
  };

cPluginNoepgmenu::cPluginNoepgmenu(void)
{
}

cPluginNoepgmenu::~cPluginNoepgmenu()
{
}

const char *cPluginNoepgmenu::CommandLineHelp(void)
{
  return NULL;
}

bool cPluginNoepgmenu::ProcessArgs(int argc, char *argv[])
{
  return true;
}

bool cPluginNoepgmenu::Initialize(void)
{
#if APIVERSNUM < 10507
  RegisterI18n(Phrases);
#endif
  return true;
}

bool cPluginNoepgmenu::Start(void)
{
  return true;
}

void cPluginNoepgmenu::Stop(void)
{
}

void cPluginNoepgmenu::Housekeeping(void)
{
}

cOsdObject *cPluginNoepgmenu::MainMenuAction(void)
{
    return new cMenuChannelsNoEPG;
}

cMenuSetupPage *cPluginNoepgmenu::SetupMenu(void)
{
  return new cMenuSetupNoEPG;
}

bool cPluginNoepgmenu::SetupParse(const char *Name, const char *Value)
{
    if      (!strcasecmp(Name, "HideMenu")) ConfigNoEPG.hidemenu = atoi(Value);
    return true;
}

bool cPluginNoepgmenu::Service(const char *Id, void *Data)
{
  return false;
}

const char **cPluginNoepgmenu::SVDRPHelpPages(void)
{
  return NULL;
}

cString cPluginNoepgmenu::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
{
  return NULL;
}

VDRPLUGINCREATOR(cPluginNoepgmenu); // Don't touch this!
