/*
    Copyright 2004-2005 Chris Tallon

    This file is part of VOMP.

    VOMP 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.

    VOMP is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with VOMP; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "mvpclient.h"
#include <vdr/remote.h>
#include "mediaplayer.h"
#include "media.h"
#include "servermediafile.h"
#include "vdrcommand.h"

// This is here else it causes compile errors with something in libdvbmpeg
//#include <vdr/menu.h>

pthread_mutex_t threadClientMutex;
int MVPClient::nr_clients = 0;


MVPClient::MVPClient(Config* cfgBase, char* tconfigDirExtra, int tsocket)
 : tcp(tsocket)
{
#ifndef VOMPSTANDALONE
  lp = NULL;
  rp = NULL;
  recordingManager = NULL;
  incClients();
#endif
  log = Log::getInstance();
  loggedIn = false;
  configDirExtra = tconfigDirExtra;
  baseConfig = cfgBase;
  media=new MediaPlayer();
  mediaprovider=new ServerMediaFile(cfgBase,media);
}

MVPClient::~MVPClient()
{
  log->log("Client", Log::DEBUG, "MVP client destructor");
#ifndef VOMPSTANDALONE  
  if (lp)
  {
    delete lp;
    lp = NULL;
  }
  else if (rp)
  {
    writeResumeData();

    delete rp;
    delete recordingManager;
    rp = NULL;
    recordingManager = NULL;
  }
  if (loggedIn) cleanConfig();
  decClients();
#endif
}

ULLONG MVPClient::ntohll(ULLONG a)
{
  return htonll(a);
}

ULLONG MVPClient::htonll(ULLONG a)
{
  #if BYTE_ORDER == BIG_ENDIAN
    return a;
  #else
    ULLONG b = 0;

    b = ((a << 56) & 0xFF00000000000000ULL)
      | ((a << 40) & 0x00FF000000000000ULL)
      | ((a << 24) & 0x0000FF0000000000ULL)
      | ((a <<  8) & 0x000000FF00000000ULL)
      | ((a >>  8) & 0x00000000FF000000ULL)
      | ((a >> 24) & 0x0000000000FF0000ULL)
      | ((a >> 40) & 0x000000000000FF00ULL)
      | ((a >> 56) & 0x00000000000000FFULL) ;

    return b;
  #endif
}

#ifndef VOMPSTANDALONE
cChannel* MVPClient::channelFromNumber(ULONG channelNumber)
{
  cChannel* channel = NULL;

  for (channel = Channels.First(); channel; channel = Channels.Next(channel))
  {
    if (!channel->GroupSep())
    {
      log->log("Client", Log::DEBUG, "Looking for channel %lu::: number: %i name: '%s'", channelNumber, channel->Number(), channel->Name());

      if (channel->Number() == (int)channelNumber)
      {
        int vpid = channel->Vpid();
        int apid1 = channel->Apid(0);

        log->log("Client", Log::DEBUG, "Found channel number %lu, vpid = %i, apid1 = %i", channelNumber, vpid, apid1);
        return channel;
      }
    }
  }

  if (!channel)
  {
    log->log("Client", Log::DEBUG, "Channel not found");
  }

  return channel;
}

void MVPClient::writeResumeData()
{
  config.setValueLong("ResumeData",
                          (char*)rp->getCurrentRecording()->FileName(),
                          rp->frameNumberFromPosition(rp->getLastPosition()) );
}
#endif

void MVPClient::sendULONG(ULONG ul)
{
  UCHAR sendBuffer[8];
  *(ULONG*)&sendBuffer[0] = htonl(4);
  *(ULONG*)&sendBuffer[4] = htonl(ul);

  tcp.sendPacket(sendBuffer, 8);
  log->log("Client", Log::DEBUG, "written ULONG %lu", ul);
}

void MVPClientStartThread(void* arg)
{
  MVPClient* m = (MVPClient*)arg;
  m->run2();
  // Nothing external to this class has a reference to it
  // This is the end of the thread.. so delete m
  delete m;
  pthread_exit(NULL);
}

int MVPClient::run()
{
  if (pthread_create(&runThread, NULL, (void*(*)(void*))MVPClientStartThread, (void *)this) == -1) return 0;
  log->log("Client", Log::DEBUG, "MVPClient run success");
  return 1;
}

void MVPClient::run2()
{
  // Thread stuff
  sigset_t sigset;
  sigfillset(&sigset);
  pthread_sigmask(SIG_BLOCK, &sigset, NULL);
  pthread_detach(runThread);  // Detach

  tcp.disableReadTimeout();

  tcp.setSoKeepTime(3);
  tcp.setNonBlocking();

  UCHAR* buffer;
  UCHAR* data;
  int packetLength;
  ULONG opcode;
  int result = 0;

  while(1)
  {
    log->log("Client", Log::DEBUG, "Waiting");
    buffer = (UCHAR*)tcp.receivePacket();
    log->log("Client", Log::DEBUG, "Received packet, length = %u", tcp.getDataLength());
    if (buffer == NULL)
    {
      log->log("Client", Log::DEBUG, "Detected connection closed");
      break;
    }

    packetLength = tcp.getDataLength() - 4;
    opcode = ntohl(*(ULONG*)buffer);
    data = buffer + 4;

    if (!loggedIn && (opcode != 1))
    {
      free(buffer);
      break;
    }

    log->log("Client", Log::DEBUG, "SwitchOp %d",opcode);
    switch(opcode)
    {
      case 1:
        result = processLogin(data, packetLength);
        break;
#ifndef VOMPSTANDALONE        
      case 2:
        result = processGetRecordingsList(data, packetLength);
        break;
      case 3:
        result = processDeleteRecording(data, packetLength);
        break;
      case 5:
        result = processGetChannelsList(data, packetLength);
        break;
      case 6:
        result = processStartStreamingChannel(data, packetLength);
        break;
      case 7:
        result = processGetBlock(data, packetLength);
        break;
      case 8:
        result = processStopStreaming(data, packetLength);
        break;
      case 9:
        result = processStartStreamingRecording(data, packetLength);
        break;
      case 10:
        result = processGetChannelSchedule(data, packetLength);
        break;
#endif     
      case 11:
        result = processConfigSave(data, packetLength);
        break;
      case 12:
        result = processConfigLoad(data, packetLength);
        break;
#ifndef VOMPSTANDALONE        
      case 13:
        result = processReScanRecording(data, packetLength);         // FIXME obselete
        break;
      case 14:
        result = processGetTimers(data, packetLength);
        break;
      case 15:
        result = processSetTimer(data, packetLength);
        break;
      case 16:
        result = processPositionFromFrameNumber(data, packetLength);
        break;
      case 17:
        result = processFrameNumberFromPosition(data, packetLength);
        break;
      case 18:
        result = processMoveRecording(data, packetLength);
        break;
      case 19:
        result = processGetIFrame(data, packetLength);
        break;
      case 20:
        result = processGetRecInfo(data, packetLength);
        break;
      case 21:
        result = processGetMarks(data, packetLength);
        break;
      case 22:
        result = processGetChannelPids(data, packetLength);
        break;
      case 23:
        result = processDeleteTimer(data, packetLength);
        break;
 #endif
      case VDR_GETMEDIALIST:
        result = processGetMediaList(data, packetLength);
        break;
      case VDR_OPENMEDIA:
        result = processOpenMedia(data, packetLength);
        break;
      case VDR_GETMEDIABLOCK:
        result = processGetMediaBlock(data, packetLength);
        break;
      case VDR_GETMEDIAINFO:
        result= processGetMediaInfo(data, packetLength);
        break;
      case VDR_CLOSECHANNEL:
        result= processCloseMediaChannel(data, packetLength);
        break;
      case 666:
        result = processVDRShutdown(data, packetLength);
        break;
    }

    free(buffer);
    if (!result) break;
  }
}

int MVPClient::processLogin(UCHAR* buffer, int length)
{
  if (length != 6) return 0;

  // Open the config

#ifndef VOMPSTANDALONE
  const char* configDir = cPlugin::ConfigDirectory(configDirExtra);
#else
  const char* configDir = ".";
#endif
  if (!configDir)
  {
    log->log("Client", Log::DEBUG, "No config dir!");
    return 0;
  }

  char configFileName[PATH_MAX];
  snprintf(configFileName, PATH_MAX, "%s/vomp-%02X-%02X-%02X-%02X-%02X-%02X.conf", configDir, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
  config.init(configFileName);

  // Send the login reply

  time_t timeNow = time(NULL);
  struct tm* timeStruct = localtime(&timeNow);
  int timeOffset = timeStruct->tm_gmtoff;

  UCHAR sendBuffer[12];
  *(ULONG*)&sendBuffer[0] = htonl(8);
  *(ULONG*)&sendBuffer[4] = htonl(timeNow);
  *(signed int*)&sendBuffer[8] = htonl(timeOffset);

  tcp.sendPacket(sendBuffer, 12);
  log->log("Client", Log::DEBUG, "written login reply");

  loggedIn = true;
  return 1;
}

#ifndef VOMPSTANDALONE
int MVPClient::processGetRecordingsList(UCHAR* data, int length)
{
  UCHAR* sendBuffer = new UCHAR[90000]; // hope this is enough
  int count = 4; // leave space for the packet length
  char* point;

  //access protection stuff
  char* lockDir = config.getValueString("General", "LockDir");

  int FreeMB;
  int Percent = VideoDiskSpace(&FreeMB);
  int Total = (FreeMB / (100 - Percent)) * 100;

  *(ULONG*)&sendBuffer[count] = htonl(Total);
  count += sizeof(ULONG);
  *(ULONG*)&sendBuffer[count] = htonl(FreeMB);
  count += sizeof(ULONG);
  *(ULONG*)&sendBuffer[count] = htonl(Percent);
  count += sizeof(ULONG);


  cRecordings Recordings;
  Recordings.Load();

  for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording))
  {
    if (count > 89000) break; // just how big is that hard disk?!
    //see if there is a lock set in the Channels config
    if( lockDir ) {
      if(  strncmp(lockDir,(char*)recording->Name(),strlen(lockDir)) != 0 && strcmp(lockDir,"Parents") !=0 ) {
        continue;
      } else {
        log->log("Client", Log::DEBUG, "Locked entry found: '%s'",(char*)recording->Name() );
      }
    }
    *(ULONG*)&sendBuffer[count] = htonl(recording->start);// + timeOffset);
    count += 4;

    point = (char*)recording->Name();
    strcpy((char*)&sendBuffer[count], point);
    count += strlen(point) + 1;

    point = (char*)recording->FileName();
    strcpy((char*)&sendBuffer[count], point);
    count += strlen(point) + 1;
  }

  *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field

  log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));

  tcp.sendPacket(sendBuffer, count);
  delete[] sendBuffer;
  log->log("Client", Log::DEBUG, "Written list");

  return 1;
}

int MVPClient::processDeleteRecording(UCHAR* data, int length)
{
  // data is a pointer to the fileName string

  cRecordings Recordings;
  Recordings.Load(); // probably have to do this

  cRecording* recording = Recordings.GetByName((char*)data);

  log->log("Client", Log::DEBUG, "recording pointer %p", recording);

  if (recording)
  {
    log->log("Client", Log::DEBUG, "deleting recording: %s", recording->Name());

    cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
    if (!rc)
    {
      if (recording->Delete())
      {
        // Copy svdrp's way of doing this, see if it works
        ::Recordings.DelByName(recording->FileName());

        sendULONG(1);
      }
      else
      {
        sendULONG(2);
      }
    }
    else
    {
      sendULONG(3);
    }
  }
  else
  {
    sendULONG(4);
  }

  return 1;
}

int MVPClient::processMoveRecording(UCHAR* data, int length)
{
  log->log("Client", Log::DEBUG, "Process move recording");
  char* fileName = (char*)data;
  char* newPath = NULL;

  for (int k = 0; k < length; k++)
  {
    if (data[k] == '\0')
    {
      newPath = (char*)&data[k+1];
      break;
    }
  }
  if (!newPath) return 0;

  cRecordings Recordings;
  Recordings.Load(); // probably have to do this

  cRecording* recording = Recordings.GetByName((char*)fileName);

  log->log("Client", Log::DEBUG, "recording pointer %p", recording);

  if (recording)
  {
    cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
    if (!rc)
    {
      log->log("Client", Log::DEBUG, "moving recording: %s", recording->Name());
      log->log("Client", Log::DEBUG, "moving recording: %s", recording->FileName());
      log->log("Client", Log::DEBUG, "to: %s", newPath);

      const char* t = recording->FileName();

      char* dateDirName = NULL;   int k;
      char* titleDirName = NULL;  int j;

      // Find the datedirname
      for(k = strlen(t) - 1; k >= 0; k--)
      {
        if (t[k] == '/')
        {
          log->log("Client", Log::DEBUG, "l1: %i", strlen(&t[k+1]) + 1);
          dateDirName = new char[strlen(&t[k+1]) + 1];
          strcpy(dateDirName, &t[k+1]);
          break;
        }
      }

      // Find the titledirname

      for(j = k-1; j >= 0; j--)
      {
        if (t[j] == '/')
        {
          log->log("Client", Log::DEBUG, "l2: %i", (k - j - 1) + 1);
          titleDirName = new char[(k - j - 1) + 1];
          memcpy(titleDirName, &t[j+1], k - j - 1);
          titleDirName[k - j - 1] = '\0';
          break;
        }
      }

      log->log("Client", Log::DEBUG, "datedirname: %s", dateDirName);
      log->log("Client", Log::DEBUG, "titledirname: %s", titleDirName);

      log->log("Client", Log::DEBUG, "viddir: %s", VideoDirectory);

      char* newContainer = new char[strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1];
      log->log("Client", Log::DEBUG, "l10: %i", strlen(VideoDirectory) + strlen(newPath) + strlen(titleDirName) + 1);
      sprintf(newContainer, "%s%s%s", VideoDirectory, newPath, titleDirName);

      // FIXME Check whether this already exists before mkdiring it

      log->log("Client", Log::DEBUG, "%s", newContainer);


      struct stat dstat;
      int statret = stat(newContainer, &dstat);
      if ((statret == -1) && (errno == ENOENT)) // Dir does not exist
      {
        log->log("Client", Log::DEBUG, "new dir does not exist");
        int mkdirret = mkdir(newContainer, 0755);
        if (mkdirret != 0)
        {
          delete[] dateDirName;
          delete[] titleDirName;
          delete[] newContainer;
          sendULONG(5);
          return 1;
        }
      }
      else if ((statret == 0) && (! (dstat.st_mode && S_IFDIR))) // Something exists but it's not a dir
      {
        delete[] dateDirName;
        delete[] titleDirName;
        delete[] newContainer;
        sendULONG(5);
        return 1;
      }

      // Ok, the directory container has been made, or it pre-existed.

      char* newDir = new char[strlen(newContainer) + 1 + strlen(dateDirName) + 1];
      sprintf(newDir, "%s/%s", newContainer, dateDirName);

      log->log("Client", Log::DEBUG, "doing rename '%s' '%s'", t, newDir);
      int renameret = rename(t, newDir);
      if (renameret == 0)
      {
        // Success. Test for remove old dir containter
        char* oldTitleDir = new char[k+1];
        memcpy(oldTitleDir, t, k);
        oldTitleDir[k] = '\0';
        log->log("Client", Log::DEBUG, "len: %i, cp: %i, strlen: %i, oldtitledir: %s", k+1, k, strlen(oldTitleDir), oldTitleDir);
        rmdir(oldTitleDir); // can't do anything about a fail result at this point.
        delete[] oldTitleDir;
      }

      if (renameret == 0)
      {
        ::Recordings.Update();
        // Success. Send a different packet from just a ulong
        int totalLength = 4 + 4 + strlen(newDir) + 1;
        UCHAR* sendBuffer = new UCHAR[totalLength];
        *(ULONG*)&sendBuffer[0] = htonl(totalLength - 4);
        *(ULONG*)&sendBuffer[4] = htonl(1); // success
        strcpy((char*)&sendBuffer[8], newDir);
        tcp.sendPacket(sendBuffer, totalLength);
        delete[] sendBuffer;
      }
      else
      {
        sendULONG(5);
      }

      delete[] dateDirName;
      delete[] titleDirName;
      delete[] newContainer;
      delete[] newDir;
    }
    else
    {
      sendULONG(3);
    }
  }
  else
  {
    sendULONG(4);
  }

  return 1;
}

int MVPClient::processGetChannelsList(UCHAR* data, int length)
{
  UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
  int count = 4; // leave space for the packet length
  char* point;
  ULONG type;

  char* chanConfig = config.getValueString("General", "Channels");
  int allChans = 1;
  if (chanConfig) allChans = strcasecmp(chanConfig, "FTA only");

  //Weired hack for group selection
  char* lockDir = config.getValueString("General", "LockDir");
  int locked = 0;
  for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel))
  {
    if( lockDir )
    {
      if( channel->GroupSep() )
      {
        if( locked == 1 )
       {
         break;
        }
       else
       {
         if( strcmp(channel->Name(), lockDir) == 0 ) {
           locked = 1;
         }
       }
      }
    }
    if (!channel->GroupSep() && (!channel->Ca(0) || allChans))
    {
      log->log("Client", Log::DEBUG, "name: '%s'", channel->Name());

      if (channel->Vpid()) type = 1;
      else if (channel->Apid(0)) type = 2;
      else continue;

      if (count > 49000) break;
      *(ULONG*)&sendBuffer[count] = htonl(channel->Number());
      count += 4;

      *(ULONG*)&sendBuffer[count] = htonl(type);
      count += 4;

      point = (char*)channel->Name();
      strcpy((char*)&sendBuffer[count], point);
      count += strlen(point) + 1;
    }
  }
  *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field

  log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));

  tcp.sendPacket(sendBuffer, count);
  delete[] sendBuffer;
  log->log("Client", Log::DEBUG, "Written channels list");

  return 1;
}

int MVPClient::processGetChannelPids(UCHAR* data, int length)
{
  ULONG channelNumber = ntohl(*(ULONG*)data);

  cChannel* channel = channelFromNumber(channelNumber);
  if (!channel)
  {
    sendULONG(0);
    return 1;
  }

  ULONG numApids = 0;
  ULONG spaceRequired = 12; // 4 for length field, 4 for vpid, 4 for number of apids
  // Work out space required and number of Apids


  for (const int *Apid = channel->Apids(); *Apid; Apid++)
  {
    spaceRequired += 4 + strlen(channel->Alang(numApids)) + 1; // 4 for pid, length of string + \0
    numApids++;
  }


  // Format of response
  // vpid
  // number of apids
  // {
  //    apid
  //    lang string
  // }

  UCHAR* sendBuffer = new UCHAR[spaceRequired];
  ULONG point = 0;
  *(ULONG*)&sendBuffer[point] = htonl(spaceRequired - 4);   point += 4;   // take off first 4 bytes
  *(ULONG*)&sendBuffer[point] = htonl(channel->Vpid());     point += 4;
  *(ULONG*)&sendBuffer[point] = htonl(numApids);            point += 4;

  for (ULONG i = 0; i < numApids; i++)
  {
    *(ULONG*)&sendBuffer[point] = htonl(channel->Apid(i));  point += 4;
    strcpy((char*)&sendBuffer[point], channel->Alang(i));   point += strlen(channel->Alang(i)) + 1;
  }

//  printf("About to send getchannelpids response. length = %u\n", spaceRequired);
  tcp.dump(sendBuffer, spaceRequired);

  tcp.sendPacket(sendBuffer, spaceRequired);
  delete[] sendBuffer;
  log->log("Client", Log::DEBUG, "Written channels pids");

  return 1;
}

int MVPClient::processStartStreamingChannel(UCHAR* data, int length)
{
  log->log("Client", Log::DEBUG, "length = %i", length);
  ULONG channelNumber = ntohl(*(ULONG*)data);

  cChannel* channel = channelFromNumber(channelNumber);
  if (!channel)
  {
    sendULONG(0);
    return 1;
  }

  // get the priority we should use
  int fail = 1;
  int priority = config.getValueLong("General", "Live priority", &fail);
  if (!fail)
  {
    log->log("Client", Log::DEBUG, "Config: Live TV priority: %i", priority);
  }
  else
  {
    log->log("Client", Log::DEBUG, "Config: Live TV priority config fail");
    priority = 0;
  }

  // a bit of sanity..
  if (priority < 0) priority = 0;
  if (priority > 99) priority = 99;

  log->log("Client", Log::DEBUG, "Using live TV priority %i", priority);
  lp = MVPReceiver::create(channel, priority);

  if (!lp)
  {
    sendULONG(0);
    return 1;
  }

  if (!lp->init())
  {
    delete lp;
    lp = NULL;
    sendULONG(0);
    return 1;
  }

  sendULONG(1);
  return 1;
}

int MVPClient::processStopStreaming(UCHAR* data, int length)
{
  log->log("Client", Log::DEBUG, "STOP STREAMING RECEIVED");
  if (lp)
  {
    delete lp;
    lp = NULL;
  }
  else if (rp)
  {
    writeResumeData();

    delete rp;
    delete recordingManager;
    rp = NULL;
    recordingManager = NULL;
  }

  sendULONG(1);
  return 1;
}

int MVPClient::processGetBlock(UCHAR* data, int length)
{
  if (!lp && !rp)
  {
    log->log("Client", Log::DEBUG, "Get block called when no streaming happening!");
    return 0;
  }

  ULLONG position = ntohll(*(ULLONG*)data);
  data += sizeof(ULLONG);
  ULONG amount = ntohl(*(ULONG*)data);

  log->log("Client", Log::DEBUG, "getblock pos = %llu length = %lu", position, amount);

  UCHAR sendBuffer[amount + 4];
  ULONG amountReceived = 0; // compiler moan.
  if (lp)
  {
    log->log("Client", Log::DEBUG, "getting from live");
    amountReceived = lp->getBlock(&sendBuffer[4], amount);

    if (!amountReceived)
    {
      // vdr has possibly disconnected the receiver
      log->log("Client", Log::DEBUG, "VDR has disconnected the live receiver");
      delete lp;
      lp = NULL;
    }
  }
  else if (rp)
  {
    log->log("Client", Log::DEBUG, "getting from recording");
    amountReceived = rp->getBlock(&sendBuffer[4], position, amount);
  }

  if (!amountReceived)
  {
    sendULONG(0);
    log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
  }
  else
  {
    *(ULONG*)&sendBuffer[0] = htonl(amountReceived);
    tcp.sendPacket(sendBuffer, amountReceived + 4);
    log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
  }

  return 1;
}

int MVPClient::processStartStreamingRecording(UCHAR* data, int length)
{
  // data is a pointer to the fileName string

  recordingManager = new cRecordings;
  recordingManager->Load();

  cRecording* recording = recordingManager->GetByName((char*)data);

  log->log("Client", Log::DEBUG, "recording pointer %p", recording);

  if (recording)
  {
    rp = new RecPlayer(recording);

    UCHAR sendBuffer[16];
    *(ULONG*)&sendBuffer[0] = htonl(12);
    *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
    *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());

    tcp.sendPacket(sendBuffer, 16);
    log->log("Client", Log::DEBUG, "written totalLength");
  }
  else
  {
    delete recordingManager;
    recordingManager = NULL;
  }
  return 1;
}

int MVPClient::processPositionFromFrameNumber(UCHAR* data, int length)
{
  ULLONG retval = 0;

  ULONG frameNumber = ntohl(*(ULONG*)data);
  data += 4;

  if (!rp)
  {
    log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
  }
  else
  {
    retval = rp->positionFromFrameNumber(frameNumber);
  }

  UCHAR sendBuffer[12];
  *(ULONG*)&sendBuffer[0] = htonl(8);
  *(ULLONG*)&sendBuffer[4] = htonll(retval);

  tcp.sendPacket(sendBuffer, 12);
  log->log("Client", Log::DEBUG, "Wrote posFromFrameNum reply to client");
  return 1;
}

int MVPClient::processFrameNumberFromPosition(UCHAR* data, int length)
{
  ULONG retval = 0;

  ULLONG position = ntohll(*(ULLONG*)data);
  data += 8;

  if (!rp)
  {
    log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
  }
  else
  {
    retval = rp->frameNumberFromPosition(position);
  }

  UCHAR sendBuffer[8];
  *(ULONG*)&sendBuffer[0] = htonl(4);
  *(ULONG*)&sendBuffer[4] = htonl(retval);

  tcp.sendPacket(sendBuffer, 8);
  log->log("Client", Log::DEBUG, "Wrote frameNumFromPos reply to client");
  return 1;
}

int MVPClient::processGetIFrame(UCHAR* data, int length)
{
  bool success = false;

  ULONG frameNumber = ntohl(*(ULONG*)data);
  data += 4;
  ULONG direction = ntohl(*(ULONG*)data);
  data += 4;

  ULLONG rfilePosition = 0;
  ULONG rframeNumber = 0;
  ULONG rframeLength = 0;

  if (!rp)
  {
    log->log("Client", Log::DEBUG, "GetIFrame recording called when no recording being played!");
  }
  else
  {
    success = rp->getNextIFrame(frameNumber, direction, &rfilePosition, &rframeNumber, &rframeLength);
  }

  // returns file position, frame number, length

  UCHAR sendBuffer[20];
  int packetLength;

  if (success)
  {
    packetLength = 20;
    *(ULONG*)&sendBuffer[0] = htonl(16);
    *(ULLONG*)&sendBuffer[4] = htonll(rfilePosition);
    *(ULONG*)&sendBuffer[12] = htonl(rframeNumber);
    *(ULONG*)&sendBuffer[16] = htonl(rframeLength);
  }
  else
  {
    packetLength = 8;
    *(ULONG*)&sendBuffer[0] = htonl(4);
    *(ULONG*)&sendBuffer[4] = 0;
  }

  log->log("Client", Log::DEBUG, "%llu %lu %lu", rfilePosition, rframeNumber, rframeLength);

  tcp.sendPacket(sendBuffer, packetLength);
  log->log("Client", Log::DEBUG, "Wrote GNIF reply to client");
  return 1;
}

int MVPClient::processGetChannelSchedule(UCHAR* data, int length)
{
  ULONG channelNumber = ntohl(*(ULONG*)data);
  data += 4;
  ULONG startTime = ntohl(*(ULONG*)data);
  data += 4;
  ULONG duration = ntohl(*(ULONG*)data);

  log->log("Client", Log::DEBUG, "get schedule called for channel %lu", channelNumber);

  cChannel* channel = channelFromNumber(channelNumber);
  if (!channel)
  {
    sendULONG(0);
    log->log("Client", Log::DEBUG, "written 0 because channel = NULL");
    return 1;
  }

  log->log("Client", Log::DEBUG, "Got channel");

  cSchedulesLock MutexLock;
  const cSchedules *Schedules = cSchedules::Schedules(MutexLock);
  if (!Schedules)
  {
    sendULONG(0);
    log->log("Client", Log::DEBUG, "written 0 because Schedule!s! = NULL");
    return 1;
  }

  log->log("Client", Log::DEBUG, "Got schedule!s! object");

  const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
  if (!Schedule)
  {
    sendULONG(0);
    log->log("Client", Log::DEBUG, "written 0 because Schedule = NULL");
    return 1;
  }

  log->log("Client", Log::DEBUG, "Got schedule object");

  UCHAR* sendBuffer = (UCHAR*)malloc(100000);
  ULONG sendBufferLength = 100000;
  ULONG sendBufferUsed = sizeof(ULONG); // leave a hole for the entire packet length

  const char* empty = "";

  // assign all the event info to temp vars then we know exactly what size they are
  ULONG thisEventID;
  ULONG thisEventTime;
  ULONG thisEventDuration;
  const char* thisEventTitle;
  const char* thisEventSubTitle;
  const char* thisEventDescription;

  ULONG constEventLength = sizeof(thisEventID) + sizeof(thisEventTime) + sizeof(thisEventDuration);
  ULONG thisEventLength;


  for (const cEvent* event = Schedule->Events()->First(); event; event = Schedule->Events()->Next(event))
  {
    thisEventID = event->EventID();
    thisEventTime = event->StartTime();
    thisEventDuration = event->Duration();
    thisEventTitle = event->Title();
    thisEventSubTitle = NULL;
    thisEventDescription = event->Description();


    log->log("Client", Log::DEBUG, "Got an event object %p", event);

    //in the past filter
    if ((thisEventTime + thisEventDuration) < (ULONG)time(NULL)) continue;

    //start time filter
    if ((thisEventTime + thisEventDuration) <= startTime) continue;

    //duration filter
    if (thisEventTime >= (startTime + duration)) continue;

    if (!thisEventTitle) thisEventTitle = empty;
    if (!thisEventSubTitle) thisEventSubTitle = empty;
    if (!thisEventDescription) thisEventDescription = empty;

    thisEventLength = constEventLength + strlen(thisEventTitle) + 1 + strlen(thisEventSubTitle) + 1 + strlen(thisEventDescription) + 1;

    log->log("Client", Log::DEBUG, "Done s1");

    // now extend the buffer if necessary
    if ((sendBufferUsed + thisEventLength) > sendBufferLength)
    {
      log->log("Client", Log::DEBUG, "Extending buffer");
      sendBufferLength += 100000;
      UCHAR* temp = (UCHAR*)realloc(sendBuffer, sendBufferLength);
      if (temp == NULL)
      {
        free(sendBuffer);
        UCHAR sendBuffer2[8];
        *(ULONG*)&sendBuffer2[0] = htonl(4);
        *(ULONG*)&sendBuffer2[4] = htonl(0);
        tcp.sendPacket(sendBuffer2, 8);
        log->log("Client", Log::DEBUG, "written 0 because failed to realloc packet");
        return 1;
      }
      sendBuffer = temp;
    }

    log->log("Client", Log::DEBUG, "Done s2");

    *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventID);       sendBufferUsed += sizeof(ULONG);
    *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventTime);     sendBufferUsed += sizeof(ULONG);
    *(ULONG*)&sendBuffer[sendBufferUsed] = htonl(thisEventDuration); sendBufferUsed += sizeof(ULONG);

    strcpy((char*)&sendBuffer[sendBufferUsed], thisEventTitle);       sendBufferUsed += strlen(thisEventTitle) + 1;
    strcpy((char*)&sendBuffer[sendBufferUsed], thisEventSubTitle);    sendBufferUsed += strlen(thisEventSubTitle) + 1;
    strcpy((char*)&sendBuffer[sendBufferUsed], thisEventDescription); sendBufferUsed += strlen(thisEventDescription) + 1;

    log->log("Client", Log::DEBUG, "Done s3 %lu", sendBufferUsed);
  }

  log->log("Client", Log::DEBUG, "Got all event data");

  if (sendBufferUsed == sizeof(ULONG))
  {
    // No data
    sendULONG(0);
    log->log("Client", Log::DEBUG, "Written 0 because no data");
  }
  else
  {
    // Write the length into the first 4 bytes. It's sendBufferUsed - 4 because of the hole!
    *(ULONG*)&sendBuffer[0] = htonl(sendBufferUsed - sizeof(ULONG));
    tcp.sendPacket(sendBuffer, sendBufferUsed);
    log->log("Client", Log::DEBUG, "written %lu schedules packet", sendBufferUsed);
  }

  free(sendBuffer);

  return 1;
}

#endif

int MVPClient::processConfigSave(UCHAR* buffer, int length)
{
  char* section = (char*)buffer;
  char* key = NULL;
  char* value = NULL;

  for (int k = 0; k < length; k++)
  {
    if (buffer[k] == '\0')
    {
      if (!key)
      {
        key = (char*)&buffer[k+1];
      }
      else
      {
        value = (char*)&buffer[k+1];
        break;
      }
    }
  }

  // if the last string (value) doesnt have null terminator, give up
  if (buffer[length - 1] != '\0') return 0;

  log->log("Client", Log::DEBUG, "Config save: %s %s %s", section, key, value);
  if (config.setValueString(section, key, value))
  {
    sendULONG(1);
  }
  else
  {
    sendULONG(0);
  }

  return 1;
}

int MVPClient::processConfigLoad(UCHAR* buffer, int length)
{
  char* section = (char*)buffer;
  char* key = NULL;

  for (int k = 0; k < length; k++)
  {
    if (buffer[k] == '\0')
    {
      key = (char*)&buffer[k+1];
      break;
    }
  }

  char* value = config.getValueString(section, key);

  if (value)
  {
    UCHAR sendBuffer[4 + strlen(value) + 1];
    *(ULONG*)&sendBuffer[0] = htonl(strlen(value) + 1);
    strcpy((char*)&sendBuffer[4], value);
    tcp.sendPacket(sendBuffer, 4 + strlen(value) + 1);

    log->log("Client", Log::DEBUG, "Written config load packet");
    delete[] value;
  }
  else
  {
    UCHAR sendBuffer[8];
    *(ULONG*)&sendBuffer[0] = htonl(4);
    *(ULONG*)&sendBuffer[4] = htonl(0);
    tcp.sendPacket(sendBuffer, 8);

    log->log("Client", Log::DEBUG, "Written config load failed packet");
  }

  return 1;
}
#ifndef VOMPSTANDALONE
void MVPClient::cleanConfig()
{
  log->log("Client", Log::DEBUG, "Clean config");

  cRecordings Recordings;
  Recordings.Load();

  int numReturns;
  int length;
  char* resumes = config.getSectionKeyNames("ResumeData", numReturns, length);
  char* position = resumes;
  for(int k = 0; k < numReturns; k++)
  {
    log->log("Client", Log::DEBUG, "EXAMINING: %i %i %p %s", k, numReturns, position, position);

    cRecording* recording = Recordings.GetByName(position);
    if (!recording)
    {
      // doesn't exist anymore
      log->log("Client", Log::DEBUG, "Found a recording that doesn't exist anymore");
      config.deleteValue("ResumeData", position);
    }
    else
    {
      log->log("Client", Log::DEBUG, "This recording still exists");
    }

    position += strlen(position) + 1;
  }

  delete[] resumes;
}


int MVPClient::processGetTimers(UCHAR* buffer, int length)
{
  UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
  int count = 4; // leave space for the packet length

  const char* fileName;
  cTimer *timer;
  int numTimers = Timers.Count();

  *(ULONG*)&sendBuffer[count] = htonl(numTimers);    count += 4;

  for (int i = 0; i < numTimers; i++)
  {
    if (count > 49000) break;

    timer = Timers.Get(i);

    *(ULONG*)&sendBuffer[count] = htonl(timer->HasFlags(tfActive));       count += 4;
    *(ULONG*)&sendBuffer[count] = htonl(timer->Recording());              count += 4;
    *(ULONG*)&sendBuffer[count] = htonl(timer->Pending());                count += 4;
    *(ULONG*)&sendBuffer[count] = htonl(timer->Priority());               count += 4;
    *(ULONG*)&sendBuffer[count] = htonl(timer->Lifetime());               count += 4;
    *(ULONG*)&sendBuffer[count] = htonl(timer->Channel()->Number());      count += 4;
    *(ULONG*)&sendBuffer[count] = htonl(timer->StartTime());              count += 4;
    *(ULONG*)&sendBuffer[count] = htonl(timer->StopTime());               count += 4;
    *(ULONG*)&sendBuffer[count] = htonl(timer->Day());                    count += 4;
    *(ULONG*)&sendBuffer[count] = htonl(timer->WeekDays());               count += 4;

    fileName = timer->File();
    strcpy((char*)&sendBuffer[count], fileName);
    count += strlen(fileName) + 1;
  }

  *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field

  log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));

  tcp.sendPacket(sendBuffer, count);
  delete[] sendBuffer;
  log->log("Client", Log::DEBUG, "Written timers list");

  return 1;
}

int MVPClient::processSetTimer(UCHAR* buffer, int length)
{
  char* timerString = new char[strlen((char*)buffer) + 1];
  strcpy(timerString, (char*)buffer);

  cTimer *timer = new cTimer;
  if (timer->Parse((char*)timerString))
  {
    cTimer *t = Timers.GetTimer(timer);
    if (!t)
    {
      Timers.Add(timer);
      Timers.SetModified();
      sendULONG(0);
      return 1;
    }
    else
    {
      sendULONG(1);
    }
  }
  else
  {
     sendULONG(2);
  }
  delete timer;
  return 1;
}

int MVPClient::processVDRShutdown(UCHAR* buffer, int length)
{
  decClients();
  cRemote::Put(kPower);
  incClients();
  return 1;
}
    
void MVPClient::incClients()
{
  pthread_mutex_lock(&threadClientMutex);
  MVPClient::nr_clients++;
  pthread_mutex_unlock(&threadClientMutex);
}

void MVPClient::decClients()
{
  pthread_mutex_lock(&threadClientMutex);
  MVPClient::nr_clients--;
  pthread_mutex_unlock(&threadClientMutex);
}

int MVPClient::getNrClients()
{
  int nrClients;
  pthread_mutex_lock(&threadClientMutex);
  nrClients = MVPClient::nr_clients;
  pthread_mutex_unlock(&threadClientMutex);
  return nrClients;
}

int MVPClient::processGetRecInfo(UCHAR* data, int length)
{
  // data is a pointer to the fileName string

  cRecordings Recordings;
  Recordings.Load(); // probably have to do this

  cRecording *recording = Recordings.GetByName((char*)data);

  time_t timerStart = 0;
  time_t timerStop = 0;
  char* summary = NULL;
  ULONG resumePoint = 0;

  if (!recording)
  {
    log->log("Client", Log::ERR, "GetRecInfo found no recording");
    sendULONG(0);
    return 1;
  }

  ULONG sendBufferSize = 10000;
  UCHAR* sendBuffer = (UCHAR*)malloc(sendBufferSize);
  ULONG pos = 4; // leave first 4 bytes for size field


  /* Return packet:
  4 bytes: start time for timer
  4 bytes: end time for timer
  4 bytes: resume point
  string: summary
  4 bytes: num components
  {
    1 byte: stream
    1 byte: type
    string: language
    string: description
  }

  */

  // Get current timer

  cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName());
  if (rc)
  {
    timerStart = rc->Timer()->StartTime();
    timerStop = rc->Timer()->StopTime();
    log->log("Client", Log::DEBUG, "GRI: RC: %lu %lu", timerStart, timerStop);
  }

  *(time_t*)&sendBuffer[pos] = htonl(timerStart);    pos += 4;
  *(time_t*)&sendBuffer[pos] = htonl(timerStop);     pos += 4;

  // Get resume point

  char* value = config.getValueString("ResumeData", (char*)data);
  if (value)
  {
    resumePoint = strtoul(value, NULL, 10);
    delete[] value;
  }
  log->log("Client", Log::DEBUG, "GRI: RP: %lu", resumePoint);

  *(ULONG*)&sendBuffer[pos] = htonl(resumePoint);    pos += 4;


  // Get summary

  const cRecordingInfo *Info = recording->Info();
  summary = (char*)Info->ShortText();
  if (isempty(summary)) summary = (char*)Info->Description();
  log->log("Client", Log::DEBUG, "GRI: S: %s", summary);
  if (summary)
  {
    // memory insanity...
    if ((sendBufferSize - pos) < (strlen(summary) + 500)) // random
    {
      UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + strlen(summary) + 10000);
      if (newBuffer)
      {
        sendBuffer = newBuffer;
        sendBufferSize += strlen(summary) + 10000;
      }
      else
      {
        free(sendBuffer);
        sendULONG(0);
        return 1;
      }
    }

    strcpy((char*)&sendBuffer[pos], summary);
    pos += strlen(summary) + 1;
  }
  else
  {
    strcpy((char*)&sendBuffer[pos], "");
    pos += 1;
  }


  // Get channels

  const cComponents* components = Info->Components();

  log->log("Client", Log::DEBUG, "GRI: D1: %p", components);

  if (!components)
  {
    *(ULONG*)&sendBuffer[pos] = htonl(0);    pos += 4;
  }
  else
  {
    *(ULONG*)&sendBuffer[pos] = htonl(components->NumComponents());    pos += 4;

    tComponent* component;
    for (int i = 0; i < components->NumComponents(); i++)
    {
      component = components->Component(i);

      // memory insanity...
      ULONG extraNeeded = 2 + (component->language ? strlen(component->language) : 0)
                            + (component->description ? strlen(component->description) : 0) + 2;

      if ((sendBufferSize - pos) < extraNeeded)
      {
        UCHAR* newBuffer = (UCHAR*)realloc(sendBuffer, sendBufferSize + extraNeeded + 10000);
        if (newBuffer)
        {
          sendBuffer = newBuffer;
          sendBufferSize += extraNeeded + 10000;
        }
        else
        {
          free(sendBuffer);
          sendULONG(0);
          return 1;
        }
      }

      log->log("Client", Log::DEBUG, "GRI: C: %i %u %u %s %s", i, component->stream, component->type, component->language, component->description);
      sendBuffer[pos] = component->stream;  pos += 1;
      sendBuffer[pos] = component->type;    pos += 1;
      if (component->language)
      {
        strcpy((char*)&sendBuffer[pos], component->language);
        pos += strlen(component->language) + 1;
      }
      else
      {
        strcpy((char*)&sendBuffer[pos], "");
        pos += 1;
      }
      if (component->description)
      {
        strcpy((char*)&sendBuffer[pos], component->description);
        pos += strlen(component->description) + 1;
      }
      else
      {
        strcpy((char*)&sendBuffer[pos], "");
        pos += 1;
      }

    }
  }


  // Done. send it

  *(ULONG*)&sendBuffer[0] = htonl(pos - 4); // -4 :  take off the size field

  log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));

  tcp.sendPacket(sendBuffer, pos);
  delete[] sendBuffer;
  log->log("Client", Log::DEBUG, "Written getrecinfo");

  return 1;
}




