/*
 * advchctrl.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id$
 */

//#include <ctype.h>
//#include <errno.h>
//#include <fcntl.h>
//#include <netinet/in.h>
//#include <stdarg.h>
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
//#include <sys/socket.h>
//#include <sys/time.h>
//#include <unistd.h>
//#include "channels.h"
//#include "config.h"
//#include "cutter.h"
//#include "device.h"
//#include "eitscan.h"
//#include "keys.h"
//#include "menu.h"
//#include "plugin.h"
//#include "remote.h"
//#include "timers.h"
//#include "tools.h"
//#include "videodir.h"
#include <vdr/plugin.h>
#include <vdr/status.h>
#include <vdr/device.h>
#include "advchctrlconfig.h"
#include "advchctrl.h"
#include "advchctrlchannel.h"
#include "advchctrlsetup.h"
#include "advchctrlosd.h"
#include "advchctrlcommon.h"
#include "advchctrli18n.h"


//***** The cStatus Interface Class Implementation ****************************************************************************
//extern cAdvChCtrlChannels   AdvChCtrlChannelsList;

void cStatusAdvChCtrl::ChannelSwitch (const cDevice *Device, int ChannelNumber)
{
    
    isInReplayMode = Device->PrimaryDevice()->Replaying();	// ok in Transfermode we don't want to change the Volume
    isMute = Device->PrimaryDevice()->IsMute();    
    DEBUGVOL ("cStatusAdvChCtrl::Channelswitch Device <%d> Channel <%d>,isPrimaryDevice <%s>,isReplaying <%s>, isMuted<%s> ", Device->CardIndex(), ChannelNumber, Device->IsPrimaryDevice() ? "yes": "no", Device->Replaying() ? "yes": "no", Device->IsMute() ? "yes":"no");
    // find the actual current channel
    cChannel *Channel = Channels.GetByNumber(Device->PrimaryDevice()->CurrentChannel());
    if (Channel && !(ChannelNumber == 0) && Device->IsPrimaryDevice()) //&& !(isMute)) 
    {
	tChannelID mycurrent = Channel->GetChannelID();
	cDevice *myDevice = Device->PrimaryDevice();
        int ChVolume = AdvChCtrlChannelsList.GetVolume(mycurrent);
	DEBUGVOL ("cStatusAdvChCtrl::Channelswitch Channel-ID <%s> ChVolume <%d>" , *(mycurrent.ToString()), ChVolume );
	if (ChVolume > 0)
	{
	    if (!isInReplayMode)
	    {
		myDevice->SetVolume(ChVolume, 1);
		if (isMute) 
		{
		    myDevice->ToggleMute();
		}
		DEBUGVOL ("cStatusAdvChCtrl::Channelswitch SetVolume"  );
            }
	    ActualVolume = ChVolume;
	}
	if (ChVolume < 0)
	{
	    if (!isInReplayMode)
	    {
		myDevice->SetVolume(advchctrlConfig.defaultvolume, 1);
		DEBUGVOL ("cStatusAdvChCtrl::Channelswitch SetDefaultVolume"  );
		if (isMute) 
		{
		    myDevice->ToggleMute();
		}
	    }
	    ActualVolume = advchctrlConfig.defaultvolume;
	}
    }
}

void cStatusAdvChCtrl::SetVolume (int Volume, bool Absolute)
{
    // DEBUGVOL ("cStatusAdvChCtrl::SetVolume <%d> <%d>", Volume, Absolute);
    // here we need the current channel of the primary device
    cDevice  *PrimaryDev = cDevice::PrimaryDevice();
    if (PrimaryDev)
    {
	cChannel *Channel = Channels.GetByNumber(PrimaryDev->CurrentChannel());
	if (Channel) 
	{   // now we get the unique ChannelID
	    tChannelID mycurrent = Channel->GetChannelID();
	    // we call the channel class for the actual channel volume, but not when in Replaymode
	    if (!isInReplayMode)
	    {
		int ChVolume = AdvChCtrlChannelsList.GetVolume(mycurrent);
		if (!PrimaryDev->IsMute())
		{
		    int SetVolume = isMute ? ChVolume : Volume; 
    		    AdvChCtrlChannelsList.AddAVolCtrlChannel(mycurrent, SetVolume, advchctrlConfig.defaultvolume);
		    if (isMute)
		    {
			isMute = false;
		    
			PrimaryDev->SetVolume(SetVolume, 1);
    			DEBUGVOL ("cStatusAdvChCtrl::SetVolume Channel-ID <%s>, <%d>" , *(mycurrent.ToString()),SetVolume);
		    }
		} else {
		    // DEBUGVOL ("cStatusAdvChCtrl::SetVolume Channel-ID <%s>, <%d> isMute" , *(mycurrent.ToString()),ChVolume);
    		    // cDevice::PrimaryDevice()->SetVolume(ChVolume, 1);
		    isMute = true;
		}
	    }
	}
    }    
}

