//***************************************************************************
// Group VDR/GraphTFT
// File comthread.c
// Date 28.10.06
// This code is distributed under the terms and conditions of the
// GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
// (c) 2006-2008 Jörg Wendel
//--------------------------------------------------------------------------
// Class ComThread
//***************************************************************************

#include "common.h"
#include "comthread.h"
#include "display.h"

//***************************************************************************
// Object
//***************************************************************************

ComThread::ComThread(void* aDisplay, int width, int height)
   : cRemote("graphtft-fe")
{
   line = new TcpChannel();

   display = aDisplay;
   themeWidth = width;
   themeHeight = height;

   bufferSize = maxBuf;

   buffer = new char[bufferSize+1];
   header = new TcpChannel::Header;

   running = false;
   timeout = 5;
   port = na;
   *host = 0;
   pid = 0;
   jpgQuality = 60;
   width = 720;
   height = 576;
}

ComThread::~ComThread()
{
   if (display)
      ((cGraphTFTDisplay*)display)->clearComThread();

   Stop();

   line->close();

   delete line;
   delete header;
   delete[] buffer;
}

//***************************************************************************
// Init
//***************************************************************************

int ComThread::init(Renderer* aRenderer, unsigned int aPort, const char* aHost)
{
   renderer = aRenderer;

   if (aHost)
      setHost(aHost);

   if (aPort)
      setPort(aPort);

   if (line->open(port) != 0)
   {
      tell(0, "Error: Can't establish listener");
      return fail;
   }

   tell(0, "Listener established!");

   return success;
}

//***************************************************************************
// Stop
//***************************************************************************

void ComThread::Stop() 
{
   if (running) 
   {
      isyslog("GraphTFT plugin try to stop communication thread");
      running = false;
      Cancel(3);
   }
}

//***************************************************************************
// Run
//***************************************************************************

void ComThread::Action()
{
   int status;

   pid = getpid();
   isyslog("GraphTFT plugin tcp communication thread started (pid=%d)", pid);

   running = true;

   while (running)
   {
      if (!line->isConnected())
      {
         if (line->listen() == 0)
         {
            line->write(cGraphTftComService::cmdWelcome);
            tell(1, "Client connection accepted");

            refresh();
         }

         sleep(1);

         continue;
      }

      while (line->isConnected() && running)
      {
         if ((status = line->look(10)) != 0)
         {
            if (status != TcpChannel::wrnNoEventPending)
            {
               tell(1, "Error: Communication problems, closing line!");
               line->close();

               break;
            }
            
            continue;
         }
         
         if (read() != 0)
         {
            line->close();
            tell(1, "Error: Communication problems, closing line!");
         }
      }
   }

   isyslog("GraphTFT plugin tcp communication thread ended (pid=%d)", pid);
}

//***************************************************************************
// Read
//***************************************************************************

int ComThread::read()
{
   int status;
   TcpChannel::Header tmp;

   // es stehen Daten an, erst einmal den Header abholen ..

   if ((status = line->read((char*)&tmp, sizeof(TcpChannel::Header))) == 0)
   {
      header->command = ntohl(tmp.command);
      header->size = ntohl(tmp.size);

      switch (header->command)
      {
         case cGraphTftComService::cmdWelcome:
         {
            tell(1, "Got welcome");
            break;
         }

         case cGraphTftComService::cmdLogout:
         {
            tell(1, "Got logout from client, closing line");
            line->close();

            break;
         }

         case cGraphTftComService::cmdData:
         {
            tell(2, "Got data");
            status = line->read(buffer, header->size);
            break;
         }

         case cGraphTftComService::cmdMouseEvent:
         {
            GraphTftTouchEvent ev;

            status = line->read((char*)&ev, header->size);
            tell(2, "Got mouse event, button (%d) at (%d/%d)", ev.button, ev.x, ev.y); 

            if (ev.flag & ComThread::efKeyboard)
               Put(ev.button);
            else
               ((cGraphTFTDisplay*)display)->mouseEvent(ev.x, ev.y, ev.button, 
                                                        ev.flag, ev.data);

            break;
         }

         case cGraphTftComService::cmdStartCalibration:
         {
            ((cGraphTFTDisplay*)display)->setCalibrate(true);

            break;
         }

         case cGraphTftComService::cmdStopCalibration:
         {
            ((cGraphTFTDisplay*)display)->setCalibrate(false);

            break;
         }

         default:
         {
            tell(eloAlways, "Got unexpected protocol (%d), aborting", header->command); 
            status = fail;

            break;
         }
      }
   }
   
   return status;
}

//***************************************************************************
// Put Key Code
//***************************************************************************

bool ComThread::Put(uint64_t Code, bool Repeat, bool Release)
{ 
   tell(5, "Put key action (%d)", Code);

   return cRemote::Put(Code, Repeat, Release); 
}

//***************************************************************************
// Refresh
//***************************************************************************

int ComThread::refresh()
{
   int size;

   LogDuration ld("ComThread::refresh()", 1);

   if (line->isConnected())
   {

#ifdef HAVE_IMAGE_MAGICK

      const char* p = 0;

      if ((size = renderer->getImageBuffer((const void*&)p, jpgQuality)) > 0)
      {
         tell(2, "Info: %ld kb to write", size/1024);
         
         if (line->write(cGraphTftComService::cmdData, p, size) != 0)
         {
            tell(1, "Error: Communication problems, closing client connection.");
            line->close();
         }
      }

#else

      // Ok, the dirty and slow way :(

      int fd;

      renderer->dumpImage2File("tmp.jpg", themeWidth, themeHeight);
      
      if ((fd = ::open("/tmp/tmp.jpg", O_RDONLY)) < 0)
      {
         tell(1, "Error: Can't open dumped image!");
         return fail;
      }

      size = ::read(fd, buffer, maxBuf);
      
      if (size <= 0 || size == maxBuf)
      {
         tell(1, "Error: Reading of image failed, result was %d."\
           "%s", size, size == maxBuf ? " buffer to smal" : strerror(errno));

         return fail;
      }
      
      tell(2, "Info: %ld kb to write", size/1024);
      
      if (line->write(cGraphTftComService::cmdData, buffer, size) != 0)
      {
         tell(1, "Error: Communication problems, closing client connection.");
         line->close();
      }

      ::close(fd);

#endif

   }

   return success;
}
