/*
 * Analogue TV Player plugin for VDR
 *
 * Copyright (C) 2003 .. 2006 Andreas Kool <akool@gmx.de>
 *
 * This code is distributed under the terms and conditions of the
 * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
 *
 * Version: $Id: analogtv.c,v 1.103 2006/04/25 17:19:34 akool Exp $
 * Release: $Name:  $
 *
 */

#define __ANALOGTV_C

#include <stdlib.h>
#include <vdr/plugin.h>
#include <vdr/sources.h>
#include <vdr/device.h>
#include <vdr/player.h>
#include <vdr/config.h>

#include <time.h>
#include <sys/utsname.h>

#include "i18n.h"
#include "player-analogtv.h"
#include "menusetup.h"
#include "status.h"
#include "analogtv.h"
#include "device.h"
#include "memcpy.h"

       const char *VERSION        = "1.0.00";
static const char *DESCRIPTION    = "Setup.analogTV$Watch analogue TV";
static const char *MAINMENUENTRY  = "Setup.analogTV$Setup AnalogTV";

// --- cPluginAnalogtv ------------------------------------------------------

const char *cPluginAnalogtv::Version(void) { return VERSION; }
const char *cPluginAnalogtv::Description(void) { return tr(DESCRIPTION); }
const char *cPluginAnalogtv::MainMenuEntry(void) { return s.HideMainMenu ? NULL : tr(MAINMENUENTRY); }

