#!/usr/bin/perl -w

# newplugin: Initializing a new plugin source directory
#
# Creates a new plugin source directory from which to start implementing
# a plugin for VDR.
# See the file PLUGINS.html for detailed instructions on how to
# write a plugin.
#
# Usage: newplugin <name>
#
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
# $Id: newplugin 1.36 2007/08/19 14:19:49 kls Exp $

$PLUGIN_NAME = $ARGV[0] || die "Usage: newplugin <name>\n";

die "Please use only lowercase letters and digits in the plugin name\n" if ($PLUGIN_NAME =~ tr/a-z0-9//c);

$PLUGIN_CLASS   = ucfirst($PLUGIN_NAME);

$PLUGIN_VERSION = "0.0.1";
$PLUGIN_DESCRIPTION = "Enter description for '$PLUGIN_NAME' plugin";
$PLUGIN_MAINENTRY = $PLUGIN_CLASS;

$PLUGINS_SRC = "PLUGINS/src";

$README = qq
{This is a "plugin" for the Video Disk Recorder (VDR).

Written by:                  Your Name <email\@host.dom>

Project's homepage:          URL

Latest version available at: URL

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.
See the file COPYING for more information.

Description:
};

$HISTORY_TITLE = "VDR Plugin '$PLUGIN_NAME' Revision History";
$HISTORY_LINE = '-' x length($HISTORY_TITLE);
$HISTORY_DATE = sprintf("%4d-%02d-%02d", (localtime)[5] + 1900, (localtime)[4] + 1, (localtime)[3]);
$HISTORY = qq
{$HISTORY_TITLE
$HISTORY_LINE

$HISTORY_DATE: Version $PLUGIN_VERSION

- Initial revision.
};

$MAKEFILE = qq
{#
# Makefile for a Video Disk Recorder plugin
#
# \$Id\$

# The official name of this plugin.
# This name will be used in the '-P...' option of VDR to load the plugin.
# By default the main source file also carries this name.
# IMPORTANT: the presence of this macro is important for the Make.config
# file. So it must be defined, even if it is not used here!
#
PLUGIN = $PLUGIN_NAME

### The version number of this plugin (taken from the main source file):

VERSION = \$(shell grep 'static const char \\*VERSION *=' \$(PLUGIN).c | awk '{ print \$\$6 }' | sed -e 's/[";]//g')

### The C++ compiler and options:

CXX      ?= g++
CXXFLAGS ?= -fPIC -g -O2 -Wall -Woverloaded-virtual

### The directory environment:

VDRDIR = ../../..
LIBDIR = ../../lib
TMPDIR = /tmp

### Allow user defined options to overwrite defaults:

-include \$(VDRDIR)/Make.config

### The version number of VDR's plugin API (taken from VDR's "config.h"):

APIVERSION = \$(shell sed -ne '/define APIVERSION/s/^.*"\\(.*\\)".*\$\$/\\1/p' \$(VDRDIR)/config.h)

### The name of the distribution archive:

ARCHIVE = \$(PLUGIN)-\$(VERSION)
PACKAGE = vdr-\$(ARCHIVE)

### Includes and Defines (add further entries here):

INCLUDES += -I\$(VDRDIR)/include

DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"\$(PLUGIN)"'

### The object files (add further files here):

OBJS = \$(PLUGIN).o ${PLUGIN_NAME}_config.o ${PLUGIN_NAME}_setup.o

### The main target:

all: libvdr-\$(PLUGIN).so i18n

### Implicit rules:

%.o: %.c
	\$(CXX) \$(CXXFLAGS) -c \$(DEFINES) \$(INCLUDES) \$<

### Dependencies:

MAKEDEP = \$(CXX) -MM -MG
DEPFILE = .dependencies
\$(DEPFILE): Makefile
	\@\$(MAKEDEP) \$(DEFINES) \$(INCLUDES) \$(OBJS:%.o=%.c) > \$\@

-include \$(DEPFILE)

### Internationalization (I18N):

PODIR     = po
LOCALEDIR = \$(VDRDIR)/locale
I18Npo    = \$(wildcard \$(PODIR)/*.po)
I18Nmo    = \$(addsuffix .mo, \$(foreach file, \$(I18Npo), \$(basename \$(file))))
I18Ndirs  = \$(notdir \$(foreach file, \$(I18Npo), \$(basename \$(file))))
I18Npot   = \$(PODIR)/\$(PLUGIN).pot

%.mo: %.po
	msgfmt -c -o \$\@ \$<

\$(I18Npot): \$(wildcard *.c)
	xgettext -C -cTRANSLATORS --no-wrap -F -k -ktr -ktrNOOP --msgid-bugs-address='<see README>' -o \$\@ \$(wildcard *.c)

\$(I18Npo): \$(I18Npot)
	msgmerge -U --no-wrap -F --backup=none -q \$\@ \$<

i18n: \$(I18Nmo)
	\@mkdir -p \$(LOCALEDIR)
	for i in \$(I18Ndirs); do\\
	    mkdir -p \$(LOCALEDIR)/\$\$i/LC_MESSAGES;\\
	    cp \$(PODIR)/\$\$i.mo \$(LOCALEDIR)/\$\$i/LC_MESSAGES/vdr-\$(PLUGIN).mo;\\
	    done

### Targets:

libvdr-\$(PLUGIN).so: \$(OBJS)
	\$(CXX) \$(CXXFLAGS) -shared \$(OBJS) -o \$\@
	\@cp --remove-destination \$\@ \$(LIBDIR)/\$\@.\$(APIVERSION)

dist: clean
	\@-rm -rf \$(TMPDIR)/\$(ARCHIVE)
	\@mkdir \$(TMPDIR)/\$(ARCHIVE)
	\@cp -a * \$(TMPDIR)/\$(ARCHIVE)
	\@tar czf \$(PACKAGE).tgz -C \$(TMPDIR) \$(ARCHIVE)
	\@-rm -rf \$(TMPDIR)/\$(ARCHIVE)
	\@echo Distribution package created as \$(PACKAGE).tgz

clean:
	\@-rm -f \$(OBJS) \$(DEPFILE) *.so *.tgz core* *~ \$(PODIR)/*.mo \$(PODIR)/*.pot
};

$MAIN = qq
{/*
 * $PLUGIN_NAME.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * \$Id\$
 */

#include <vdr/plugin.h>

#include "${PLUGIN_NAME}_config.h"
#include "${PLUGIN_NAME}_setup.h"

static const char *VERSION        = "$PLUGIN_VERSION";
static const char *DESCRIPTION    = "$PLUGIN_DESCRIPTION";
static const char *MAINMENUENTRY  = "$PLUGIN_MAINENTRY";

class cPlugin$PLUGIN_CLASS : public cPlugin {
private:
  // Add any member variables or functions you may need here.
public:
  cPlugin$PLUGIN_CLASS(void);
  virtual ~cPlugin$PLUGIN_CLASS();
  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 void MainThreadHook(void);
  virtual cString Active(void);
  virtual time_t WakeupTime(void);
  virtual const char *MainMenuEntry(void) { return (${PLUGIN_NAME}Config.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);
  };

cPlugin${PLUGIN_CLASS}::cPlugin$PLUGIN_CLASS(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!
}

cPlugin${PLUGIN_CLASS}::~cPlugin$PLUGIN_CLASS()
{
  // Clean up after yourself!
}

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

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

bool cPlugin${PLUGIN_CLASS}::Initialize(void)
{
  // Initialize any background activities the plugin shall perform.
  return true;
}

bool cPlugin${PLUGIN_CLASS}::Start(void)
{
  // Start any background activities the plugin shall perform.
  return true;
}

void cPlugin${PLUGIN_CLASS}::Stop(void)
{
  // Stop any background activities the plugin is performing.
}

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

void cPlugin${PLUGIN_CLASS}::MainThreadHook(void)
{
  // Perform actions in the context of the main program thread.
  // WARNING: Use with great care - see PLUGINS.html!
}

cString cPlugin${PLUGIN_CLASS}::Active(void)
{
  // Return a message string if shutdown should be postponed
  return NULL;
}

time_t cPlugin${PLUGIN_CLASS}::WakeupTime(void)
{
  // Return custom wakeup time for shutdown script
  return 0;
}

cOsdObject *cPlugin${PLUGIN_CLASS}::MainMenuAction(void)
{
  // Perform the action when selected from the main VDR menu.
  return NULL;
}

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

bool cPlugin${PLUGIN_CLASS}::SetupParse(const char *Name, const char *Value)
{
  // Parse your own setup parameters and store their values.
  if      (!strcasecmp(Name, "DummyInt"))        ${PLUGIN_NAME}Config.dummyInt = atoi(Value);
  else if (!strcasecmp(Name, "HideMenu"))        ${PLUGIN_NAME}Config.hideMenu = atoi(Value);
  else if (!strcasecmp(Name, "DummyStr"))        strn0cpy(${PLUGIN_NAME}Config.dummyStr, Value,sizeof(${PLUGIN_NAME}Config.dummyStr));
  else
    return false;
  return false;
}

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

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

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

VDRPLUGINCREATOR(cPlugin$PLUGIN_CLASS); // Don't touch this!
};

my $TMP=uc($PLUGIN_NAME);
$SETUP_HEADER = qq
{
#ifndef __${TMP}_SETUP_H
#define __${TMP}_SETUP_H

#include <vdr/i18n.h>
#include <vdr/interface.h>
#include <vdr/plugin.h>

#include "${PLUGIN_NAME}_config.h"

class cMenuSetup${PLUGIN_CLASS} : public cMenuSetupPage {
  private:
    c${PLUGIN_CLASS}Config data;

  protected:
    virtual void Store(void);

  public:
    cMenuSetup${PLUGIN_CLASS}();
};

#endif
};

$CONFIG_HEADER = qq
{
#ifndef __${TMP}_CONFIG_H
#define __${TMP}_CONFIG_H

#define MAXDUMMYSTRLEN 20

struct c${PLUGIN_CLASS}Config
{
public:
    c${PLUGIN_CLASS}Config(void);
    int hideMenu;
    int dummyInt;
    char dummyStr[MAXDUMMYSTRLEN+1];
};

extern c${PLUGIN_CLASS}Config ${PLUGIN_NAME}Config;

#endif
};

$SETUP = qq
{
#include "${PLUGIN_NAME}_config.h"
#include "${PLUGIN_NAME}_setup.h"

const char *cDummyStrChars     = "ab12";

cMenuSetup${PLUGIN_CLASS}::cMenuSetup${PLUGIN_CLASS}()
{
  data = ${PLUGIN_NAME}Config;

  int current = Current();
  Clear();

  Add(new cMenuEditStrItem( tr("DummyStr"),    data.dummyStr,sizeof(data.dummyStr),cDummyStrChars));
  Add(new cMenuEditIntItem( tr("DummyInt"),   &data.dummyInt ,   1,  100));
  Add(new cMenuEditBoolItem(tr("Hide main menu entry"),  &data.hideMenu,       tr("no"),    tr("yes")));
  SetHelp(NULL, NULL, NULL, NULL);

  SetCurrent(Get(current));
  Display();

}

void cMenuSetup${PLUGIN_CLASS}::Store(void)
{
  ${PLUGIN_NAME}Config = data;
  SetupStore("DummyInt",    data.dummyInt);
  SetupStore("DummyStr",    data.dummyStr);
  SetupStore("HideMenu",    data.hideMenu);
}
};

$CONFIG = qq
{
#include <string.h>

#include "${PLUGIN_NAME}_config.h"

c${PLUGIN_CLASS}Config ${PLUGIN_NAME}Config;

c${PLUGIN_CLASS}Config::c${PLUGIN_CLASS}Config(void)
{
  //initialization
  hideMenu       =   1;
  dummyInt       =  30;
  strncpy(dummyStr, "aabb1122", sizeof(MAXDUMMYSTRLEN));
}
};

$PLUGINDIR = "$PLUGINS_SRC/$PLUGIN_NAME";

die "The directory $PLUGINS_SRC doesn't exist!\n" unless (-d "$PLUGINS_SRC");
die "A plugin named '$PLUGIN_NAME' already exists in $PLUGINS_SRC!\n" if (-e "$PLUGINDIR");
mkdir("$PLUGINDIR") || die "$!";

CreateFile("README", $README);
CreateFile("HISTORY", $HISTORY);
CreateFile("Makefile", $MAKEFILE);
CreateFile("${PLUGIN_NAME}.c", $MAIN);
CreateFile("${PLUGIN_NAME}_setup.c", $SETUP);
CreateFile("${PLUGIN_NAME}_config.c", $CONFIG);
CreateFile("${PLUGIN_NAME}_setup.h", $SETUP_HEADER);
CreateFile("${PLUGIN_NAME}_config.h", $CONFIG_HEADER);
`cp COPYING "$PLUGINDIR"` if (-e "COPYING");

print qq{
The new plugin source directory has been created in "$PLUGINDIR".

The next steps you should perform now are:

* edit the file "README" to adjust it to your specific implementation
* fill in the code skeleton in "$PLUGIN_NAME.c" to implement your plugin function
* add further source files if necessary
* adapt the "Makefile" if necessary
* do "make plugins" from the VDR source directory to build your plugin

};

sub CreateFile
{
  my ($Name, $Content) = @_;
  open(FILE, ">$PLUGINDIR/$Name") || die "$Name:  $!\n";
  print FILE $Content;
  close(FILE);
}

