package com.videonext.mplayer;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.util.LinkedList;
import java.util.List;

import com.videonext.mplayer.api.ICapturer;
import com.videonext.mplayer.api.ICapturerURLProvider;
import com.videonext.mplayer.api.IControllerListener;
import com.videonext.mplayer.api.events.CapturerEvent;
import com.videonext.mplayer.internal.NativeHelper;

public class Capturer implements ICapturer {

	private ICapturerURLProvider urlProvider;
	private long captureId;
	private boolean isRunning = false;
	private Thread captureThread;
	private LinkedList<IControllerListener> listeners = new LinkedList<IControllerListener>();
	
	private BufferedImage previewImage;
	private byte[] previewBuffer;
	private int previewWidth = 0;
	private int previewHeight = 0;
	
	static {
		String playerVersion = NativeHelper.getPlayerVersion();
		//
		// Loading MediaPlayer library
		//
		if (playerVersion == null) { 
			System.err.println("Player version is not set");
		}

		String nativeLibName = null; 

		String sys = System.getProperty("os.name");
		String arch = System.getProperty("os.arch");
		if (sys == null) {
			System.err.println("Unable to identify your OS");
		}

		if (sys.startsWith("Windows")) {
			if (arch.startsWith("x86"))
				nativeLibName = "VNMediaClient.dll"; 
			else
				nativeLibName = "VNMediaClient-64.dll"; 

		} else if (System.getProperty("mrj.version") != null || sys.startsWith("Mac")) { 
			// Mac OS X TODO: check version
			nativeLibName = "libVNMediaClient.dylib";
		} else {
			// assuming it's linux..
			if (arch.startsWith("x86"))
				nativeLibName = "libVNMediaClient.so";
			else
				nativeLibName = "libVNMediaClient-64.so";
		}

		try {
			NativeHelper.loadFromJar(playerVersion, nativeLibName);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void init() throws Exception {
		captureId = nativeInit();
	}
	
	@Override
	public List<String> getVideoDevices() throws Exception {
		if (captureId == 0)
			throw new Exception("Capturing is not initialized");
		return nativeGetVideoDevices(captureId);
	}

	@Override
	public List<String> getAudioDevices() throws Exception {
		if (captureId == 0)
			throw new Exception("Capturing is not initialized");
		
		return nativeGetAudioDevices(captureId);
	}

	@Override
	public List<String> getVideoParams(String videoDevice) throws Exception {
		if (captureId == 0)
			throw new Exception("Capturing is not initialized");
		return nativeGetVideoParams(captureId, videoDevice);
	}
	
	@Override
	public void start(String audioDevice, String videoDevice, String videoParams, int bitRate) throws Exception {
		if (captureId == 0)
			throw new Exception("Capturing is not initialized");
		
		if (isRunning)
			throw new Exception("Capturing already running");
		
		if (captureThread != null) // waiting previous capturing task
		{
			System.err.println("Waitong for pending capturing task");
			captureThread.join();
		}
		
		isRunning = true;
		captureThread = new CaptureThread(audioDevice, videoDevice, videoParams, bitRate);
		captureThread.start();
	}

	@Override
	public void stop() throws Exception {
		if (captureId == 0)
			throw new Exception("Capturing is not initialized");
		
		if (!isRunning)
			throw new Exception("Capturing already stoped");
		
		isRunning = false;
		nativeStopCapture(captureId);
		
	}

	@Override
	public void setURLProvider(ICapturerURLProvider provider) {
		urlProvider = provider;
	}
	
	@Override
	public byte[] getPreviewBuffer()
	{
		return previewBuffer;
	}
	
	@Override
	public int getPreviewWidth()
	{
		return previewWidth;
	}
	
	@Override
	public int getPreviewHeight()
	{
		return previewHeight;
	}
	
	private void onError(int errorCode, String errorMessage)
	{
		synchronized (listeners) {
			CapturerEvent event = new CapturerEvent(this, true, errorMessage, null);
			for (IControllerListener listener : listeners) {
				listener.controllerUpdate(event);
			}
		}	
	}

	private void onAudioStats(short samples[])
	{	
		synchronized (listeners) {
			CapturerEvent event = new CapturerEvent(this, false, null, samples);
			for (IControllerListener listener : listeners) {
				listener.controllerUpdate(event);
			}
		}	
	}
	
	private byte[] createPreviewBuffer(int width, int height)
	{
		previewWidth  = width;
		previewHeight = height;
		
		previewImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
		
		previewBuffer = ((DataBufferByte)previewImage.getRaster().getDataBuffer()).getData();
		
		return previewBuffer;
	}

	public BufferedImage getPreviewImage()
	{
		return previewImage;
	}	

	class CaptureThread extends Thread {

		private String audioDevice;
		private String videoDevice;
        private String videoParams;
		private int bitRate;
		
        public CaptureThread(String audioDevice, String videoDevice, String videoParams, int bitRate) {
			super();

			this.audioDevice = audioDevice;
			this.videoDevice = videoDevice;
            this.videoParams = videoParams;
			this.bitRate     = bitRate;
		}
		
		@Override
		public void run() {
			while (isRunning)
			{
				String url = urlProvider.getURL();
				
				try {
					nativeStartCapture(captureId, audioDevice, videoDevice, videoParams, url, bitRate);
				} catch (Exception e) {
					e.printStackTrace();
				}
				
				if (isRunning) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
				if (isRunning)
					System.err.println("Restarting capturing");
			}
		}
		
	}
	
	 @Override
	public void addControllerListener(IControllerListener listener) {
			synchronized (listeners) {
				listeners.add(listener);
			}		
	}

	@Override
	public void removeControllerListener(IControllerListener listener) {
			synchronized (listeners) {
				listeners.remove(listener);
			}		
	}
		
	private native long nativeInit();
	
	private native List<String> nativeGetVideoDevices(long captureId);
	
	private native List<String> nativeGetAudioDevices(long captureId);
	
	private native List<String> nativeGetVideoParams(long captureId, String videoDevice);
	
	private native long nativeStartCapture(long captureId, String audioDEvice, String videoDevice, String videoParams, String url, int bitRate);

	private native long nativeStopCapture(long captureId);

	
}