// FIXME obselete

int MVPClient::processReScanRecording(UCHAR* data, int length)
{
  if (!rp)
  {
    log->log("Client", Log::DEBUG, "Rescan recording called when no recording being played!");
    return 0;
  }

  rp->scan();

  UCHAR sendBuffer[16];
  *(ULONG*)&sendBuffer[0] = htonl(12);
  *(ULLONG*)&sendBuffer[4] = htonll(rp->getLengthBytes());
  *(ULONG*)&sendBuffer[12] = htonl(rp->getLengthFrames());

  tcp.sendPacket(sendBuffer, 16);
  log->log("Client", Log::DEBUG, "Rescan recording, wrote new length to client");
  return 1;
}

// FIXME without client calling rescan, getblock wont work even tho more data is avail


int MVPClient::processGetMarks(UCHAR* data, int length)
{
  // data is a pointer to the fileName string

  UCHAR* sendBuffer = new UCHAR[50000]; // FIXME hope this is enough
  int count = 4; // leave space for the packet length


  cMarks Marks;
  cRecordings Recordings;
  Recordings.Load(); // probably have to do this

  cRecording *recording = Recordings.GetByName((char*)data);

  log->log("Client", Log::DEBUG, "recording pointer %p", recording);

  if (recording)
  {
    Marks.Load(recording->FileName());
    if (Marks.Count())
    {
      for (const cMark *m = Marks.First(); m; m = Marks.Next(m))
      {
        log->log("Client", Log::DEBUG, "found Mark %i", m->position);

        if (count > 49000) break;
        *(ULONG*)&sendBuffer[count] = htonl(m->position);
        count += 4;
      }
    }
    else
    {
      log->log("Client", Log::DEBUG, "no marks found, sending 0-mark");
      *(ULONG*)&sendBuffer[count] = htonl(0);
      count += 4;
    }
  }

  *(ULONG*)&sendBuffer[0] = htonl(count - 4); // -4 :  take off the size field

  log->log("Client", Log::DEBUG, "recorded size as %u", ntohl(*(ULONG*)&sendBuffer[0]));

  tcp.sendPacket(sendBuffer, count);
  delete[] sendBuffer;
  log->log("Client", Log::DEBUG, "Written Marks list");

  return 1;
}