cPluginAnalogtv::cPluginAnalogtv(void) // 1. VDR call
{
  register int   i;
#ifdef HAVE_AC3_OVER_DVB
  auto 	   int   Version, Major, Minor;
  auto	   char  sx[128];
#endif
  auto	   char *sy;
  auto	   struct utsname u;

  // initialising default values

  s.logger = STDOUT;
  s.debug = 1;
  lf = (FILE *)NULL;
  lv = 6; // assume Linux 2.6

  d(0, "analogTV version %s starting up ...", VERSION);
  d(0, "");
  d(0, "Copyright (c) 2003 .. 2006 by Andreas Kool (akool@gmx.de)");
  d(0, "");
  d(0, "The analogTV plugin is the work of many people;");
  d(0, "for at least a partial list see CREDITS.");
  d(0, "");
  d(0, "analogTV comes with ABSOLUTELY NO WARRANTY; for details see COPYING.");
  d(0, "This is free software, and you are welcome to redistribute it");
  d(0, "under certain conditions; see COPYING for details.");
  d(0, "");

  if (!uname(&u)) {
    d(1, "Detected Linux version %s", u.release);

    if (!memcmp(u.release, "2.4", 3))
      lv = 4;
    else if (!memcmp(u.release, "2.6", 3))
      lv = 6;
    else {
      lv = -1;
      d(0, "Unknown kernel version");
    }
  }
  else
    d(0, "Cannot detect kernel version");

  sprintf(vdrver, "%s", APIVERSION);

#if VDRVERSNUM >= 10300
  sy = "\n\t\tW A R N I N G: Unstable development version!";
#else
  sy = "";
#endif

  sy = ""; // AK:20-Jul-2004

  d(1, "Detected VDR version %s%s", vdrver, sy);

#ifdef HAVE_BEAUTYPATCH
#ifdef ELCHIAIOVERSION
  d(1, "Detected ElchiAIO%d%c patch", ELCHIAIOVERSION / 100, ELCHIAIOVERSION % 100 ? char(87 + (ELCHIAIOVERSION % 100)) : ' ');
#else
  d(1, "Detected beauty patch");
#endif
#else
  d(1, "Detected plain-vanilla VDR (hopefully :-)");
#endif

#ifdef HAVE_AC3_OVER_DVB

  // The following is a braindamaged hack!
  // "HAVE_AC3_OVER_DVB" is currently defined as "000202"
  // Because of the leading "0" this results in 130 (octal notation!) :-(

  sprintf(sx, "%o", HAVE_AC3_OVER_DVB);
  Minor = atoi(sx);
  Version = Minor / 10000;
  Minor %= 10000;
  Major = Minor / 100;
  Minor %= 100;
  sprintf(ac3ver, "%d.%d.%d", Version, Major, Minor);

  d(1, "Detected AC3overDVB patch version %s", ac3ver);
#else
  *ac3ver = 0;
#endif

  strcpy(s.audio_device, "/dev/dsp");   // alsa/default
  strcpy(  video_device, "/dev/video"); // not really known yet ...
  strcpy(s.mixer_device, "/dev/mixer");
  strcpy(FIFO, "/tmp/.analogtv.avi");

  *capdev = 0;

  s.mixer_line = SOUND_MIXER_LINE1;     // "Line1" | SOUND_MIXER_LINE = "Line"
  s.mixer_volume = 91;			// PVR default: 59343

  s.audio_mode = 3;
  s.sample_rate = 2;
  v4l = -1;
  ReaderLoopRunning = 0;
  EncoderNeeded = 0;
  EncoderRunning = 0;
  Restart = 0;

  s.EncoderAlwaysUp = 0;
  s.link = 0;  // blocking read()
  dropPES = 1;
  SetChannelDeviceDone = 0;

  vdr_ready = 0;
  s.HideMainMenu = 1;

  frequency = -1;
  freqOffset = 16;

  // best settings for IESY:
  // ----------------------
  // "|C-0-154-10=12|C-0-303-10=11|C-0-203-10=10|"

  //       N24 = 12 -> 12 - 16 =  -4 * 50 * 16 / 1000 = -3,20 Hz
  // Super RTL = 11 -> 11 - 16 =  -5 * 50 * 16 / 1000 = -4,00 Hz
  //     SAT.1 =  6 ->  6 - 16 = -10 * 50 * 16 / 1000 = -8,00 Hz

  strcpy(s.finetune, "|");

  videosource = lastvideosource = -1;
  videodev = lastvideodev = -1;
  videonorm = lastvideonorm = -1;

  // best settings for Bt878:
  // ------------------------
  // Brightness = 20 -> 38550 (0x9696) 60%
  // Contrast   = 14 -> 26985 (0x6969) 40%
  // Colour     = 16 -> 30840 (0x7878) 47%
  // Hue        = 15 -> 28912 (0x70F0) 45%

  // best settings for PVR (ITU standard):
  // -------------------------------------
  // Brightness = 17 -> 0x80
  // Contrast   = 18 -> 0x44
  // Colour     = 17 -> 0x40
  // Hue        = 17 -> 0x00

  s.brightness = s.contrast = s.colour = s.hue = -1;

  Apid = Vpid = Tpid = -1;

  s.video_bit_rate = 50; // i.e. 5000 Mbits/s

  s.audio_bit_rate = 80; // i.e. 80 kbit/s

  s.image_size = 0;
  s.clipping = 1;
  s.gop = 1;

  s.filter = 7; // CM_YUV_VERTICAL_DECIMATION
  s.motion[0] = s.motion[1] = s.analysis = s.test = s.profile = 0;
  s.mpeg = 0;
  s.encode = VIDEO | AUDIO;

  wide = 0;
  *extraOpts = 0;

  s.mpeg_encoder = MP1E;

  s.enabledigibox = 0;
  strcpy(s.redremote_device, "/dev/redremote");
  s.backupHack = 1;
  s.backupInterval = 30;
  s.fastdigibox_change = 0;

  analogModule = 0;

  AnalogtvDevice = NULL;
  AnalogtvStatus = NULL;

  for (i = 0; i < MAXSOURCES; i++)
    *sources[i] = 0;

  recordings = replays = 0;

  s.memcpy_method = MEMCPY_PROBE;

  s.epg_source = 1;             // pipe
  *s.epg_external = 0;          // "ssh tv.akool.de nxtvepg -dump pi -prov %s > /tmp/pi.txt"
  strcpy(s.epg_provider, "FF"); // merged database
  s.epg_autoupdate = 0;         // disabled
  s.epg_language = 2;           // ger

  epg_update = time((time_t)NULL);

  nxtvepg_killed = 0;

  // PVR specials
  s.framespergop = 12;
  s.gop_closure = 1;
  s.bframes = 3;
  s.dnr_mode = 0;
  s.dnr_spatial = 0;
  s.dnr_temporal = 0;
  s.dnr_type = 0;
  s.bitrate_mode = 0; // VBR
  s.aspect = 1;       // 4:3
  s.pulldown = 0;     // enabled

  s.syncHack = 1;
  s.winingHack = 0;
  s.avHack = 0;
  s.propHack = 0;
  s.stutteringHack = 1;
  s.remux = 0;
  s.usePVR350 = 0;

  // Current (active) settings
  sc.mpeg_encoder = -1;
  sc.video_bit_rate = -1;
  sc.framespergop = -1;
  sc.bframes = -1;
  sc.gop_closure = -1;
  sc.dnr_mode = -1;
  sc.dnr_type = -1;
  sc.dnr_spatial = -1;
  sc.dnr_temporal = -1;
  sc.bitrate_mode = -1;
  sc.aspect = -1;
  sc.pulldown = -1;

  sc.syncHack = -1;
  sc.winingHack = -1;
  sc.avHack = -1;
  sc.propHack = -1;
  sc.stutteringHack = -1;
  sc.remux = -1;
  sc.usePVR350 = -1;

  sc.audio_bit_rate = -1;
  sc.image_size = -1;
  sc.mixer_volume = -1;

  sc.brightness = -1;
  sc.contrast   = -1;
  sc.colour     = -1;
  sc.hue        = -1;

  sc.clipping   = -1;
  sc.gop        = -1;
  sc.filter     = -1;
  sc.motion[0]  = -1;
  sc.motion[1]  = -1;
  sc.profile    = -1;
  sc.test       = -1;
  sc.encode 	= VIDEO | AUDIO;
  *sc.audio_device = 0;
  *sc.mixer_device = 0;
  sc.mixer_line = -1;
  sc.audio_mode = -1;
  sc.sample_rate = -1;

  sc.enabledigibox = -1;
  *sc.redremote_device = 0;
  sc.backupHack = -1;
  sc.backupInterval = -1;
  sc.fastdigibox_change = -1;
}