void cStatusAdvChCtrl::Replaying(const cControl *Control, const char *Name)
{
    isInReplayMode = (Name != NULL ? true : false);
    DEBUGVOL ("cStatusAdvChCtrl::Replaying <%s> <%s>", Name, (isInReplayMode ? "yes" : "no"));
    if (!(ActualVolume == advchctrlConfig.defaultvolume) && isInReplayMode)
    {
        cDevice *myPrimary = cDevice::PrimaryDevice();
	// Reset Volume to Default volume on start of replay
	myPrimary->SetVolume(advchctrlConfig.defaultvolume, 1);
	//cDevice::PrimaryDevice()->SetVolume(advchctrlConfig.defaultvolume, 1);
	DEBUGVOL ("cStatusAdvChCtrl::Channelswitch SetDefaultVolume on Replaystart"  );
	ActualVolume = advchctrlConfig.defaultvolume;
    }
}

//***** The cPlugin Interface Class Implementation **************************************************************
cPluginAdvChCtrl::cPluginAdvChCtrl(void)
{
  // Initialize any member variables here.
  // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
  // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
  statusAdvChCtrl = NULL;
  
  advchctrlConfig.defaultvolume = 100;
  advchctrlConfig.hidemainmenu = 1;
  advchctrlConfig.osdposition = 0;
  advchctrlConfig.osdwidth = 600;
  advchctrlConfig.osdheight = 400;
  advchctrlConfig.osdoffset = 0;
  advchctrlConfig.theme = eThemeClassic;
}

cPluginAdvChCtrl::~cPluginAdvChCtrl()
{
  // Cleanup after yourself!
  delete statusAdvChCtrl;
}

const char *cPluginAdvChCtrl::CommandLineHelp(void)
{
  // Return a string that describes all known command line options.
  return NULL;
}

bool cPluginAdvChCtrl::ProcessArgs(int argc, char *argv[])
{
  // Implement command line argument processing here if applicable.
  return true;
}

bool cPluginAdvChCtrl::Initialize(void)
{
  // Initialize any background activities the plugin shall perform.
  RegisterI18n(Phrases);	// we want support other languages
  
  return true;
}

bool cPluginAdvChCtrl::Start(void)
{
  // Start any background activities the plugin shall perform.
  statusAdvChCtrl = new cStatusAdvChCtrl;
  if (!AdvChCtrlChannelsList.Load(AddDirectory(ConfigDirectory(),"AdvChCtrl.conf")))
  {
     isyslog ("AdvChCtrl: Loading 'AdvChCtrl.conf' faild");
  }
  AdvChCtrlChannelsList.Save();
  return true;
}

void cPluginAdvChCtrl::Stop(void)
{
  // Stop any background activities the plugin shall perform.
}

void cPluginAdvChCtrl::Housekeeping(void)
{
  // Perform any cleanup or other regular tasks.
}

cOsdObject *cPluginAdvChCtrl::MainMenuAction(void)
{
  // Perform the action when selected from the main VDR menu.
  return new cAdvChCtrlOsd;
}

cMenuSetupPage *cPluginAdvChCtrl::SetupMenu(void)
{
  // Return a setup menu in case the plugin supports one.
  return new cAdvChCtrlSetup;	
}

