/* 2010-03-15
 */
package com.cwasa.javacv;


import java.io.IOException;
import java.io.File;
import java.io.OutputStream;

import java.util.zip.GZIPOutputStream;

import java.net.Socket;

import java.awt.image.BufferedImage;

import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import javax.imageio.ImageIO;

import java.util.Date;

import com.cwasa.javacv.Logging;

import static com.cwasa.javacv.ServiceApp.DO_LOGGING;

import static com.cwasa.javacv.JAVideoServiceThread.VIDEO_SERVICE_PORT;


public class ClientForTesting extends Thread {

	public static void startTest(
		int n, int w, int h, int fps, String moviepath, Logging thelog) {
		try {
			ClientForTesting cthread =
				new ClientForTesting(n, w, h, fps, moviepath, thelog);
			cthread.start();
		}
		//catch (IOException iox) { System.out.println("Client ctor:"+iox); }
		catch (IOException iox) { thelog.log("Client ctor:"+iox); }
	}

	private final Logging			LOG;

	private final int				N_FRAMES;
	private final int				W;
	private final int				H;
	private final int				FPS;
	private final String			MOVIE_PATH;

	private OutputStream			OUT_TO_SERVER;

	private final byte[]			BUF;
	private int						nWrite;

//	private byte[]					pbuf;
//	private ByteBuffer				bbuf;
//	private IntBuffer				ibuf;

	private final int				P_SIZE;
	private final byte[]			BGR_BYTES;

	private ClientForTesting(
		int n, int w, int h, int fps, String moviepath, Logging thelog)
	throws IOException {

		this.LOG = thelog;
		//System.out.println("ClientForTesting starts ...");
		this.LOG.log("ClientForTesting starts ...");

		this.N_FRAMES = n;
		this.W = w;  this.H = h;
		this.FPS = fps;  this.MOVIE_PATH = moviepath;

		this.P_SIZE = w * h;

		Socket sckt = new Socket("localhost", VIDEO_SERVICE_PORT);
		OutputStream couts = sckt.getOutputStream();
		this.OUT_TO_SERVER = new GZIPOutputStream(couts);
//		OutputStream couts = new FileOutputStream("clientData.gzip");
//		this.OUT_TO_SERVER = new GZIPOutputStream(couts);

		this.BUF = new byte[4];
		this.BGR_BYTES = new byte[w*h*3];
		this.nWrite = 0;

//		this.pbuf = new byte[w*h*4];
//		this.bbuf = ByteBuffer.wrap(this.pbuf);
//		bbuf.order(ByteOrder.nativeOrder());
//		this.ibuf = bbuf.asIntBuffer();
	}

	public void run() {
		try {
			//System.out.println("ClientForTesting.run()");
			this.LOG.log("ClientForTesting.run() at "+(new Date()));

			long ta = System.nanoTime();

			this.writeInt(this.N_FRAMES);
			this.writeInt(this.W);
			this.writeInt(this.H);
			this.writeInt(this.FPS);
			this.writeString(this.MOVIE_PATH);

			this.LOG.log(String.format(
				"Client: N=%d (W,H)=(%d,%d) FPS=%d  Video file: %s",
				this.N_FRAMES, this.W, this.H, this.FPS, this.MOVIE_PATH));

			int[] pixels = new int[this.P_SIZE];
			for (int f=0; f!=this.N_FRAMES; ++f) {
				long t0 = System.nanoTime();
				BufferedImage bimg = getImage(f);
				long t1 = System.nanoTime();
				bimg.getRGB(0, 0, this.W, this.H, pixels, 0, this.W);
				long t2 = System.nanoTime();
				this.writePixels(pixels);

				if (DO_LOGGING) {
					long t3 = System.nanoTime();
					int tr = (int)((t1-t0)/1000);
					int tp = (int)((t2-t1)/1000);
					int tw = (int)((t3-t2)/1000);
					//System.out.printf(
					this.LOG.log(String.format(
						"Client frame %d:  tR=%d  tP=%d  tW=%d",
						f, tr, tp, tw));
				}
//				long t0 = System.nanoTime();
//				BufferedImage bimg = getImage(f);
//				long t1 = System.nanoTime();
//				bimg.getRGB(0, 0, W, H, pixels, 0, W);
//				long t2 = System.nanoTime();
//				this.writePixels(pixels);
//				long t3 = System.nanoTime();
//				int tr = (int)((t1-t0)/1000);
//				int tp = (int)((t2-t1)/1000);
//				int tw = (int)((t3-t2)/1000);
//				System.out.printf(
//					"Client frame %d:  tR=%d  tP=%d  tW=%d\n", f, tr, tp, tw);
					//Client frame 49:  tR=6478  tP=14084  tW=29481
			}
			this.OUT_TO_SERVER.close();

			long tb = System.nanoTime();

			this.LOG.log("Client output: nWrite="+this.nWrite);
			this.LOG.log("Client done: N_FRAMES="+this.N_FRAMES);

			int tall = (int)((tb-ta)/(1000*1000));
			this.LOG.log(String.format("Client total t=%d ms.", tall));
		}
		catch (IOException iox) {
			//iox.printStackTrace();
			this.LOG.log("Client error:");
			for (StackTraceElement ste : iox.getStackTrace()) {
				this.LOG.log(ste.toString());
			}
		}
	}