#endif //VOMPSTANDALONE
//helper for sending from a serialize buffer
//insert the used len into the first 4 Bytes of the buffer
void MVPClient::sendPacket(SerializeBuffer *b) {
  UCHAR buf[b->getCurrent()-b->getStart()+4];
  memcpy(&buf[4],b->getStart(),b->getCurrent()-b->getStart());
  *(ULONG*)&buf[0]=htonl(b->getCurrent()-b->getStart());
  tcp.sendPacket(buf,b->getCurrent()-b->getStart()+4);
}

/**
  * media List Request:
  * Media List response:
  * flags, mediaList
*/
#define MLISTBUF 500000
int MVPClient::processGetMediaList(UCHAR* data, int length)
{
  SerializeBuffer buffer(data,length);
  MediaURI uri(0,NULL,NULL);
  VDR_GetMediaListRequest request(&uri);
  if (request.deserialize(&buffer) != 0) {
    log->log("Client", Log::ERR, "getMediaList unable to deserialize");
    return 0;
  }
  const char *dirname=uri.getName();
  log->log("Client", Log::DEBUG, "getMediaList for %s", dirname);

  MediaList * ml=NULL;
  if (dirname == NULL) {
    ml=media->getRootList();
  } else {
    ml=media->getMediaList(&uri);
  }
  if (ml == NULL) {
     log->log("Client", Log::ERR, "getMediaList returned NULL");
     return 0;
  }
  SerializeBuffer rbuf(MLISTBUF,false,true);
  ULONG flags=0; //TODO: real error handling by setting flags
  VDR_GetMediaListResponse response(&flags,ml);
  if (response.serialize(&rbuf) != 0) {
     log->log("Client", Log::ERR, "getMediaList returned NULL");
     return 0;
  }
  log->log("Client", Log::DEBUG, "getMediaList size  %u", ml->size());
  delete ml;
  

  sendPacket(&rbuf);
  log->log("Client", Log::DEBUG, "Written Media list");
  return 1;
}
/**
  * openMedia Request:
  * openMedia response:
*/
int MVPClient::processOpenMedia(UCHAR* data, int length)
{
  SerializeBuffer buffer(data,length);
  MediaURI uri(0,NULL,NULL);
  ULONG channel=0;
  ULONG xs=0;
  ULONG ys=0;
  VDR_OpenMediumRequest request(&channel,&uri,&xs,&ys);
  if (request.deserialize(&buffer) != 0) {
    log->log("Client", Log::ERR, "openMediaRequest unable to deserialize");
    return 0;
  }
  const char *name=uri.getName();
  log->log("Client", Log::DEBUG, "openMediaRequest for %s", name);
  ULLONG size=0;
  int rt=media->openMedium(channel,&uri,&size,xs,ys);
  ULONG flags=0;
  if (rt != 0) {
    size=0;
    flags=1;
    log->log("Client", Log::ERR, "openMediaRequest unable to open");
  }
  VDR_OpenMediumResponse response(&flags,&size);
  SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
  if (response.serialize(&rbuf) != 0) {
     log->log("Client", Log::ERR, "openMediaRequest cannot serialize");
     return 0;
  }
  log->log("Client", Log::DEBUG, "openMediaRequest size  %llu", size);
  sendPacket(&rbuf);
  return 1;
}
/**
  * VDR_GETMEDIABLOCK
  * resp
  * packet - no serialized response!
  */
