/*
 * event und message handler
 */

#include <time.h>
#include <unistd.h>
#include <vdr/plugin.h>
#include <vdr/timers.h>
#include <vdr/videodir.h>
#include "helpers.h"
#include "vdrmonthread.h"

string cHelpers::GetRunningRecordings()
{
  return SafeCall(GetRunningRecordingsIntern);
}

string cHelpers::GetNextRecordings(time_t& starttime)
{
  return SafeCall(GetNextRecordingsIntern, starttime);
}

string cHelpers::GetShutdownTime(time_t& stoptime)
{
  return SafeCall(GetShutdownTimeIntern, stoptime);
}

string cHelpers::GetRunningRecordingsIntern()
{
  int count = 0;

  // send clear command
  string lines = "CREC\n";
  
  // iterate through all timers
  char text[40];
  for(cTimer * timer = Timers.First(); timer; timer = Timers.Next(timer))
  {
    if (timer->Recording())
    {
      sprintf(text, "%08x %08x ", (int)timer->StartTime(), (int)timer->StopTime());
      lines += string("AREC ") + text + (const char *)(timer->ToText());
      count++;
    }
  }

  sprintf(text, "NREC %d\n", count);

  return lines + text;
}

string cHelpers::GetNextRecordingsIntern(time_t& starttime)
{
  time_t now = time(0);

  cTimer *timer = NULL;
  for (cTimer *ti = Timers.First(); ti; ti = Timers.Next(ti))
  {
    // skip inactive timers
    if (!ti->HasFlags(tfActive))
      continue;

    // skip old (possibly running) timers
    if (ti->StartTime() < now)
      continue;

    // select the nearest
    if (timer == 0 || ti->Compare(*timer) < 0)
      timer = ti;
  }

  if (timer == NULL)
  {
    starttime = 0;
    return "CNXT\nNNXT\n";
  }

  // assemble text
  string recs = "CNXT\n";
  char text[40];
  starttime = timer->StartTime();

  // it may be multiple recordings
  for (cTimer *ti = Timers.First(); ti; ti = Timers.Next(ti))
  {
    // skip inactive or wrong timers
    if (!ti->HasFlags(tfActive) || ti->StartTime() != timer->StartTime())
      continue;

    // get times
    sprintf(text, "%08x %08x ", (int)starttime, (int)timer->StopTime());

    // cTimer::ToText() already contains a newline
    recs += string("NEXT ") + text + (const char *)(ti->ToText());
  }

  recs += "NNXT\n";

  return recs;
}

string cHelpers::GetShutdownTimeIntern(time_t& stoptime)
{
  class timernode
  {
  public:
    timernode(cTimer * t) { ti = t; next = NULL; }
    ~timernode() { delete next; }
    cTimer * ti;
    timernode * next;
  } * timers = NULL;

  // timer present?
  if (Timers.First() == NULL)
  {
    stoptime = 0;
    return "DOWN 0\n";
  }

  // sort by start time (nur mit HasFlags(tfActive))
  for(cTimer * t = Timers.First(); t; t = Timers.Next(t))
  {
    // Aktiv?
    if (!t->HasFlags(tfActive))
      continue;

    // insert by start time
    timernode * ti = timers;
    timernode * last = NULL;
    while (ti && ti->ti->StartTime() < t->StartTime())
    {
      last = ti;
      ti = ti->next;
    }
    ti = new timernode(t);
    if (last == NULL)
    {
      // insert before the first timer
      ti->next = timers;
      timers = ti;
    }
    else
    {
      ti->next = last->next;
      last->next = ti;
    }
  }

  // calculate shutdown time
  timernode * t = timers;
  stoptime = t->ti->StopTime();
  while (t)
  {
    if (t->ti->StartTime() <= stoptime && t->ti->StopTime() > stoptime)
      stoptime = t->ti->StopTime();
    t = t->next;
  }
  delete timers;
 
  char text[20];
  sprintf(text, "DOWN %08lx\n", stoptime);

  return text;
}

string cHelpers::ToUpper(string text)
{
  for(unsigned i = 0; i < text.length(); i++)
  {
    if (islower(text[i]))
      text[i] = toupper(text[i]);
  }

  return text;
}

string cHelpers::SafeCall(string (*f)())
{
  // loop, if vdr modified list and we crash
  for (int i = 0; i < 3; i++)
  {
    try
    {
      return f();
    }
    catch (...)
    {
      usleep(100);
    }
  }

  return "";
}

string cHelpers::SafeCall(string (*f)(time_t& starttime), time_t& starttime)
{
  // loop, if vdr modified list and we crash
  for (int i = 0; i < 3; i++)
  {
    try
    {
      return f(starttime);
    }
    catch (...)
    {
      usleep(100);
    }
  }

  return "";
}

string cHelpers::GetFreeSpace()
{
  int FreeMB, UsedMB, Percent;
  Percent = VideoDiskSpace(&FreeMB, &UsedMB);
  char text[50];
  sprintf(text, "DISK %d %d %d\n", FreeMB + UsedMB, FreeMB, Percent);

  return text;
}

string cHelpers::GetVersion()
{
  extern const char * VERSION;
  return string("VERS ") + VERSION + "\n";
}


string cHelpers::GetPlays()
{
  string title;
  if (cVdrmonThread::Plays(title))
    return "PLAY " + title + "\n";
  else
    return string("NPLY\n");
}
