package jp.androidgroup.nyartoolkit;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;

import jp.androidgroup.nyartoolkit.model.VoicePlayer;
import jp.nyatla.nyartoolkit.NyARException;
import jp.nyatla.nyartoolkit.core.NyARCode;
import jp.nyatla.nyartoolkit.core.param.NyARParam;
import jp.nyatla.nyartoolkit.core.raster.rgb.NyARRgbRaster_RGB;
import jp.nyatla.nyartoolkit.core.transmat.NyARTransMatResult;
import jp.nyatla.nyartoolkit.core.types.NyARBufferType;
import jp.nyatla.nyartoolkit.detector.NyARDetectMarker;
import jp.nyatla.nyartoolkit.jogl.utils.NyARGLUtil;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.SystemClock;
import android.util.Log;

public class ARToolkitDrawer {
	
	private static final int PATT_MAX = 2;
	private static final int MARKER_MAX = 8;
	
	private NyARDetectMarker nya = null;
	private NyARRgbRaster_RGB raster = null;
	private NyARGLUtil ar_util = null;
	private NyARParam ar_param = null;
	private NyARCode[] ar_code = new NyARCode[PATT_MAX];
	private NyARTransMatResult ar_transmat_result = new NyARTransMatResult();

	
	private ModelRenderer mRenderer = null;
	private InputStream camePara = null;
	private ArrayList<InputStream> patt = new ArrayList<InputStream>();
    private boolean mTranslucentBackground;
    private boolean isYuv420spPreviewFormat;
	
	private VoicePlayer mVoiceSound = null;