int MVPClient::processGetMediaBlock(UCHAR* data, int length)
{
  SerializeBuffer buffer(data,length);
  ULLONG position = 0;
  ULONG amount = 0;
  ULONG channel = 0;
  VDR_GetMediaBlockRequest request(&channel,&position,&amount);
  if (request.deserialize(&buffer) != 0) {
    log->log("Client", Log::ERR, "getMediaBlock unable to deserialize");
    return 0;
  }
  log->log("Client", Log::DEBUG, "getMediaBlock pos = %llu length = %lu,chan=%lu", position, amount,channel);

  UCHAR sendBuffer[amount +4];
  UCHAR *sendptr=&sendBuffer[4];
  ULONG amountReceived = 0; 
  int rt=media->getMediaBlock(channel,position,amount,&amountReceived,&sendptr);
  if (!amountReceived || rt != 0)
  {
    log->log("Client", Log::DEBUG, "written 4(0) as getblock got 0");
  }
  else
  {
    if (sendptr != &sendBuffer[4]) {
      //the provider did not use the optimized handling with using my buffer
      memcpy(&sendBuffer[4],sendBuffer,amountReceived);
      free(sendptr);
    } else {
      // the provider did not allocate a new buffer
      ;
    }
  }
  *(ULONG *)(&sendBuffer[0])=htonl(amountReceived);
  tcp.sendPacket(sendBuffer,amountReceived+4);
  log->log("Client", Log::DEBUG, "written ok %lu", amountReceived);
  return 1;
}
/**
  * VDR_GETMEDIAINFO
  */

