#pragma once

#include <boost/shared_ptr.hpp>
#include <cv.h>
#include <highgui.h>
#include <memory>

using namespace boost;
using namespace std;

void releaseCapture(CvCapture * capture);
void releaseImage(IplImage * image);

class Capture
{
private:
	enum CaptureState { PLAYING, PAUSED, STOPPED};

	shared_ptr<CvCapture> capture;

	CaptureState captureState;

	Capture(shared_ptr<CvCapture> capture);

public:
	
	static shared_ptr<Capture> createFileCapture(const char * filename);
	static shared_ptr<Capture> createCamCapture();

	bool isValid() {
		return capture.get() != NULL;
	}

	bool isPaused() {
		return captureState == PAUSED;
	}

	bool isPlaying() {
		return captureState == PLAYING;
	}

	bool isStopped() {
		return captureState == STOPPED;
	}

	CaptureState getState() {
		return captureState;
	}

	void play() {
		captureState = 	PLAYING;
	}
	void pause() {
		captureState = PAUSED;
	}
	void stop()	{
		captureState = STOPPED;
		setVideoPositionRatio(0);
	}
	void rewind() {
		setNextFrameIndex(getNextFrameIndex()-2);
	}
	void forward() {
		setNextFrameIndex(getNextFrameIndex()+1);
	}

	void playOrPause() {
		if(captureState == PLAYING) 
			pause();
		else if(captureState == PAUSED || captureState == STOPPED)
			play();
	}
  	
  	void setCurrentFrameNumber(int currentFrameNumber);

	const IplImage * nextFrame() {
		if(captureState == PLAYING)
			cvGrabFrame(capture.get());
		return cvRetrieveFrame(capture.get());
	}

	IplImage * getCurrentFrame() {
		return cvRetrieveFrame(capture.get());
	}

	shared_ptr<IplImage> cloneCurrentFrame() {
		return shared_ptr<IplImage>(cvCloneImage(getCurrentFrame()),releaseImage);
	}

	double getCurrentTimestamp() const {
		return cvGetCaptureProperty(capture.get(),CV_CAP_PROP_POS_MSEC);			// - film current position in milliseconds or video capture timestamp
	}
	
	int getNextFrameIndex() const {
		return cvGetCaptureProperty(capture.get(),CV_CAP_PROP_POS_FRAMES);		// - 0-based index of the frame to be decoded/captured next
	}
	void setNextFrameIndex(int val) {
		cvSetCaptureProperty(capture.get(),CV_CAP_PROP_POS_FRAMES,val);
		cvGrabFrame(capture.get());
	}

	int getVideoPositionRatio() const {
		return (int)(100.0*cvGetCaptureProperty(capture.get(),CV_CAP_PROP_POS_AVI_RATIO));	// - relative position of video file (0 - start of the film, 1 - end of the film)
	}
	void setVideoPositionRatio(int val) {
		cvSetCaptureProperty(capture.get(),CV_CAP_PROP_POS_AVI_RATIO,((double)val)/100.0);
		cvGrabFrame(capture.get());
	}

	int getWidth() const{
		return (int) cvGetCaptureProperty(capture.get(),CV_CAP_PROP_FRAME_WIDTH);		// - width of frames in the video stream
	}
	int getHeight() const{
		return (int) cvGetCaptureProperty(capture.get(),CV_CAP_PROP_FRAME_HEIGHT);		// - height of frames in the video stream
	}


	double getFrameRate() {
		return cvGetCaptureProperty(capture.get(),CV_CAP_PROP_FPS);				// - frame rate
	}

	const char * getCodecID () const {
		double f = cvGetCaptureProperty(capture.get(),CV_CAP_PROP_FOURCC);			// - 4-character code of codec. 
		return (const char*)(&f);
	}

	int getLastFrameIndex() const {
		return ((int) cvGetCaptureProperty(capture.get(),CV_CAP_PROP_FRAME_COUNT))-1;		// - number of frames in video file.
	}
	
	~Capture(void);
};