bool cPluginAdvChCtrl::SetupParse(const char *Name, const char *Value)
{
  // Parse your own setup parameters and store their values.
  if (!strcasecmp(Name, "DefaultVolume"))       advchctrlConfig.defaultvolume       = atoi(Value);
  else if (!strcasecmp (Name, "HideMainMenu"))  advchctrlConfig.hidemainmenu        = atoi(Value);
  else if (!strcasecmp(Name, "OSDPosition"))    advchctrlConfig.osdposition         = atoi(Value);
  else if (!strcasecmp(Name, "OSDHeight"))      advchctrlConfig.osdheight           = atoi(Value);
  else if (!strcasecmp(Name, "OSDWidth"))       advchctrlConfig.osdwidth            = atoi(Value);
  else if (!strcasecmp(Name, "OSDOffset"))      advchctrlConfig.osdoffset           = atoi(Value);
  else if (!strcasecmp(Name, "Theme"))          advchctrlConfig.theme               = atoi(Value);

//  else if (!strcasecmp(Name, "RedLimit"))       advchctrlConfig.redlimit       = atoi(Value);
//  else if (!strcasecmp(Name, "GreenLimit"))     advchctrlConfig.greenlimit     = atoi(Value);
//  else if (!strcasecmp(Name, "UpdateInterval")) advchctrlConfig.updateinterval = atoi(Value);
  else     return false;
  if (advchctrlConfig.theme < 0 || advchctrlConfig.theme >= eThemeMaxNumber) advchctrlConfig.theme = eThemeClassic;
  return true;
}


bool cPluginAdvChCtrl::Service(const char *Id, void *Data)
{
  // Handle custom service requests from other plugins
  return false;
}


const char **cPluginAdvChCtrl::SVDRPHelpPages(void)
{
  static const char *HelpPages[] = {
    "PVOLU [<channelnr>]\n"
    "    Prints out channel volume for <channelnr>,\n"
    "    and default channel volume, when called without parameter.\n",
    "RVOLU [<channelnr>]\n"
    "       Resets the channel volume to the default channel volume \n"
    "       for <channel>. channel can be a valid channel number, \n"
    "       or 0 to reset the actual channel volume.\n",
    "SVOLU <channelnr>,<value>\n"
    "       Set the default channel volume to <value>.\n"
    "       range vor <value> from 0 to 255\n",
    NULL
    };
  return HelpPages;
}

cString cPluginAdvChCtrl::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
{
    if (strcasecmp(Command, "PVOLU") == 0) return SVDRPCmdPvolu(Option, ReplyCode);
    if (strcasecmp(Command, "RVOLU") == 0) return SVDRPCmdRvolu(Option, ReplyCode);
    if (strcasecmp(Command, "SVOLU") == 0) return SVDRPCmdSvolu(Option, ReplyCode);
    ReplyCode = 500;
    return cString::sprintf("Unknown command: \"%s\"", Command);
}

cString cPluginAdvChCtrl::SVDRPCmdPvolu(const char *Option, int &ReplyCode)
{
    ReplyCode = 900;
    if (*Option) 
    {
    	cChannel *Channel = NULL;
	if (isnumber(Option))
	{
    	    int myChannelNr = strtol(Option, NULL, 10);
            Channel = Channels.GetByNumber((0 == myChannelNr) ?  cDevice::PrimaryDevice()->CurrentChannel(): myChannelNr);
	} 
	//DEBUGSVDRP("Option:<%s>  myChannelNr=%d\n", Option, myChannelNr);
	if (Channel)
	{
	    tChannelID myCurrentID = Channel->GetChannelID();
	    int ChVolume = AdvChCtrlChannelsList.GetVolume(myCurrentID);
	    DEBUGSVDRP ("cStatusAdvChCtrl::SVDRPCmdPvolu Channel-ID <%s> ChVolume <%d>" , *(myCurrentID.ToString()), ChVolume );
   	    if (ChVolume < 0) 
	    {
		ReplyCode = 901;
		return cString::sprintf("%d ;%s\n", advchctrlConfig.defaultvolume, "Channel has default volume");
	    }
	    return cString::sprintf("%d \n", ChVolume );
	} 
	ReplyCode = 502;
	return cString::sprintf ("Unknown channel number <%s>\n", Option);
    } 
    ReplyCode = 901;
    return cString::sprintf("%d ;%s\n", advchctrlConfig.defaultvolume, "this is the default channel volume");
}