int MVPClient::processGetMediaInfo(UCHAR* data, int length)
{
  SerializeBuffer buffer(data,length);
  ULONG channel=0;
  VDR_GetMediaInfoRequest request(&channel);
  if (request.deserialize(&buffer) != 0) {
    log->log("Client", Log::ERR, "getMediaInfo unable to deserialize");
    return 0;
  }
  log->log("Client", Log::DEBUG, "getMediaInfo chan=%lu", channel);
  ULONG flags=0;
  MediaInfo mi;
  int rt=media->getMediaInfo(channel,&mi);
  if (rt != 0) {
    flags=1;
    log->log("Client", Log::ERR, "getMediaInfo unable to get");
  }
  VDR_GetMediaInfoResponse response(&flags,&mi);
  SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
  if (response.serialize(&rbuf) != 0) {
     log->log("Client", Log::ERR, "getMediaInfo cannot serialize");
     return 0;
  }
  sendPacket(&rbuf);
  return 1;
}

/**
  * VDR_CLOSECHANNEL
  */


int MVPClient::processCloseMediaChannel(UCHAR* data, int length)
{
  SerializeBuffer buffer(data,length);
  ULONG channel=0;
  VDR_CloseMediaChannelRequest request(&channel);
  if (request.deserialize(&buffer) != 0) {
    log->log("Client", Log::ERR, "closeMediaChannel unable to deserialize");
    return 0;
  }
  ULONG flags=0;
  log->log("Client", Log::DEBUG, "closeMediaChannel chan=%lu", channel);
  int rt=media->closeMediaChannel(channel);
  if (rt != 0) {
    flags=1;
    log->log("Client", Log::ERR, "closeMediaChannel unable to get");
  }
  VDR_CloseMediaChannelResponse response(&flags);
  SerializeBuffer rbuf(response.getSerializedLen()+4,false,true);
  if (response.serialize(&rbuf) != 0) {
     log->log("Client", Log::ERR, "closeMediaChannel cannot serialize");
     return 0;
  }
  sendPacket(&rbuf);
  return 1;
}



