//////////////////////////////////////////////////////////////////////
// RawSource - reads raw video data files
// Peché Ernst - 2005
//
// RawsourceSeq (RawSource Mod for raw files sequences input ONLY (no headers)
// Billou2k - 2006.04
//////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <io.h>
#include "FCNTL.H"
#include <stdlib.h>
#include "avisynth.h"

#define maxwidth 2880

class RawSourceSeq: public IClip {

	VideoInfo vi;
	int h_rawfile;						//file handle for 64bit operations
	__int64 filelen;
	char pixel_type[32];
	int bytes_per_line;
	unsigned char rawbuf[maxwidth*4];	//max linesize 4Bytes * maxwidth
	int ret;
	int mapping[4];
	int mapcnt;
	int maxframe;
	int framesize;
	
	char seqpattern[256];
	int startframe;
	int duration;

	int level;
	bool show;	//show debug info
	
public:
	RawSourceSeq(const char name[], const int _start, const int _duration, const int _width, const int _height, const char _pixel_type[], const char indexstring[], const bool _show, IScriptEnvironment* env) ;
	virtual ~RawSourceSeq();

// avisynth virtual functions
	PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
	bool __stdcall GetParity(int n);
	void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) {};
	const VideoInfo& __stdcall GetVideoInfo();
	void __stdcall SetCacheHints(int cachehints,int frame_range) {};
};

//
//	name is the pattern for your seq of files. It should include the full path + " %0xd " where x represents the number of digits in your filename pattern
//	start is the number of the first frame
//	duration can either be the number of frames (make it negative)
//		or the last frame (leave it positive)
//	the other parameters are unchanged:
//	width is the frame width 
//	height is the frame height
//	and pixeltype is the fourcc code for your uncompressed format 
RawSourceSeq::RawSourceSeq (const char name[], const int _start, const int _duration, const int _width, const int _height, const char _pixel_type[], const char indexstring[], const bool _show, IScriptEnvironment* env) {

	if (_width > maxwidth) env->ThrowError("Width too big.");

	char cname[256];
	startframe=_start;
	if (_duration<0)
		duration=-_duration;
	else
		duration=_duration-_start;

	strcpy(seqpattern,name);
	sprintf(cname,name,_start);

	h_rawfile = _open(cname, _O_BINARY | _O_RDONLY);

	if (h_rawfile==-1) {
		env->ThrowError("Cannot open frame %d.",_start);
	}

	level = 0;
	show = _show;
	
	filelen = _filelengthi64(h_rawfile);
	if (filelen==-1L) env->ThrowError("Cannot get videofile length.");

	strcpy(pixel_type, _pixel_type);

	vi.width = _width;
	vi.height = _height;
	vi.fps_numerator = 25;
	vi.fps_denominator = 1;
	vi.SetFieldBased(false);
	vi.audio_samples_per_second=0;

	vi.pixel_type = VideoInfo::CS_UNKNOWN;

	if (!stricmp(pixel_type,"RGB")) {
		vi.pixel_type = VideoInfo::CS_BGR24;
		mapping[0] = 2;
		mapping[1] = 1;
		mapping[2] = 0;
		mapcnt = 3;
	}

	if (!stricmp(pixel_type,"BGR")) {
		vi.pixel_type = VideoInfo::CS_BGR24;
		mapping[0] = 0;
		mapping[1] = 1;
		mapping[2] = 2;
		mapcnt = 3;
	}

	if (!stricmp(pixel_type,"RGBA")) {
		vi.pixel_type = VideoInfo::CS_BGR32;
		mapping[0] = 2;
		mapping[1] = 1;
		mapping[2] = 0;
		mapping[3] = 3;
		mapcnt = 4;
	}
	
	if (!stricmp(pixel_type,"BGRA")) {
		vi.pixel_type = VideoInfo::CS_BGR32;
		mapping[0] = 0;
		mapping[1] = 1;
		mapping[2] = 2;
		mapping[3] = 3;
		mapcnt = 4;
	}

	if (!stricmp(pixel_type,"YUYV")) {
		vi.pixel_type = VideoInfo::CS_YUY2;
		mapping[0] = 0;
		mapping[1] = 1;
		mapcnt = 2;
	}
	
	if (!stricmp(pixel_type,"UYVY")) {
		vi.pixel_type = VideoInfo::CS_YUY2;
		mapping[0] = 1;
		mapping[1] = 0;
		mapcnt = 2;
	}

	if (!stricmp(pixel_type,"YVYU")) {
		vi.pixel_type = VideoInfo::CS_YUY2;
		mapping[0] = 0;
		mapping[1] = 3;
		mapping[2] = 2;
		mapping[3] = 1;
		mapcnt = 4;
	}
	
	if (!stricmp(pixel_type,"VYUY")) {
		vi.pixel_type = VideoInfo::CS_YUY2;
		mapping[0] = 1;
		mapping[1] = 2;
		mapping[2] = 3;
		mapping[3] = 0;
		mapcnt = 4;
	}

	if (!stricmp(pixel_type,"YV16")) {
		vi.pixel_type = VideoInfo::CS_YUY2;
		mapcnt = 4;
	}

	if (!stricmp(pixel_type,"I420")) {
		vi.pixel_type = VideoInfo::CS_YV12;
		mapping[0] = 0;
		mapcnt = 1;
	}

	if (!stricmp(pixel_type,"YV12")) {
		vi.pixel_type = VideoInfo::CS_YV12;
		mapping[0] = 1;
		mapcnt = 1;
	}

	if (vi.pixel_type == VideoInfo::CS_UNKNOWN) env->ThrowError("Invalid pixel type. Supported: RGB, RGBA, BGR, BGRA, YUYV, UYVY, YVYU, VYUY, YV16, YV12, I420");

	bytes_per_line = vi.width * vi.BitsPerPixel() / 8;
	framesize = bytes_per_line * vi.height;

	maxframe = duration;		// may need to add some check on missing files here but may be slow
								// 

	if (maxframe < 1) {
		env->ThrowError("File too small for even one frame.");
	}

	vi.num_frames = maxframe ;
	_close(h_rawfile);

}


