/*
 * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
 * 
 * This file is part of Spydroid (http://code.google.com/p/spydroid-ipcamera/)
 * 
 * Spydroid is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This source code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this source code; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package org.easydarwin.easyipcamera.hw;

import android.media.MediaCodecInfo;

import java.nio.ByteBuffer;

/**
 * Converts from NV21 to YUV420 semi planar or planar.
 */
public class NV21Convertor {

	private int mSliceHeight, mHeight;

	private int mStride, mWidth;

	private int mSize;

	private boolean mPlanar, mPanesReversed = false;

	private int mYPadding;

	private byte[] mBuffer;

	ByteBuffer mCopy;

	public void setSize(int width, int height) {
		mHeight = height;
		mWidth = width;
		mSliceHeight = height;
		mStride = width;
		mSize = mWidth * mHeight;
	}

	public void setStride(int width) {
		mStride = width;
	}

	public void setSliceHeigth(int height) {
		mSliceHeight = height;
	}

	public void setPlanar(boolean planar) {
		mPlanar = planar;
	}

	public void setYPadding(int padding) {
		mYPadding = padding;
	}

	public int getBufferSize() {
		return 3 * mSize / 2;
	}

	public void setEncoderColorFormat(int colorFormat) {
		switch (colorFormat) {
			case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
			case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
			case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
				setPlanar(false);
				break;
			case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
			case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
				setPlanar(true);
				break;
		}
	}

	public void setColorPanesReversed(boolean b) {
		mPanesReversed = b;
	}

	public int getStride() {
		return mStride;
	}

	public int getSliceHeigth() {
		return mSliceHeight;
	}

	public int getYPadding() {
		return mYPadding;
	}

	public boolean getPlanar() {
		return mPlanar;
	}

	public boolean getUVPanesReversed() {
		return mPanesReversed;
	}

	public void convert(byte[] data, ByteBuffer buffer) {
		byte[] result = convert(data);
		int min = buffer.capacity() < data.length ? buffer.capacity() : data.length;
		buffer.put(result, 0, min);
	}

	public byte[] convert(byte[] data) {

		// A buffer large enough for every case
		if (mBuffer == null || mBuffer.length != 3 * mSliceHeight * mStride / 2 + mYPadding) {
			mBuffer = new byte[3 * mSliceHeight * mStride / 2 + mYPadding];
		}

		if (!mPlanar) {
			if (mSliceHeight == mHeight && mStride == mWidth) {
				// Swaps U and V
				if (!mPanesReversed) {
					for (int i = mSize; i < mSize + mSize / 2; i += 2) {
						mBuffer[0] = data[i + 1];
						data[i + 1] = data[i];
						data[i] = mBuffer[0];
					}
				}
				if (mYPadding > 0) {
					System.arraycopy(data, 0, mBuffer, 0, mSize);
					System.arraycopy(data, mSize, mBuffer, mSize + mYPadding, mSize / 2);
					return mBuffer;
				}
				return data;
			}
		}
		else {
			if (mSliceHeight == mHeight && mStride == mWidth) {
				// De-interleave U and V
				if (!mPanesReversed) {
					for (int i = 0; i < mSize / 4; i += 1) {
						mBuffer[i] = data[mSize + 2 * i + 1];
						mBuffer[mSize / 4 + i] = data[mSize + 2 * i];
					}
				}
				else {
					for (int i = 0; i < mSize / 4; i += 1) {
						mBuffer[i] = data[mSize + 2 * i];
						mBuffer[mSize / 4 + i] = data[mSize + 2 * i + 1];
					}
				}
				if (mYPadding == 0) {
					System.arraycopy(mBuffer, 0, data, mSize, mSize / 2);
				}
				else {
					System.arraycopy(data, 0, mBuffer, 0, mSize);
					System.arraycopy(mBuffer, 0, mBuffer, mSize + mYPadding, mSize / 2);
					return mBuffer;
				}
				return data;
			}
		}

		return data;
	}

}