cPluginAnalogtv::~cPluginAnalogtv()
{
  d(3, "cPluginAnalogtv::~cPluginAnalogtv()");

  if (confdir)
    delete confdir;

  if (AnalogtvDevice) {
    // FIXME: segfaults!   delete AnalogtvDevice;
    AnalogtvDevice = NULL;
  }

  if (AnalogtvStatus) {
    delete AnalogtvStatus;
    AnalogtvStatus = NULL;
  }
}

bool cPluginAnalogtv::Initialize(void) // early initialization, 3. VDR call
{
  RegisterI18n(AnalogtvPhrases);

  d(3, "cPluginAnalogtv::Initialize()");

  AnalogtvDevice = new cAnalogtvDevice;
  return true;
}

bool cPluginAnalogtv::Start(void) // final initialization, 5. VDR call
{
  extern void cpuinfo(void);

  d(3, "cPluginAnalogtv::Start()");

  RegisterI18n(AnalogtvPhrases);

  confdir = strdup(ConfigDirectory());
  d(1, "Detected ConfigDirectory()=\"%s\"", confdir);

  cpuinfo();

#ifdef HAVE_FAST_MEMCPY
  s.memcpy_method = probe_fast_memcpy(s.memcpy_method);
#else
  s.memcpy_method = MEMCPY_GLIBC;
  fast_memcpy = memcpy;
#endif

  SetupStore("MemcpyMethod", s.memcpy_method);

  AnalogtvStatus = new cAnalogtvStatus;

  return true;
}

