#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "MediaStreamProducer.h"
#include "MediaClient.h"

#ifdef WIN32
#include <windows.h>
#endif

using namespace videonext;
using namespace videonext::media;

time_t start_time;

std::string state_to_str(STREAM_STATE state)
{
   std::string state_str;
   switch (state)
   {
      case IDLE:
         state_str="IDLE";
         break;
      case PLAY_FROM_SERVER:
         state_str="PLAY_FROM_SERVER";
         break;
      case PLAY_FROM_BUFFER:
         state_str="PLAY_FROM_BUFFER";
         break;
      case STOPPED:
         state_str="STOPPED";
         break;
      case CONNECTING:
         state_str="CONNECTING";
         break;
      default:
         state_str="UNKNOWN";
   }

   return state_str;
}


class SimpleMediaPlayer : public MediaStreamHandler
{
public:
   SimpleMediaPlayer(const std::string &masterHost, 
                     unsigned int masterPort,
                     const std::string &user,
                     const std::string &password,
                     unsigned objID)
      : masterHost_(masterHost), masterPort_(masterPort), 
        user_(user), password_(password), objID_(objID), producer_(0)
   {}

   virtual ~SimpleMediaPlayer()
   {}

   void startPlaying(ResultStatus *result)
   {
      std::string url;

      // Logging in...
      MediaClient::instance()->login(result, masterHost_, masterPort_, user_, password_);
      if (result->errorCode != 0) return;

      // Getting URL
      MediaClient::instance()->getMediaURL(result, &url, objID_);
      if (result->errorCode != 0) return;

      // Creating new instance of MediaStremProducer and spawn playback thread
      // NOTE: you never delete that instance. Use teardown() instead.
      producer_ = new MediaStreamProducer(this, url, 0, 0, true, 0);

#ifdef WIN32
      HANDLE hndl = CreateThread(NULL, 0, &SimpleMediaPlayer::play, producer_, 0, NULL);
      CloseHandle(hndl);
#else
      pthread_t thr;
      pthread_create(&thr, 0, SimpleMediaPlayer::play, producer_);
      pthread_detach(thr);
#endif
   }

   void stopPlaying()
   {
      if (producer_)
         producer_->teardown();
   }

   virtual void newFrame(const Frame &frame, const CacheBoundaries &bounds)
   {
     

      printf("New frame---\n");   
      printf("Data Size: %d, time: %ld.%ld\n", frame.dataSize[0], (unsigned long)frame.capturedTime->tv_sec, (unsigned long)frame.capturedTime->tv_usec);
/*
      printf("Frame Dimensions: %dx%d\n", frame.width, frame.height);
      printf("Cache boundaries: %d.%d  .. %d.%d\n", bounds.begin.tv_sec,  bounds.begin.tv_usec,  bounds.end.tv_sec,  bounds.end.tv_usec);
      printf("---\n");   
*/
   }
   
   virtual void bufferChanged(const CacheBoundaries &bounds)
   {
      printf("Buffer Changed---\n");   
      printf("Cache boundaries: %ld.%d  .. %ld.%d\n", bounds.begin.tv_sec,  bounds.begin.tv_usec,  bounds.end.tv_sec,  bounds.end.tv_usec);
      printf("---\n");
   }
        
   virtual void stateChanged(STREAM_STATE state, const ResultStatus &status)
   {
      printf("State Changed---\n");   
      printf("State: %d (%s)\n", state, state_to_str(state).c_str());   
      printf("Result status: %d, %s\n", status.errorCode, status.errorString.c_str());
      printf("---\n");   
      if (state == STOPPED) // Restart engine (stream stopped unexpectedly)
      {
         fprintf(stderr, "Restart engine (stream stopped unexpectedly)\n");
         ResultStatus result;
         do
         {
#ifdef WIN32
            Sleep(3000);
#else
            usleep(3000000);
#endif
            result.errorCode = 0;
            startPlaying(&result);
            if (result.errorCode != 0)
            {
               // Oups. something wrong..
               fprintf(stderr, "Could not start playback: %s\n", result.errorString.c_str());
            }

         } while (result.errorCode != 0);
      }
   }
        
   virtual void newStream(const StreamInfo &streamInfo)
   {
      printf("New Stream---\n");
      printf("Type: %d\n", streamInfo.type);   
      printf("Id: %d\n", streamInfo.streamId);
      printf("RTPCodecStr: %s\n", streamInfo.RTPCodecStr.c_str());
      printf("ConfigDataSize: %d\n", streamInfo.configDataSize);
      printf("---\n");   
   }
        
   virtual void removeStream(const StreamInfo &streamInfo) 
   {
      printf("Removed Stream---\n");
      printf("Type: %d\n", streamInfo.type);   
      printf("Id: %d\n", streamInfo.streamId);
      printf("RTPCodecStr: %s\n", streamInfo.RTPCodecStr.c_str());
      printf("---\n");   
   }

private:
#ifdef WIN32
   static DWORD WINAPI play(LPVOID arg)
#else
   static void* play(void* arg)
#endif
   {
      MediaStreamProducer *producer = (MediaStreamProducer*)arg;
      producer->play();
      fprintf(stderr, "Done playing\n");
      return 0;
   }


private:
   std::string masterHost_;
   unsigned int masterPort_;
   std::string user_;
   std::string password_;
   unsigned objID_;

   MediaStreamProducer *producer_;
};
		    

int main(int argc, char *argv[])
{
   if (argc != 6) 
   {
      fprintf(stderr, "Usage: %s masterip port user passwd objid\n", argv[0]);
      return 1;
   }

   start_time = time(0);

   SimpleMediaPlayer *player = new SimpleMediaPlayer(argv[1], atol(argv[2]), argv[3], argv[4], atol(argv[5]));

   ResultStatus result;
   player->startPlaying(&result);
   if (result.errorCode != 0)
   {
      fprintf(stderr, "Could not start playback: %s\n", result.errorString.c_str());
      return 1;
   }

   while (1)
   {
      // Do something usefull
#ifdef WIN32
      Sleep(10000);
#else
      usleep(10000000);
#endif
   }
   
   return 0;
}
