content: /*
  The contents of this file are subject to the Mozilla Public License
  Version 1.1 (the "License"); you may not use this file except in
  compliance with the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL/
  
  Software distributed under the License is distributed on an "AS IS"
  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
  License for the specific language governing rights and limitations
  under the License.
  
  The Original Code is collection of files collectively known as Open Camera.
  
  The Initial Developer of the Original Code is Almalence Inc.
  Portions created by Initial Developer are Copyright (C) 2013 
  by Almalence Inc. All Rights Reserved.
   */
  
  package com.almalence.plugins.processing.objectremoval;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  
  import android.content.Context;
  import android.graphics.Bitmap;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.DashPathEffect;
  import android.graphics.Matrix;
  import android.graphics.Paint;
  import android.graphics.Point;
  import android.graphics.Rect;
  import android.os.Handler;
  import android.os.Message;
  import android.view.Display;
  import android.view.KeyEvent;
  import android.view.LayoutInflater;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.WindowManager;
  import android.widget.Button;
  import android.widget.ImageView;
  import android.widget.RelativeLayout;
  import android.widget.RelativeLayout.LayoutParams;
  
  import com.almalence.SwapHeap;
  /* <!-- +++
   import com.almalence.opencam_plus.ApplicationInterface;
   import com.almalence.opencam_plus.ApplicationScreen;
   import com.almalence.opencam_plus.PluginManager;
   import com.almalence.opencam_plus.R;
   import com.almalence.opencam_plus.cameracontroller.CameraController;
   
   +++ --> */
  // <!-- -+-
  import com.almalence.opencam.ApplicationInterface;
  import com.almalence.opencam.ApplicationScreen;
  import com.almalence.opencam.PluginManager;
  import com.almalence.opencam.R;
  import com.almalence.opencam.cameracontroller.CameraController;
  //-+- -->
  
  import com.almalence.plugins.processing.multishot.MultiShotProcessingPlugin;
  import com.almalence.plugins.processing.multishot.AlmaCLRShot.ObjBorderInfo;
  import com.almalence.plugins.processing.multishot.AlmaCLRShot.ObjectInfo;
  
  /***
   * Implements night processing
   ***/
  
  public class ObjectRemovalProcessingPlugin extends MultiShotProcessingPlugin
  {
  
  	private View			postProcessingView;
  
  	private long			sessionID		= 0;
  
  	private static int		mAngle			= 0;
  
  	private boolean			released		= false;
  
  	private int				mImageDataOrientation;
  	private int				mLayoutOrientation;
  	private boolean			mCameraMirrored;
  	private int				mSensorOrientation;
  
  	// indicates that no more user interaction needed
  	private boolean			finishing		= false;
  	
  	public ObjectRemovalProcessingPlugin()
  	{
  		super("com.almalence.plugins.objectremovalprocessing", "objectremoval", 0, 0, 0, null);
  	}
  
  	public View getPostProcessingView()
  	{
  		return postProcessingView;
  	}
  
  	public void onStart()
  	{
  		getPrefs();
  	}
  
  	public void onStartProcessing(long SessionID)
  	{
  		finishing = false;
  		released = false;
  		Message msg = new Message();
  		msg.what = ApplicationInterface.MSG_PROCESSING_BLOCK_UI;
  		ApplicationScreen.getMessageHandler().sendMessage(msg);
  
  		PluginManager.getInstance().sendMessage(ApplicationInterface.MSG_BROADCAST, 
  				ApplicationInterface.MSG_CONTROL_LOCKED);
  
  		ApplicationScreen.getGUIManager().lockControls = true;
  
  		sessionID = SessionID;
  
  		PluginManager.getInstance().addToSharedMem("modeSaveName" + sessionID,
  				PluginManager.getInstance().getActiveMode().modeSaveName);
  
  		mImageDataOrientation = Integer.valueOf(PluginManager.getInstance().getFromSharedMem("frameorientation1" + sessionID));
  		mCameraMirrored = Boolean.valueOf(PluginManager.getInstance().getFromSharedMem("framemirrored1" + sessionID));
  		mSensorOrientation = CameraController.getSensorOrientation(mCameraMirrored);
  		
  		mLayoutOrientation = ApplicationScreen.getGUIManager().getLayoutOrientation();
  		
  		CameraController.Size imageSize = CameraController.getCameraImageSize();
  		int iSaveImageWidth = imageSize.getWidth();
  		int iSaveImageHeight = imageSize.getHeight();
  
  		getPrefs();
  	
  		int imagesAmount = Integer.parseInt(PluginManager.getInstance().getFromSharedMem(
  				"amountofcapturedframes" + sessionID));
  
  		if (imagesAmount == 0)
  			imagesAmount = 1;
  
  		PluginManager.getInstance()
  				.addToSharedMem("amountofresultframes" + sessionID, String.valueOf(imagesAmount));
  
  		PluginManager.getInstance().addToSharedMem("saveImageWidth" + sessionID, String.valueOf(iSaveImageWidth));
  		PluginManager.getInstance().addToSharedMem("saveImageHeight" + sessionID, String.valueOf(iSaveImageHeight));
  
  		try
  		{
  			getDisplaySize();
  			ObjectRemovalCore.getInstance().initializeParameters(mDisplayWidth, mDisplayHeight);
  			ObjectRemovalCore.getInstance().onStartProcessing();
  		} catch (Exception e)
  		{
  			e.printStackTrace();
  		}
  	}
  
  	/************************************************
  	 * POST PROCESSING
  	 ************************************************/
  	public boolean isPostProcessingNeeded()
  	{
  		return true;
  	}
  
  	private ImageView				mImgView;
  	private Button					mSaveButton;
  
  	private int						mLayoutOrientationCurrent;
  	private int						mDisplayOrientationCurrent;
  
  	private static final int		MSG_REDRAW			= 1;
  	private static final int		MSG_LEAVING			= 3;
  	private static final int		MSG_END_OF_LOADING	= 4;
  	private static final int		MSG_SAVE			= 5;
  	private final Handler			mHandler			= new Handler(this);
  	private boolean[]				mObjStatus;
  	private Bitmap					PreviewBmp			= null;
  	public static int				mPreviewWidth;
  	public static int				mPreviewHeight;
  	public static int				mDisplayWidth;
  	public static int				mDisplayHeight;
  	
  	@Override
  	public void setYUVBufferList(ArrayList<Integer> YUVBufferList)
  	{
  		ObjectRemovalCore.getInstance().setYUVBufferList(YUVBufferList);
  	}
  
  	Paint								paint				= null;
  
  	private boolean						postProcessingRun	= false;
  
  	public void onStartPostProcessing()
  	{
  		mDisplayOrientationCurrent = ApplicationScreen.getGUIManager().getDisplayOrientation();
  		int orientation = ApplicationScreen.getGUIManager().getLayoutOrientation();
  		mLayoutOrientationCurrent = (orientation == 0 || orientation == 180) ? orientation : (orientation + 180) % 360;
  
  		LayoutInflater inflator = ApplicationScreen.instance.getLayoutInflater();
  		postProcessingView = inflator.inflate(R.layout.plugin_processing_objectremoval_postprocessing, null, false);
  
  		mImgView = ((ImageView) postProcessingView.findViewById(R.id.objectremovalImageHolder));
  
  		mObjStatus = new boolean[ObjectRemovalCore.getTotalObjNum()];
  		Arrays.fill(mObjStatus, true);
  
  		if (PreviewBmp != null)
  		{
  			PreviewBmp.recycle();
  		}
  
  		paint = new Paint();
  		paint.setColor(0xFF00AAEA);
  		paint.setStrokeWidth(5);
  		paint.setPathEffect(new DashPathEffect(new float[] { 5, 5 }, 0));
  		
  		PreviewBmp = ObjectRemovalCore.getPreviewBitmap();
  		mPreviewWidth = PreviewBmp.getWidth();
  		mPreviewHeight = PreviewBmp.getHeight();
  		drawObjectRectOnBitmap(PreviewBmp, ObjectRemovalCore.getObjectInfoList(), ObjectRemovalCore.getObjBorderBitmap(paint));
  
  		if (PreviewBmp != null)
  		{
  			int rotation = ApplicationScreen.getGUIManager().getMatrixRotationForBitmap(mImageDataOrientation, mLayoutOrientation, mCameraMirrored);
  			
  			if(rotation != 0)
  			{
  				Matrix matrix = new Matrix();
  				matrix.postRotate(rotation);
  				Bitmap rotated = Bitmap.createBitmap(PreviewBmp, 0, 0, PreviewBmp.getWidth(), PreviewBmp.getHeight(),
  						matrix, true);
  				mImgView.setImageBitmap(rotated);
  			}
  			else
  				mImgView.setImageBitmap(PreviewBmp);
  		}
  
  		mHandler.sendEmptyMessage(MSG_END_OF_LOADING);
  	}
  
  	public void getDisplaySize()
  	{
  		Display display = ((WindowManager) ApplicationScreen.instance.getSystemService(Context.WINDOW_SERVICE))
  				.getDefaultDisplay();
  		Point dis = new Point();
  		display.getSize(dis);
  
  		CameraController.Size imageSize = CameraController.getCameraImageSize();
  		float imageRatio = (float) imageSize.getWidth() / (float) imageSize.getHeight();
  		float displayRatio = (float) dis.y / (float) dis.x;
  
  		if (imageRatio > displayRatio)
  		{
  			mDisplayWidth = dis.y;
  			mDisplayHeight = (int) ((float) dis.y / (float) imageRatio);
  		} else
  		{
  			mDisplayWidth = (int) ((float) dis.x * (float) imageRatio);
  			mDisplayHeight = dis.x;
  		}
  		return;
  	}
  
  	private void setupImageView()
  	{
  		mImgView.setOnTouchListener(new View.OnTouchListener()
  		{
  			@Override
  			public boolean onTouch(View v, MotionEvent event)
  			{
  				if (event.getAction() == MotionEvent.ACTION_DOWN)
  				{
  					if (finishing)
  						return true;
  					float x = event.getX();
  					float y = event.getY();
  					
  					//Image data has different orientation according to device's sensor orientation
  					//Object removal operate with data as is, it means that seeing preview is differ by orientation from processing data.
  					//So we have to calculate new touch coordinates for real data
  					if(mSensorOrientation == 270)
  					{
  						x = mDisplayWidth - event.getY();
  						y = event.getX();
  					}
  					else if(mSensorOrientation == 90)
  					{
  						x = event.getY();
  						y = mDisplayHeight - 1 - event.getX();
  					}
  					else if(mSensorOrientation == 180)
  					{
  						x = mDisplayHeight - 1 - event.getX();
  						y = mDisplayWidth - 1 - event.getY();
  					}
  					
  					int objIndex = 0;
  					try
  					{
  						objIndex = ObjectRemovalCore.getOccupiedObject(x, y);
  					} catch (Exception e)
  					{
  						e.printStackTrace();
  					}
  
  					if (objIndex >= 1)
  					{
  						mObjStatus[objIndex - 1] = !mObjStatus[objIndex - 1];
  					}
  					mHandler.sendEmptyMessage(MSG_REDRAW);
  				}
  				return false;
  			}
  		});
  	}
  
  	public void setupSaveButton()
  	{
  		// put save button on screen
  		mSaveButton = new Button(ApplicationScreen.instance);
  		mSaveButton.setBackgroundResource(R.drawable.button_save_background);
  		mSaveButton.setOnClickListener(this);
  		LayoutParams saveLayoutParams = new LayoutParams(
  				(int) (ApplicationScreen.getMainContext().getResources().getDimension(R.dimen.postprocessing_savebutton_size)),
  				(int) (ApplicationScreen.getMainContext().getResources().getDimension(R.dimen.postprocessing_savebutton_size)));
  		saveLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
  		saveLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
  		float density = ApplicationScreen.getAppResources().getDisplayMetrics().density;
  		saveLayoutParams.setMargins((int) (density * 8), (int) (density * 8), 0, 0);
  		((RelativeLayout) postProcessingView.findViewById(R.id.objectremovalLayout)).addView(mSaveButton,
  				saveLayoutParams);
  		mSaveButton.setRotation(mLayoutOrientationCurrent);
  	}
  
  	public void onOrientationChanged(int orientation)
  	{
  		if (orientation != mDisplayOrientationCurrent)
  		{
  			mLayoutOrientationCurrent = (orientation == 0 || orientation == 180) ? orientation + 90 : orientation - 90;
  			mDisplayOrientationCurrent = orientation;
  			if (postProcessingRun)
  				mSaveButton.setRotation(mLayoutOrientationCurrent);
  		}
  	}
  
  	@Override
  	public void onClick(View v)
  	{
  		if (v == mSaveButton)
  		{
  			if (finishing)
  				return;
  			finishing = true;
  			mHandler.sendEmptyMessage(MSG_SAVE);
  		}
  	}
  
  	public void savePicture(Context context)
  	{
  		byte[] result = ObjectRemovalCore.processingSaveData();
  		int frame_len = result.length;
  		int frame = SwapHeap.SwapToHeap(result);
  		PluginManager.getInstance().addToSharedMem("resultframeformat1" + sessionID, "jpeg");
  		PluginManager.getInstance().addToSharedMem("resultframe1" + sessionID, String.valueOf(frame));
  		PluginManager.getInstance().addToSharedMem("resultframelen1" + sessionID, String.valueOf(frame_len));
  
  		PluginManager.getInstance().addToSharedMem("resultframeorientation1" + sessionID,String.valueOf(mImageDataOrientation));
  		PluginManager.getInstance().addToSharedMem("resultframemirrored1" + sessionID, String.valueOf(mCameraMirrored));
  
  		PluginManager.getInstance().addToSharedMem("amountofresultframes" + sessionID, String.valueOf(1));
  
  		PluginManager.getInstance().addToSharedMem("sessionID", String.valueOf(sessionID));
  	}
  
  	@Override
  	public boolean handleMessage(Message msg)
  	{
  		switch (msg.what)
  		{
  		case MSG_END_OF_LOADING:
  			setupImageView();
  			setupSaveButton();
  			postProcessingRun = true;
  			break;
  		case MSG_SAVE:
  			try
  			{
  				ObjectRemovalCore.setObjectList(mObjStatus);
  			} catch (Exception e)
  			{
  				e.printStackTrace();
  			}
  			savePicture(ApplicationScreen.getMainContext());
  			mHandler.sendEmptyMessage(MSG_LEAVING);
  			break;
  		case MSG_LEAVING:
  			if (released)
  				return false;
  			ApplicationScreen.getMessageHandler().sendEmptyMessage(ApplicationInterface.MSG_POSTPROCESSING_FINISHED);
  			
  			ObjectRemovalCore.getInstance().clear();
  
  			PluginManager.getInstance().sendMessage(ApplicationInterface.MSG_BROADCAST, 
  					ApplicationInterface.MSG_CONTROL_UNLOCKED);
  
  			ApplicationScreen.getGUIManager().lockControls = false;
  
  			postProcessingRun = false;
  
  			ObjectRemovalCore.release();
  			released = true;
  			break;
  
  		case MSG_REDRAW:
  			if (PreviewBmp != null)
  				PreviewBmp.recycle();
  			if (finishing)
  				return true;
  			PreviewBmp = ObjectRemovalCore.getPreviewBitmap();
  			mPreviewWidth = PreviewBmp.getWidth();
  			mPreviewHeight = PreviewBmp.getHeight();
  			drawObjectRectOnBitmap(PreviewBmp, ObjectRemovalCore.getObjectInfoList(), ObjectRemovalCore.getObjBorderBitmap(paint));
  			if (PreviewBmp != null)
  			{
  				int rotation = ApplicationScreen.getGUIManager().getMatrixRotationForBitmap(mImageDataOrientation, mLayoutOrientation, mCameraMirrored);
  				if(rotation != 0)
  				{
  					Matrix matrix = new Matrix();
  					matrix.postRotate(rotation);
  					Bitmap rotated = Bitmap.createBitmap(PreviewBmp, 0, 0, PreviewBmp.getWidth(), PreviewBmp.getHeight(),
  							matrix, true);
  					mImgView.setImageBitmap(rotated);
  				}
  				else
  					mImgView.setImageBitmap(PreviewBmp);	
  			}
  			break;
  		default:
  			break;
  		}
  		return true;
  	}
  
  	public boolean onKeyDown(int keyCode, KeyEvent event)
  	{
  		if (keyCode == KeyEvent.KEYCODE_BACK
  				&& ApplicationScreen.instance.findViewById(R.id.postprocessingLayout).getVisibility() == View.VISIBLE)
  		{
  			if (finishing)
  				return true;
  			finishing = true;
  			mHandler.sendEmptyMessage(MSG_LEAVING);
  			return true;
  		}
  
  		return false;
  	}
  
  	private void drawObjectRectOnBitmap(Bitmap bitmap, ObjectInfo[] objInfo, ObjBorderInfo[] boderInfo)
  	{
  		float ratio = 0.f;
  
  		Paint paint = new Paint();
  		paint.setColor(Color.rgb(0, 255, 0));
  		paint.setStrokeWidth(6);
  		paint.setAlpha(255);
  		paint.setStyle(Paint.Style.STROKE);
  		paint.setStrokeJoin(Paint.Join.ROUND);
  		paint.setStrokeCap(Paint.Cap.ROUND);
  
  		switch (mAngle)
  		{
  		case 0:
  			ratio = (float) 1.f;
  			break;
  		case 90:
  			ratio = (float) bitmap.getWidth() / (float) bitmap.getHeight();
  			break;
  		case 180:
  			ratio = (float) 1.f;
  			break;
  		case 270:
  			ratio = (float) bitmap.getWidth() / (float) bitmap.getHeight();
  			break;
  		default:
  			break;
  		}
  
  		Canvas c = new Canvas(bitmap);
  
  		int i = 0;
  		for (ObjectInfo obj : objInfo)
  		{
  			if (obj.getThumbnail() == null)
  			{
  				continue;
  			}
  			float left = (float) obj.getRect().left / ratio;
  			float top = (float) obj.getRect().top / ratio;
  			float right = (float) obj.getRect().right / ratio;
  			float bottom = (float) obj.getRect().bottom / ratio;
  
  			Rect newRect = new Rect((int) Math.round(left), (int) Math.round(top), (int) Math.round(right),
  					(int) Math.round(bottom));
  
  			if (!mObjStatus[i])
  			{
  				c.drawBitmap(obj.getThumbnail(), null, newRect, paint);
  			}
  			i++;
  		}
  
  		Paint p;
  		p = new Paint();
  
  		for (ObjBorderInfo obj : boderInfo)
  		{
  			if (obj == null)
  			{
  				continue;
  			}
  			if (obj.getThumbnail() == null)
  			{
  				continue;
  			}
  
  			Rect newRect = obj.getRect();
  
  			c.drawBitmap(obj.getThumbnail(), null, newRect, p);
  		}
  
  		return;
  	}
  
  	/************************************************
  	 * POST PROCESSING END
  	 ************************************************/
  
  	private void getPrefs()
  	{
  		/*
  		 Code commented out because there are no correspondent controls exposed to the user
  		 ToDo: either delete (more likely), or add these controls as advanced
  		 
  		// Get the xml/preferences.xml preferences
  		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen.instance
  				.getBaseContext());
  		mSensitivity = prefs.getInt("Sensitivity", 19); // Should we manage this
  														// parameter or it's
  														// final value of 19?
  		mMinSize = prefs.getInt("MinSize", 1000); // Should we manage this
  													// parameter or it's final
  													// value of 1000?
  		mGhosting = prefs.getString("Ghosting", "2"); // Should we manage this
  														// parameter or it's
  														// final value of 2?
  		*/
  	}
  }
  