void cPluginAnalogtv::StopEncoder(void)
{
  d(3, "cPluginAnalogtv::StopEncoder()");

  if (AnalogtvDevice)
    AnalogtvDevice->StopEncoder();
}

void cPluginAnalogtv::Housekeeping(void)
{
  register int    cc;
  auto     time_t now;

  d(3, "cPluginAnalogtv::Housekeeping()");

  if (EncoderRunning && !EncoderNeeded) {
    d(2, "Shutting down encoder, not needed at the moment");

    StopEncoder();
  }

  if (!EncoderRunning && nxtvepg_killed) {
    cc = SystemExec("nxtvepg -daemon");

    if (!cc) {
      nxtvepg_killed = 0;
      d(1, "nxtvepg restarted");
    }
  }

  if (s.epg_autoupdate) {
    now = time((time_t)NULL);

    if (now > (epg_update + (s.epg_autoupdate * 60 * 60))) {
      d(0, "EPG autoupdate %s", ctime(&now));

      AnalogtvEPGhandler = new cAnalogtvEPGhandler;

      if (!AnalogtvEPGhandler->Process())
        d(0, "%s", tr("Setup.analogTV$Loading of EPG failed"));

      delete AnalogtvEPGhandler;
      AnalogtvEPGhandler = NULL;

      epg_update = time((time_t)NULL);
    }
  }
}

cOsdMenu *cPluginAnalogtv::MainMenuAction(void)
{
  d(3, "cPluginAnalogtv::MainMenuAction()");

  return new cMenuSetupAnalogtv(this);
}

cMenuSetupPage *cPluginAnalogtv::SetupMenu(void)
{
  d(3, "cPluginAnalogtv::SetupMenu()");

  return new cMenuSetupAnalogtv(this);
}

static const char *strip(const char *s)
{
  if (*s == '"')
    return s + 1;
  else
    return s;
}

