package com.lemberg.screenstabilizationdemo.service;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.os.Parcel;
import android.os.ServiceManager;
import android.app.Service;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.IBinder;
import android.util.Log;

import com.lemberg.screenstabilizationdemo.settings.AppSettings;
import com.lemberg.screenstabilizationdemo.Constants;
import com.lemberg.screenstabilizationdemo.utils.Utils;

public class StabilizationService extends Service
{
	private static final String TAG = StabilizationService.class.getSimpleName();

	private AppSettings settings;
	private SensorManager sensorManager;
	private Sensor accelerometer;

	private boolean accListenerRegistered = false;

	private final float[] tempAcc = new float[3];
	private final float[] acc = new float[3];
	private final float[] velocity = new float[3];
	private final float[] position = new float[3];
	private long timestamp = 0;

	private int x = 0, y = 0;

	private IBinder flinger = null;

	private final SensorEventListener sensorEventListener = new SensorEventListener()
	{
		@Override
		public void onAccuracyChanged(Sensor sensor, int accuracy)
		{
		}

		@Override
		public void onSensorChanged(SensorEvent event)
		{
			if (timestamp != 0)
			{
				tempAcc[0] = Utils.rangeValue(event.values[0], -Constants.MAX_ACC, Constants.MAX_ACC);
				tempAcc[1] = Utils.rangeValue(event.values[1], -Constants.MAX_ACC, Constants.MAX_ACC);
				tempAcc[2] = Utils.rangeValue(event.values[2], -Constants.MAX_ACC, Constants.MAX_ACC);

				Utils.lowPassFilter(tempAcc, acc, settings.getLowPassAlpha());

				float dt = (event.timestamp - timestamp) * Constants.NS2S;

				for(int index = 0; index < 3; ++index)
				{
					velocity[index] += acc[index] * dt - settings.getVelocityFriction() * velocity[index];
					velocity[index] = Utils.fixNanOrInfinite(velocity[index]);

					position[index] += velocity[index] * settings.getVelocityAmpl() * dt - settings.getPositionFriction() * position[index];
					position[index] = Utils.rangeValue(position[index], -Constants.MAX_POS_SHIFT, Constants.MAX_POS_SHIFT);
				}
			}
			else
			{
				velocity[0] = velocity[1] = velocity[2] = 0f;
				position[0] = position[1] = position[2] = 0f;

				acc[0] = Utils.rangeValue(event.values[0], -Constants.MAX_ACC, Constants.MAX_ACC);
				acc[1] = Utils.rangeValue(event.values[1], -Constants.MAX_ACC, Constants.MAX_ACC);
				acc[2] = Utils.rangeValue(event.values[2], -Constants.MAX_ACC, Constants.MAX_ACC);
			}

			timestamp = event.timestamp;

			int newPosX = Math.round(position[0]);
			int newPosY = Math.round(position[1]);
			if ((newPosX != x) || (newPosY != y))
			{
				x = newPosX;
				y = newPosY;
				setSurfaceFlingerTranslate(-x, y);
			}
		}
	};

	private final BroadcastReceiver screenOnReceiver = new BroadcastReceiver()
	{
		@Override
		public void onReceive(Context context, Intent intent)
		{
			Log.d(TAG, "Screen is on");
			setSurfaceFlingerTranslate(0, 0);
			reset();
			registerAccListener();
		}
	};

	private final BroadcastReceiver screenOffReceiver = new BroadcastReceiver()
	{
		@Override
		public void onReceive(Context context, Intent intent)
		{
			unregisterAccListener();
			setSurfaceFlingerTranslate(0, 0);
			Log.d(TAG, "Screen is off");
		}
	};

	public StabilizationService()
	{
	}

	@Override
	public IBinder onBind(Intent intent)
	{
		return null;
	}

	@Override
	public void onCreate()
	{
		super.onCreate();

		settings = AppSettings.getAppSettings(getApplicationContext());

		sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
		accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);

		registerReceiver(screenOnReceiver, new IntentFilter(Intent.ACTION_SCREEN_ON));
		registerReceiver(screenOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));

		if (Utils.isScreenOn(this)) registerAccListener();
	}

	@Override
	public void onDestroy()
	{
		unregisterReceiver(screenOnReceiver);
		unregisterReceiver(screenOffReceiver);

		unregisterAccListener();
		setSurfaceFlingerTranslate(0, 0);

		super.onDestroy();
	}

	private void reset()
	{
		position[0] = position[1] = position[2] = 0;
		velocity[0] = velocity[1] = velocity[2] = 0;
		acc[0] = acc[1] = acc[2] = 0;
		timestamp = 0;
		x = y = 0;
	}

	private void registerAccListener()
	{
		if (accListenerRegistered) return;
		accListenerRegistered = sensorManager.registerListener(sensorEventListener, accelerometer, SensorManager.SENSOR_DELAY_FASTEST);
		if (!accListenerRegistered)
		{
			Log.wtf(TAG, "Sensor listener not registered");
		}
	}

	private void unregisterAccListener()
	{
		if (accListenerRegistered)
		{
			accListenerRegistered = false;
			sensorManager.unregisterListener(sensorEventListener);
		}
	}

	private void setSurfaceFlingerTranslate(int x, int y)
	{
		try
		{
			if (flinger == null) flinger = ServiceManager.getService("SurfaceFlinger");
			if (flinger == null)
			{
				Log.wtf(TAG, "SurfaceFlinger is null");
				return;
			}

			Parcel data = Parcel.obtain();
			data.writeInterfaceToken("android.ui.ISurfaceComposer");
			data.writeInt(x);
			data.writeInt(y);
			flinger.transact(2020, data, null, 0);
			data.recycle();
		}
		catch(Exception e)
		{
			Log.e(TAG, "SurfaceFlinger error", e);
		}
	}

}