//
// ZoneMinder Remote Camera Class Implementation, $Date: 2008-03-13 13:36:12 +0000 (Thu, 13 Mar 2008) $, $Revision: 2357 $
// Copyright (C) 2003, 2004, 2005, 2006  Philip Coombes
// 
// This program 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.
// 
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//

// 
// original zm_remote_camera.cpp altered to use libavcodec, loosely based on avcodec_sample.cpp family for:
// ing. Gert-Jan de Jonge
//	1. better performance
//	2. support of non mjpeg camera's 
//	3. support of other protocols like rtsp
// TODO :
//	1. check wether password protected camera's work 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <syslog.h>
#include <signal.h>
#include <stdarg.h>
#include <errno.h>
#include <netdb.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}

#include "zm.h"
#include "zm_remote_camera.h"


    AVFormatContext 	*pFormatCtx=NULL;
    int             	i, videoStream;
    AVCodecContext  	*pCodecCtx;
    AVCodec        	*pCodec;
    AVFrame         	*pFrame; 
    AVFrame         	*pFrameRGB;
    AVPacket        	packet;
    int             	frameFinished;
    int             	numBytes;
    uint8_t         	*av_buffer;
    struct SwsContext   *sws_context = NULL;


RemoteCamera::RemoteCamera( const char *p_host, const char *p_port, const char *p_path, int p_width, int p_height, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : Camera( REMOTE, p_width, p_height, p_palette, p_brightness, p_contrast, p_hue, p_colour, p_capture )
{
	strncpy( host, p_host, sizeof(host) );
	strncpy( port, p_port, sizeof(port) );
	strncpy( path, p_path, sizeof(path) );

	auth[0] = '\0';
	auth64[0] = '\0';

	sd = -1;
	hp = 0;
	request[0] = '\0';
	timeout.tv_sec = 0;
	timeout.tv_usec = 0;

	if ( capture )
	{
		Initialise();
	}
}

RemoteCamera::~RemoteCamera()
{
// sluit alles af
	if ( capture )
	{
		Terminate();
	}

    // Free the RGB image
    delete [] av_buffer;
    av_free(pFrameRGB);

    // Free the YUV frame
    av_free(pFrame);

    // Close the codec
    avcodec_close(pCodecCtx);

    // Close the video file
    av_close_input_file(pFormatCtx);
	
}

void RemoteCamera::Initialise()
{
// gosh initialiseer

    // Register all formats and codecs
    av_register_all();

	if( !host )
	{
		Error(( "No host specified for remote get" ));
		exit( -1 );
	}

	if( !port )
	{
		Error(( "No port specified for remote get" ));
		exit( -1 );
	}

	if( !path )
	{
		Error(( "No path specified for remote get" ));
		exit( -1 );
	}
}

int RemoteCamera::Connect()
{
	return( sd );
}

int RemoteCamera::Disconnect()
{
	return( 0 );
}

int RemoteCamera::SendRequest()
{
	return( 0 );
}

int RemoteCamera::ReadData( Buffer &buffer, int bytes_expected )
{
	return( 0 );
}

int RemoteCamera::GetResponse()
{
	return( 0 );
}

int RemoteCamera::PreCapture()
{
// maak verbinding met camera, doe 1e initialisatie

        // Open video file
        if(pFormatCtx==NULL)
        {
            Debug( 0, ( "Trying to connect to %s", path ));

            if(av_open_input_file(&pFormatCtx, path, NULL, 0, NULL)!=0)
                return -1; // Couldn't open file

            // Retrieve stream information
            if(av_find_stream_info(pFormatCtx)<0)
                return -1; // Couldn't find stream information

            // Dump information about file onto standard error
            dump_format(pFormatCtx, 0, path, 0);

            // Find the first video stream
            videoStream=-1;
            for(i=0; i<pFormatCtx->nb_streams; i++)
                if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO)
                {
                    videoStream=i;
                    break;
                }
            if(videoStream==-1)
                return -1; // Didn't find a video stream

            // Get a pointer to the codec context for the video stream
            pCodecCtx=pFormatCtx->streams[videoStream]->codec;

            // Find the decoder for the video stream
            pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
            if(pCodec==NULL)
                return -1; // Codec not found

            // Open codec
            if(avcodec_open(pCodecCtx, pCodec)<0)
                return -1; // Could not open codec

            // Hack to correct wrong frame rates that seem to be generated by some 
            // codecs
            // if(pCodecCtx->frame_rate>1000 && pCodecCtx->frame_rate_base==1)
            //    pCodecCtx->frame_rate_base=1000;

            // Allocate video frame
            pFrame=avcodec_alloc_frame();

            // Allocate an AVFrame structure
            pFrameRGB=avcodec_alloc_frame();
            if(pFrameRGB==NULL)
                return -1;

            // Determine required buffer size and allocate buffer
            numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
            av_buffer=new uint8_t[numBytes];
            
            // Assign appropriate parts of buffer to image planes in pFrameRGB
            avpicture_fill((AVPicture *)pFrameRGB, av_buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);

            // Read frames and save first five frames to disk
            i=0;
            sws_context = sws_getCachedContext(sws_context, pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,  
                                               pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
        }
	return( 0 );
}

int RemoteCamera::PostCapture( Image &image )
{
// wacht tot het plaatje binnen is, decodeer en zet het in de image struct
    while(av_read_frame(pFormatCtx, &packet)>=0)
    {
        // Is this a packet from the video stream?
        if(packet.stream_index==videoStream)
        {
            // Decode video frame
            avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, packet.data, packet.size);

            // Did we get a video frame?
            if(frameFinished)
            {
                // Convert the image from its native format to RGB
                // We need a context for swscale
                if (!sws_context)
                    printf("ffmpeg-conversion failed (%d->%d)", pCodecCtx->pix_fmt, PIX_FMT_RGB24);
                else if (sws_scale(sws_context, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize)<0)
                    printf("ffmpeg-conversion failed (%d->%d)", pCodecCtx->pix_fmt, PIX_FMT_RGB24);
                 
//                Debug( 0, ( "size '%dx%d' linesize %d", pCodecCtx->width, pCodecCtx->height, pFrameRGB->linesize[0] ));
 
                image.Assign( pCodecCtx->width, pCodecCtx->height, 3, (unsigned char *)pFrameRGB->data[0] );
                break;
            }
        }

        // Free the packet that was allocated by av_read_frame
        av_free_packet(&packet);
    }
    return (0);
}

void RemoteCamera::Base64Encode( const char *in_string, char *out_string )
{
}


