/*
 * a simple video client
 * by Hoang Nguyen (hnguyen5@illinois.edu)
 * 
 * Note:
 * No buffering/sync/rate control mechanism is used.
 * The client simply receives frames from the server, decodes and displays
 * TCP is used.
 *
 * Feel free to use/modify this code
 * at YOUR OWN RISK
 *
 * Protocol: 
 * 1) Server reads the avi file, setup AVCodecContext
 * 2) Client connects, receives AVCodecContext, fixes interal pointers of AVCodecContext
 * 3) Client sends requested duration in frames, following by a READY signal 
 * 4) Server receives requested duration and starts sending frames right after receiving READY signal from the client
 * 5) Server reads frames and sends to the client for every 40ms 
*/


// mp3client
//
#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>

#include <SDL.h>
#include <SDL_thread.h>

#ifdef __MINGW32__
#undef main /* Prevents SDL from overriding main() */
#endif

#include <stdio.h>
#include "msg.h"

void fixCodecContext(AVCodecContext *s)
{
  // setup default pointers for AVCodecContext

  AVCodecContext temp;
  avcodec_get_context_defaults(&temp);

  s->av_class= temp.av_class;
  s->get_buffer= avcodec_default_get_buffer;
  s->release_buffer= avcodec_default_release_buffer;
  s->get_format= avcodec_default_get_format;
  s->execute= avcodec_default_execute;
     
  s->palctrl = NULL;
  s->reget_buffer= avcodec_default_reget_buffer;
}



int main(int argc, char *argv[]) {
  int             i, videoStream;
  AVCodecContext  codecCtx;
  AVCodecContext  *pCodecCtx = &codecCtx;
  AVCodec         *pCodec;
  AVFrame         *pFrame; 
  AVPacket        packet;
  int             frameFinished;
  float           aspect_ratio;

  SDL_Overlay     *bmp;
  SDL_Surface     *screen;
  SDL_Rect        rect;
  SDL_Event       event;

  if(argc < 4) {
    fprintf(stderr, "Usage: mp3client server port duration(in frames) \n");
    exit(1);
  }
  int duration = atoi(argv[3]);

  int sockfd = connectToServer(argv[1], argv[2]);

  if (sockfd < 0) {
 	printf("Cannot connect to %s:%s\n", argv[1], argv[2]);
	return 1;
  }

   printf("Waiting for AVCodecContext...");
  int len = recv(sockfd, pCodecCtx, sizeof(AVCodecContext), MSG_WAITALL);
  printf("len = %d finished\n", len);

  fixCodecContext(pCodecCtx);
  
   printf("Request for %d frames from the server\n", duration);  
   send(sockfd, (char *) &duration, sizeof(duration), 0);



  // Register all formats and codecs
  av_register_all();
  
  if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
    fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
    exit(1);
  }
  
  // Find the decoder for the video stream
  pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
  if(pCodec==NULL) {
    fprintf(stderr, "Unsupported codec!\n");
    return -1; // Codec not found
  }
  
  // Open codec
  if(avcodec_open(pCodecCtx, pCodec)<0) {
    	printf("Cannot find codec\n");
	return -1; // Could not open codec
  }
  
  // Allocate video frame
  pFrame=avcodec_alloc_frame();

  // Make a screen to put our video
#ifndef __DARWIN__
        screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0);
#else
        screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 24, 0);
#endif
  if(!screen) {
    fprintf(stderr, "SDL: could not set video mode - exiting\n");
    exit(1);
  }
  
  // Allocate a place to put our YUV image on that screen
  bmp = SDL_CreateYUVOverlay(pCodecCtx->width,
				 pCodecCtx->height,
				 SDL_YV12_OVERLAY,
				 screen);


  // Read frames and save first five frames to disk
  i=0;
  double pts;
  int64_t last_time = 0;
  double last_pts = 0, delay;
  double  actual_delay; // in ms

  int step = 1;

  int s = 0;

  //if (step < 0)
  //s = pFormatCtx->streams[videoStream]->duration-1;
   
  char buf[32000];
  int ready = 1;

  printf("Start receiving packets\n");
  send(sockfd, (char *) &ready, sizeof(ready), 0);
  long count = 1;
  while (count <= duration) {
    // Is this a packet from the video stream?
      // Decode video frame
      //
      while ((len = recv(sockfd, buf, sizeof(buf), 0)) <= 0);
      printf("Received a packet %ld of len %d\n", count++, len);
      avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, 
			   buf, len);
      printf("Decoded\n"); 
      if(frameFinished) {
	SDL_LockYUVOverlay(bmp);

	AVPicture pict;
	pict.data[0] = bmp->pixels[0];
	pict.data[1] = bmp->pixels[2];
	pict.data[2] = bmp->pixels[1];

	pict.linesize[0] = bmp->pitches[0];
	pict.linesize[1] = bmp->pitches[2];
	pict.linesize[2] = bmp->pitches[1];

	// Convert the image into YUV format that SDL uses
	img_convert(&pict, PIX_FMT_YUV420P,
                    (AVPicture *)pFrame, pCodecCtx->pix_fmt, 
		    pCodecCtx->width, pCodecCtx->height);
	
	SDL_UnlockYUVOverlay(bmp);
	
	rect.x = 0;
	rect.y = 0;
	rect.w = pCodecCtx->width;
	rect.h = pCodecCtx->height;

	SDL_DisplayYUVOverlay(bmp, &rect);
      }
  }
  
  // Free the YUV frame
  av_free(pFrame);
  
  // Close the codec
  avcodec_close(pCodecCtx);
  
  return 0;
}