/*
 * svdrpservice.c: generic service providing access to a remote SVDRP server
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id$
 */

#include <vdr/plugin.h>
#include <vdr/thread.h>
#include <vdr/tools.h>
#include <vdr/i18n.h>

#include "svdrpservice.h"
#include "connection.h"
#include "setup.h"

#define MAX_SVDRP_CONNECTIONS 8

static const char *VERSION        = "1.0.0";
static const char *DESCRIPTION    = "SVDRP client";

class cPluginSvdrpService : public cPlugin {
private:
  cMutex connsMutex;
  cSvdrpConnection* connections[MAX_SVDRP_CONNECTIONS];
  int FindSharedConnection(const char *ServerIp, unsigned int ServerPort) const;
  int AddConnection(const char *ServerIp, unsigned int ServerPort, bool Shared);
public:
  cPluginSvdrpService(void);
  virtual ~cPluginSvdrpService();
  virtual const char *Version(void) { return VERSION; }
  virtual const char *Description(void) { return 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 NULL; }
  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);
  };

cPluginSvdrpService::cPluginSvdrpService(void)
{
	for (int i = 0; i < MAX_SVDRP_CONNECTIONS; i++)
		connections[i] = NULL;
}

cPluginSvdrpService::~cPluginSvdrpService()
{
	for (int i = 0; i < MAX_SVDRP_CONNECTIONS; i++) {
		DELETENULL(connections[i]);
	}
}

const char *cPluginSvdrpService::CommandLineHelp(void)
{
	return "  IP[:PORT]  Default server IP and optional port (e.g. 192.0.2.1:6419).\n"
	       "             If no port is given, the default SVDRP port 6419 is asumed.\n";
}

bool cPluginSvdrpService::ProcessArgs(int argc, char *argv[])
{
	for (int i = 1; i < argc; i++)
	{
  		if (*argv[i] == '-')
		{
			fprintf(stderr, "Plugin %s: unknown commandline argument %s\n", Name(), argv[i]);
			return false;
		}
		cSvdrpServiceSetup::opt_serverIp = argv[i];
		char *p = strchr(argv[i], ':');
		if (p)
		{
			*p = 0;
			cSvdrpServiceSetup::opt_serverPort = ++p;
		}
	}
	return true;
}

bool cPluginSvdrpService::Initialize(void)
{
  return true;
}

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

void cPluginSvdrpService::Stop(void)
{
}

void cPluginSvdrpService::Housekeeping(void)
{
}

cOsdObject *cPluginSvdrpService::MainMenuAction(void)
{
  return NULL;
}

cMenuSetupPage *cPluginSvdrpService::SetupMenu(void)
{
  return new cSvdrpServiceMenuSetup();;
}

bool cPluginSvdrpService::SetupParse(const char *Name, const char *Value)
{
  return SvdrpServiceSetup.Parse(Name, Value);
}

bool cPluginSvdrpService::Service(const char *Id, void *Data)
{
  // Handle custom service requests from other plugins
  if (strcmp(Id, "SvdrpConnection-v1.0") == 0) {
	  if (Data) {
		  SvdrpConnection_v1_0 *conn = (SvdrpConnection_v1_0 *) Data;
		  connsMutex.Lock();
		  if (conn->handle < 0) {
			  if (conn->serverIp[0] == 0 || strcmp(conn->serverIp, "0.0.0.0") == 0)
				  conn->serverIp = SvdrpServiceSetup.serverIp;
			  if (conn->serverPort == 0)
				  conn->serverPort = SvdrpServiceSetup.serverPort;
			  if (conn->shared)
				  conn->handle = FindSharedConnection(conn->serverIp, conn->serverPort);
			  if (conn->handle < 0)
				  conn->handle = AddConnection(conn->serverIp, conn->serverPort, conn->shared);
			  if (conn->handle >= 0) {
				  connections[conn->handle]->Lock();
				  bool IsOpen = connections[conn->handle]->Open();
				  connections[conn->handle]->Unlock();
				  if (IsOpen)
					  connections[conn->handle]->AddRef();
				  else
					  conn->handle = -1;
			  }
		  }
		  else if (conn->handle < MAX_SVDRP_CONNECTIONS && connections[conn->handle]) {
			  if (connections[conn->handle]->DelRef() == 0)
				  DELETENULL(connections[conn->handle]);
			  conn->handle = -1;
		  }
		  else {
			  esyslog("svdrpservice: SvdrpConnection: Invalid handle %d", conn->handle);
		  }
		  connsMutex.Unlock();
	  }
	  return true;
  }
  if (strcmp(Id, "SvdrpCommand-v1.0") == 0) {
	  if (Data) {
		  SvdrpCommand_v1_0 *cmd = (SvdrpCommand_v1_0 *) Data;
		  cmd->reply.Clear();
		  cmd->responseCode = 0;
		  if (cmd->handle >= 0 && cmd->handle < MAX_SVDRP_CONNECTIONS && connections[cmd->handle]) {
			  connections[cmd->handle]->Lock();
			  if (connections[cmd->handle]->Send(cmd->command))
				  cmd->responseCode = connections[cmd->handle]->Receive(&cmd->reply);
			  connections[cmd->handle]->Unlock();
		  }
		  else {
			  esyslog("svdrpservice: SvdrpCommand: Invalid handle %d", cmd->handle);
		  }
	  }
	  return true;
  }
  return false;
}

const char **cPluginSvdrpService::SVDRPHelpPages(void)
{
  // Return help text for SVDRP commands this plugin implements
  return NULL;
}

cString cPluginSvdrpService::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
{
  // Process SVDRP commands this plugin implements
  return NULL;
}

int cPluginSvdrpService::FindSharedConnection(const char *ServerIp, unsigned int ServerPort) const {
  for (int i = 0; i < MAX_SVDRP_CONNECTIONS; i++) {
	  if (connections[i] && connections[i]->IsShared() &&
			  connections[i]->HasDestination(ServerIp, ServerPort))
		  return i;
  }
  return -1;
}

int cPluginSvdrpService::AddConnection(const char *ServerIp, unsigned int ServerPort, bool Shared) {
  for (int i = 0; i < MAX_SVDRP_CONNECTIONS; i++) {
	  if (connections[i] == NULL) {
		  connections[i] = new cSvdrpConnection(ServerIp, ServerPort, Shared);
		  return i;
	  }
  }
  esyslog("svdrpservice: Too many open connections");
  return -1;
}

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