bool cPluginAnalogtv::SetupParse(const char *Name, const char *Value) // 2. VDR call
{
  d(5, "cPluginAnalogtv::SetupParse()");

  if      (!strcasecmp(Name, "debug"))              s.debug = atoi(Value);
  else if (!strcasecmp(Name, "Logger"))             s.logger = atoi(Value);
  else if (!strcasecmp(Name, "Report"))             s.report = atoi(Value);
  else if (!strcasecmp(Name, "Encoder"))            s.mpeg_encoder = atoi(Value);
  else if (!strcasecmp(Name, "PCMdevice"))          strcpy(s.audio_device, strip(Value));
  else if (!strcasecmp(Name, "MixerDevice"))        strcpy(s.mixer_device, strip(Value));
  else if (!strcasecmp(Name, "MixerInput"))         s.mixer_line = atoi(Value);
  else if (!strcasecmp(Name, "MixerVolume"))        s.mixer_volume = atoi(Value);
  else if (!strcasecmp(Name, "Videobitrate"))       s.video_bit_rate = atoi(Value);
  else if (!strcasecmp(Name, "AudioBitRate"))       s.audio_bit_rate = atoi(Value);
  else if (!strcasecmp(Name, "ImageSize"))          s.image_size = atoi(Value);
  else if (!strcasecmp(Name, "Clipping"))           s.clipping = atoi(Value);
  else if (!strcasecmp(Name, "GOP"))                s.gop = atoi(Value);
  else if (!strcasecmp(Name, "Filter"))             s.filter = atoi(Value);
  else if (!strcasecmp(Name, "MotionMin"))          s.motion[0] = atoi(Value);
  else if (!strcasecmp(Name, "MotionMax"))          s.motion[1] = atoi(Value);
  else if (!strcasecmp(Name, "Deinterlace"))        s.deinterlace = atoi(Value);
  else if (!strcasecmp(Name, "Profile"))            s.profile = atoi(Value);
  else if (!strcasecmp(Name, "MPEG"))               s.mpeg = atoi(Value);
  else if (!strcasecmp(Name, "Test"))               s.test = atoi(Value);
  else if (!strcasecmp(Name, "AudioMode"))          s.audio_mode = atoi(Value);
  else if (!strcasecmp(Name, "SampleRate"))         s.sample_rate = atoi(Value);
  else if (!strcasecmp(Name, "PsychoAnalysis"))     s.analysis = atoi(Value);
  else if (!strcasecmp(Name, "Brightness"))         s.brightness = atoi(Value);
  else if (!strcasecmp(Name, "Hue"))                s.hue = atoi(Value);
  else if (!strcasecmp(Name, "Colour"))             s.colour = atoi(Value);
  else if (!strcasecmp(Name, "Contrast"))           s.contrast = atoi(Value);
  else if (!strcasecmp(Name, "HideMainMenu"))       s.HideMainMenu = atoi(Value);
  else if (!strcasecmp(Name, "Finetune"))           strcpy(s.finetune, strip(Value));
  else if (!strcasecmp(Name, "EncoderAlwaysUp"))    s.EncoderAlwaysUp = atoi(Value);
  else if (!strcasecmp(Name, "Link"))               s.link = atoi(Value);
  else if (!strcasecmp(Name, "MemcpyMethod"))       s.memcpy_method = atoi(Value);
  else if (!strcasecmp(Name, "EPGfrom"))            s.epg_source = atoi(Value);
  else if (!strcasecmp(Name, "External"))           strcpy(s.epg_external, strip(Value));
  else if (!strcasecmp(Name, "EPGprovider"))        strcpy(s.epg_provider, strip(Value));
  else if (!strcasecmp(Name, "AutoUpdate"))         s.epg_autoupdate = atoi(Value);
  else if (!strcasecmp(Name, "ThemeLanguage"))      s.epg_language = atoi(Value);
  else if (!strcasecmp(Name, "FramesPerGOP"))       s.framespergop = atoi(Value);
  else if (!strcasecmp(Name, "Bframes"))            s.bframes = atoi(Value);
  else if (!strcasecmp(Name, "GOPclosure"))         s.gop_closure = atoi(Value);
  else if (!strcasecmp(Name, "DNRmode"))            s.dnr_mode = atoi(Value);
  else if (!strcasecmp(Name, "DNRspatial"))         s.dnr_spatial = atoi(Value);
  else if (!strcasecmp(Name, "DNRtemporal"))        s.dnr_temporal = atoi(Value);
  else if (!strcasecmp(Name, "DNRtype"))            s.dnr_type = atoi(Value);
  else if (!strcasecmp(Name, "BitrateMode"))        s.bitrate_mode = atoi(Value);
  else if (!strcasecmp(Name, "Aspect"))        	    s.aspect = atoi(Value);
  else if (!strcasecmp(Name, "Pulldown"))           s.pulldown = atoi(Value);
  else if (!strcasecmp(Name, "SyncHack"))           s.syncHack = atoi(Value);
  else if (!strcasecmp(Name, "WiningHack"))         s.winingHack = atoi(Value);
  else if (!strcasecmp(Name, "avHack"))             s.avHack = atoi(Value);
  else if (!strcasecmp(Name, "ppHack"))             s.propHack = atoi(Value);
  else if (!strcasecmp(Name, "StutteringHack"))     s.stutteringHack = atoi(Value);
  else if (!strcasecmp(Name, "Remux"))     	    s.remux = atoi(Value);
  else if (!strcasecmp(Name, "usePVR350"))     	    s.usePVR350 = atoi(Value);
  else if (!strcasecmp(Name, "EnableDigiBox"))      s.enabledigibox = atoi(Value);
  else if (!strcasecmp(Name, "RedRemoteDevice"))    strcpy(s.redremote_device, strip(Value));
  else if (!strcasecmp(Name, "backupHack"))         s.backupHack = atoi(Value);
  else if (!strcasecmp(Name, "backupInterval"))     s.backupInterval = atoi(Value);
  else if (!strcasecmp(Name, "FastDigiboxChange"))  s.fastdigibox_change = atoi(Value);
  else
    return false;

  return true;
}

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