#ifndef VOMPSTANDALONE
int MVPClient::processDeleteTimer(UCHAR* buffer, int length)
{
  log->log("Client", Log::DEBUG, "Delete timer called");
  // get timer
  
  int position = 0;
  
  INT delChannel = ntohl(*(ULONG*)&buffer[position]); position += 4;
  INT delWeekdays = ntohl(*(ULONG*)&buffer[position]); position += 4;
  INT delDay = ntohl(*(ULONG*)&buffer[position]); position += 4;  
  INT delStart = ntohl(*(ULONG*)&buffer[position]); position += 4;  
  INT delStop = ntohl(*(ULONG*)&buffer[position]); position += 4;
    
  cTimer* ti = NULL;
  for (ti = Timers.First(); ti; ti = Timers.Next(ti))
  {
    if  ( (ti->Channel()->Number() == delChannel)
     &&   ((ti->WeekDays() && (ti->WeekDays() == delWeekdays)) || (!ti->WeekDays() && (ti->Day() == delDay)))
     &&   (ti->StartTime() == delStart)
     &&   (ti->StopTime() == delStop) )
       break;
  }
  
  if (!ti)
  {
    sendULONG(4);
    return 1;
  }
          
  if (!Timers.BeingEdited())
  {
    if (!ti->Recording())
    {
      Timers.Del(ti);
      Timers.SetModified();
      sendULONG(10);
      return 1;
    }
    else
    {
      log->log("Client", Log::ERR, "Unable to delete timer - timer is running");
      sendULONG(3);
      return 1;
    }  
  }
  else
  {
    log->log("Client", Log::ERR, "Unable to delete timer - timers being edited at VDR");
    sendULONG(1);
    return 1;
  }  
}

#endif