	private void writePixels(int[] pixels) throws IOException {

		//		this.ibuf.rewind();
//		this.ibuf.put(pixels);
//		this.bbuf.rewind();
//		this.OUT_TO_SERVER.write(this.pbuf);
//		this.nWrite += this.pbuf.length;

		final byte[] BGR = this.BGR_BYTES;
		int b = 0;
		for (int y=this.H; y!=0; --y) {
			final int ROW_BASE = (y - 1) * this.W;
			for (int x=0; x!=this.W; ++x) {
				int val = pixels[ROW_BASE + x];
				BGR[b+0] = (byte)val;  val >>= 8;	// B
				BGR[b+1] = (byte)val;  val >>= 8;	// G
				BGR[b+2] = (byte)val;  val >>= 8;	// R
				b += 3;
			}
		}
		this.OUT_TO_SERVER.write(BGR);
		this.nWrite += BGR.length;

//		for (int p=0; p!=P_SIZE; ++p) {
//			this.writeInt(pixels[p]);
//		}

//		final int P_SIZE = this.W * this.H;
//		int pp = 0;
//		for (int p=0; p!=P_SIZE; ++p) {
//			int i = pixels[p];
//			this.pbuf[pp] = (byte)i;  i >>= 8;  ++pp;
//			this.pbuf[pp] = (byte)i;  i >>= 8;  ++pp;
//			this.pbuf[pp] = (byte)i;  i >>= 8;  ++pp;
//			this.pbuf[pp] = (byte)i;            ++pp;
//		}
//		this.OUT_TO_SERVER.write(this.pbuf);

//		int pval = pixels[0];
//		int bval =
//			(((pbuf[3]<<8) |
//				(pbuf[2]&0x0FF))<<8 |
//					(pbuf[1]&0x0FF))<<8 |
//						(pbuf[0]&0x0FF);
//		int ok = pval==bval?1:0;
//		System.out.printf("%08x %08x %1d\n", pval, bval, ok);
	}

	private void writeString(String s) throws IOException {
		final int N = s.length();
		this.writeInt(N);
		for (int i=0; i!=N; ++i) {
			this.writeInt(s.charAt(i) & 0xFFFF);
		}
	}

	private void writeInt(int val) throws IOException {
		int i = val;
		this.BUF[0] = (byte)i;  i >>= 8;
		this.BUF[1] = (byte)i;  i >>= 8;
		this.BUF[2] = (byte)i;  i >>= 8;
		this.BUF[3] = (byte)i;
		this.OUT_TO_SERVER.write(this.BUF);
		this.nWrite += 4;
	}

	protected static BufferedImage getImage(int f) throws IOException {
		return ImageIO.read(imageFile(f));
	}

	protected static File imageFile(int f) {
		return new File(imagePath(f));
	}

	protected static String imagePath(int f) {
		final String F_TAG = String.format("%03d", f);
		return "frames/GRAB_"+F_TAG+".png";
	}
}