cString cPluginAdvChCtrl::SVDRPCmdRvolu(const char *Option, int &ReplyCode)
{
    ReplyCode = 900;
    if (*Option) 
    {
    	cChannel *Channel = NULL;
	if (isnumber(Option))
	{
    	    int myChannelNr = strtol(Option, NULL, 10);
            Channel = Channels.GetByNumber((0 == myChannelNr) ?  cDevice::PrimaryDevice()->CurrentChannel(): myChannelNr);
	} else
    	    Channel = Channels.GetByChannelID(tChannelID::FromString(Option));
	// DEBUGSVDRP("Option:<%s>  myChannelNr=%d\n", Option, myChannelNr);
	if (Channel)
	{
	    tChannelID myCurrentID = Channel->GetChannelID();
	    AdvChCtrlChannelsList.AddAVolCtrlChannel(myCurrentID, advchctrlConfig.defaultvolume, advchctrlConfig.defaultvolume);
	    //AdvChCtrlChannelsList.Save();
	    if (! cDevice::PrimaryDevice()->Replaying())
	    {
    		bool isMute = cDevice::PrimaryDevice()->IsMute();
		cDevice::PrimaryDevice()->SetVolume(advchctrlConfig.defaultvolume,1);
		if (isMute) cDevice::PrimaryDevice()->ToggleMute();	    
	    }
	    DEBUGSVDRP ("cStatusAdvChCtrl::SVDRPCmdPvolu Channel-ID <%s> reset to default volume" , *(myCurrentID.ToString()));
	    ReplyCode = 901;
	    return cString::sprintf("%d ;%s\n", advchctrlConfig.defaultvolume, "Channel is reset to default volume");
	} 
	ReplyCode = 502;
	return cString::sprintf ("Unknown channel number <%s>\n", Option);        //return cString::sprintf("%ld\nThis is the number of seconds since the epoch\nand a demo of a multi-line reply", time(NULL));
    }
    ReplyCode = 502;
    return cString::sprintf("RVOLU Not yet implemented \n");
}

cString cPluginAdvChCtrl::SVDRPCmdSvolu(const char *Option, int &ReplyCode)
{
    ReplyCode = 900;
    if (*Option) 
    {
        //return cString::sprintf("%ld\nThis is the number of seconds since the epoch\nand a demo of a multi-line reply", time(NULL));
	char *strChannel = NULL;
	char buf[strlen(Option) + 1];
	char *p = strcpy(buf, Option);
	const char *delim = " \t";
	char *strtok_next;
        strChannel = strtok_r(p, delim, &strtok_next);
        DEBUGSVDRP ("cStatusAdvChCtrl::SVDRPCmdSvolu Channel-ID <%s> isnumber():%s" , strChannel,(isnumber(strChannel)? "yes":"no"));
	
    	cChannel *Channel = NULL;
	if (isnumber(strChannel))
	{
    	    int myChannelNr = strtol(strChannel, NULL, 10);
	    if (myChannelNr < 0) 
	    {
		ReplyCode = 501;
		return cString::sprintf("Illegal channel \"%d\"", myChannelNr);
	    }
            Channel = Channels.GetByNumber((0 == myChannelNr) ?  cDevice::PrimaryDevice()->CurrentChannel(): myChannelNr);
    	    if (Channel)
    	    {
		if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
		    if (isnumber(p)){
			int ChVolume = atoi(p);
	    		if (ChVolume >= 0 && ChVolume < 256)
			{
			    DEBUGSVDRP ("cStatusAdvChCtrl::SVDRPCmdSvolu Channel-ID <%s> <%d>" , strChannel, ChVolume);
	    		    AdvChCtrlChannelsList.AddAVolCtrlChannel(Channel->GetChannelID(), ChVolume, advchctrlConfig.defaultvolume);
			    if (! cDevice::PrimaryDevice()->Replaying())
			    {
				bool isMute = cDevice::PrimaryDevice()->IsMute();
				cDevice::PrimaryDevice()->SetVolume(ChVolume,1);
    				if (isMute) cDevice::PrimaryDevice()->ToggleMute();
			    }
			    ReplyCode = 900;
			    return cString::sprintf("Set Volume to %d for channel %d", ChVolume, myChannelNr);
			}
		    } 
		    ReplyCode = 501;
		    return cString::sprintf("Illegal volume \"%s\"", p);
    		}
    		if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
		    ReplyCode = 501;
    		    return cString::sprintf("Unexpected parameter \"%s\"", p);
    		}    
	    } 	
	}
	//DEBUGSVDRP("Option:<%s>  myChannelNr=%d\n", Option, myChannelNr);
	ReplyCode = 501;
	return cString::sprintf("Illegal channel \"%s\"", strChannel);
    }	
    ReplyCode = 501;
    return cString::sprintf("Parameters missing\n");
}

sAdvChCtrlConfig advchctrlConfig;

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