RawSourceSeq::~RawSourceSeq() {
	_close(h_rawfile);
}

PVideoFrame __stdcall RawSourceSeq::GetFrame(int n, IScriptEnvironment* env) {

	unsigned char* pdst;
	char cname[256];
	int i;
	int j;
	int k;
	PVideoFrame dst;


	if (show && (level==0)) {
	//output debug info - call Subtitle

		char message[255];
		//	sprintf(message, "%d : %I64d %c", n, index[n].index, index[n].type );

		const char* arg_names[11] = {   0,                 0, "x", "y", "font", "size", "text_color", "halo_color"};
		AVSValue args[8]          = {this, AVSValue(message),   4,  12, "Arial",    15, 0xFFFFFF,         0x000000};

		level = 1;
		PClip resultClip = (env->Invoke("Subtitle",AVSValue(args,8), arg_names )).AsClip();
		PVideoFrame src1 = resultClip->GetFrame(n, env);
		level = 0;
		return src1;
	//end debug
	} 

	dst = env->NewVideoFrame(vi);

	// set the filename to open and load it
	sprintf(cname,seqpattern,startframe+n);
	h_rawfile = _open(cname, _O_BINARY | _O_RDONLY);

	if (h_rawfile ==-1L) return dst;	//error. do nothing

	if (vi.IsPlanar()) {
//planar formats. Assuming 4 luma pixels with 1 chroma pair
		pdst = dst->GetWritePtr(PLANAR_Y);
		for (i=0; i<vi.height; i++) {
			memset(rawbuf, 0, maxwidth * 4);
			ret = _read(h_rawfile, rawbuf, vi.width);	//luma bytes
			memcpy (pdst, rawbuf, vi.width);
			pdst = pdst + dst->GetPitch(PLANAR_Y);
		}

		//switching between UV and VU
		if (mapping[0]==0) {
			pdst = dst->GetWritePtr(PLANAR_U);
		} else {
			pdst = dst->GetWritePtr(PLANAR_V);
		}
		for (i=0; i<vi.height/2; i++) {
			memset(rawbuf, 0, maxwidth * 4);
			ret = _read(h_rawfile, rawbuf, vi.width/2);	//chroma bytes
			memcpy (pdst, rawbuf, vi.width/2);
			pdst = pdst + dst->GetPitch(PLANAR_U);
		}

		if (mapping[0]==0) {
			pdst = dst->GetWritePtr(PLANAR_V);
		} else {
			pdst = dst->GetWritePtr(PLANAR_U);
		}
		for (i=0; i<vi.height/2; i++) {
			memset(rawbuf, 0, maxwidth * 4);
			ret = _read(h_rawfile, rawbuf, vi.width/2);	//chroma bytes
			memcpy (pdst, rawbuf, vi.width/2);
			pdst = pdst + dst->GetPitch(PLANAR_V);
		}

    } else if (!stricmp(pixel_type,"YV16")) {
// convert to YUY2 by rearranging bytes
		pdst = dst->GetWritePtr();
		//read and copy all luma lines
		for (i=0; i < vi.height; i++) {
				memset(rawbuf, 0, maxwidth * 4);
				ret = _read(h_rawfile, rawbuf, bytes_per_line/2);
				for (j=0; j<(bytes_per_line/4); j++) {
						pdst[j*4] = rawbuf[j*2];
						pdst[j*4+2] = rawbuf[j*2+1];
				}
				pdst = pdst + dst->GetPitch();
		}
		pdst = dst->GetWritePtr();
		//read and copy all U lines
		for (i=0; i < vi.height; i++) {
				ret = _read(h_rawfile, rawbuf, bytes_per_line/4);
				for (j=0; j<(bytes_per_line/4); j++) {
						pdst[j*4+1] = rawbuf[j]; // u-samples
				}
				pdst = pdst + dst->GetPitch();
		}
		pdst = dst->GetWritePtr();
		//read and copy all V lines
		for (int i=0; i < vi.height; i++) {
				ret = _read(h_rawfile, rawbuf, bytes_per_line/4);
				for (j=0; j<(bytes_per_line/4); j++) {
						pdst[j*4+3] = rawbuf[j]; // v-samples
				}
				pdst = pdst + dst->GetPitch();
		}
	} else {
//interleaved formats
		pdst = dst->GetWritePtr();

		//read and copy all lines
		for (i=0; i<vi.height; i++) {
			memset(rawbuf, 0, maxwidth * 4);
			ret = _read(h_rawfile, rawbuf, bytes_per_line);
			for (j=0; j<(bytes_per_line/mapcnt); j++) {
				for (k=0; k<mapcnt; k++) {
					pdst[j*mapcnt + k] = rawbuf[j*mapcnt + mapping[k]];
				}
			}
			pdst = pdst + dst->GetPitch();
		}
	}
	_close(h_rawfile);
	return dst;
}


bool __stdcall RawSourceSeq::GetParity(int n) { return false; }
//void __stdcall RawSourceSeq::GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env);
const VideoInfo& __stdcall RawSourceSeq::GetVideoInfo() { return vi;}
//void __stdcall RawSourceSeq::SetCacheHints(int cachehints,int frame_range) ;


AVSValue __cdecl Create_RawSourceSeq(AVSValue args, void* user_data, IScriptEnvironment* env) {
	return new RawSourceSeq(args[0].AsString(), args[1].AsInt(1), args[2].AsInt(10), args[3].AsInt(720), args[4].AsInt(576), args[5].AsString("YUY2"), args[6].AsString(""), args[7].AsBool(false), env);
}
extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env)
{
  env->AddFunction("RawSourceSeq","[file]s[start]i[duration]i[width]i[height]i[pixel_type]s[index]s[show]b",Create_RawSourceSeq,0);
  return 0;
}