	static {
        System.loadLibrary("yuv420sp2rgb");
    }
    public static native void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height, int type);
    public static native void decodeYUV420SP(byte[] rgb, byte[] yuv420sp, int width, int height, int type);
	
	public ARToolkitDrawer(InputStream camePara, ArrayList<InputStream> patt, ModelRenderer mRenderer, boolean mTranslucentBackground, boolean isYuv420spPreviewFormat) {
		this.camePara = camePara;
		this.patt = patt;
		this.mRenderer = mRenderer;
		this.mTranslucentBackground = mTranslucentBackground;
		this.isYuv420spPreviewFormat = isYuv420spPreviewFormat;
	}
	
	private void createNyARTool(int w, int h) {
		// NyARToolkit setting.
		try {
			if (ar_param == null) {
				ar_util = new NyARGLUtil();
				ar_param = new NyARParam();
				ar_param.loadARParam(camePara);
				for (int i = 0; i < PATT_MAX; i++) {
					ar_code[i] = new NyARCode(16, 16);
					ar_code[i].loadARPatt(patt.get(i));
				}
				//TODO 本当は、ここはifの外でないと行けない。だけど、出すとOutOfMemory
				ar_param.changeScreenSize(w, h);
				double[] width = new double[] {80.0, 80.0};
				nya = new NyARDetectMarker(ar_param, ar_code, width, PATT_MAX, NyARBufferType.BYTE1D_B8G8R8_24);
				nya.setContinueMode(false);
			}
			Log.d("nyar", "resources have been loaded");
		} catch (Exception e) {
			Log.e("nyar", "resource loading failed", e);
		}

	}

	public void setVoicePlayer(VoicePlayer mVoiceSound) {
		this.mVoiceSound = mVoiceSound;
	}
	
	public void draw(byte[] data) {
		
		if(data == null) {
			Log.d("AR draw", "data= null");
			return;
		}
		Log.d("AR draw", "data.length= " + data.length);
		int width = 320;
		int height = 240;

		Bitmap bitmap = null;
		if (!isYuv420spPreviewFormat) {
			BitmapFactory.Options options = new BitmapFactory.Options();
			options.inSampleSize = 4;
			bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);
			if (bitmap == null) {
				Log.d("AR draw", "data is not BitMap data.");
				return;
			}

			if(bitmap.getHeight() < 240) {
				bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
				if (bitmap == null) {
					Log.d("AR draw", "data is not BitMap data.");
					return;
				}
			}

			width = bitmap.getWidth();
			height = bitmap.getHeight();
			Log.d("AR draw", "bitmap width * height()= " + width + " * " + height);

			mRenderer.setBgBitmap(bitmap);

		} else if (!mTranslucentBackground) {
			// assume YUV420SP
			int[] rgb = new int[(width * height)];
			try {
				bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
			} catch (Exception e) {
				Log.d("AR draw", "bitmap create error.");
				return;
			}		
			long time1 = SystemClock.uptimeMillis();
			// convert YUV420SP to ARGB
			decodeYUV420SP(rgb, data, width, height, 2);
			long time2 = SystemClock.uptimeMillis();
			Log.d("ARToolkitDrawer", "ARGB decode time: " + (time2 - time1) + "ms");
			bitmap.setPixels(rgb, 0, width, 0, 0, width, height);

			mRenderer.setBgBitmap(bitmap);
		}

		// start coordinates calculation.
		byte[] buf = new byte[width * height * 3];

		if (!isYuv420spPreviewFormat) {
			int[] rgb = new int[(width * height)];

			bitmap.getPixels(rgb, 0, width, 0, 0, width, height);

			// convert ARGB to RGB24
			for (int i = 0; i < rgb.length; i++) {
				byte r = (byte) (rgb[i] & 0x00FF0000 >> 16);
				byte g = (byte) (rgb[i] & 0x0000FF00 >> 8);
				byte b = (byte) (rgb[i] & 0x000000FF);
				buf[i * 3] = r;
				buf[i * 3 + 1] = g;
				buf[i * 3 + 2] = b;
			}
		} else {
			// assume YUV420SP
			long time1 = SystemClock.uptimeMillis();
			// convert YUV420SP to RGB24
			decodeYUV420SP(buf, data, width, height, 1);
			long time2 = SystemClock.uptimeMillis();
			Log.d("ARToolkitDrawer", "RGB decode time: " + (time2 - time1) + "ms");
		}
		
		float[][] resultf = new float[MARKER_MAX][16];

		int found_markers;
		int ar_code_index[] = new int[MARKER_MAX];
		
		createNyARTool(width, height);
		// Marker detection 
		try {
			Log.d("AR draw", "Marker detection.");
			raster = new NyARRgbRaster_RGB(width, height);
			raster.wrapBuffer(buf);
			found_markers = nya.detectMarkerLite(raster, 130);
		} catch (NyARException e) {
			Log.e("AR draw", "marker detection failed", e);
			return;
		}
		
		// An OpenGL object will be drawn if matched.
		if (found_markers > 0) {
			Log.d("AR draw", "!!!!!!!!!!!exist marker." + found_markers + "!!!!!!!!!!!");
			// Projection transformation.
			float[] cameraRHf = new float[16];
			ar_util.toCameraFrustumRHf(ar_param, cameraRHf);

			if (found_markers > MARKER_MAX)
				found_markers = MARKER_MAX;
			for (int i = 0; i < found_markers; i++) {
				try {
					ar_code_index[i] = nya.getARCodeIndex(i);
					NyARTransMatResult transmat_result = ar_transmat_result;
					nya.getTransmationMatrix(i, transmat_result);
					ar_util.toCameraViewRHf(transmat_result, resultf[i]);
				} catch (NyARException e) {
					Log.e("AR draw", "getCameraViewRH failed", e);
					return;
				}
			}
			mRenderer.objectPointChanged(found_markers, ar_code_index, resultf, cameraRHf);
			if(mVoiceSound != null)
				mVoiceSound.startVoice();
		} else {
			Log.d("AR draw", "not exist marker.");
			if(mVoiceSound != null)
				mVoiceSound.stopVoice();
			mRenderer.objectClear();
		}

	}
	
}
