package com.lemberg.screenstabilizationdemo.activity;

import android.app.Activity;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.Toast;

import com.lemberg.screenstabilizationdemo.settings.AppSettings;
import com.lemberg.screenstabilizationdemo.Constants;
import com.lemberg.screenstabilizationdemo.view.LineGraphView;
import com.lemberg.screenstabilizationdemo.R;
import com.lemberg.screenstabilizationdemo.service.StabilizationService;
import com.lemberg.screenstabilizationdemo.dialog.SettingsDialog;
import com.lemberg.screenstabilizationdemo.utils.Utils;

public class MainActivity extends Activity
{
	private static final String TAG = MainActivity.class.getSimpleName();

	private View layoutSensor;

	private LineGraphView graph1;
	private LineGraphView graph2;
	private LineGraphView graph3;

	private SensorManager sensorManager;
	private Sensor accelerometer;

	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 AppSettings settings;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		settings = AppSettings.getAppSettings(getApplicationContext());

		initViews();

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

	@Override
	protected void onResume()
	{
		super.onResume();
		boolean ret = sensorManager.registerListener(sensorEventListener, accelerometer, SensorManager.SENSOR_DELAY_FASTEST);
		if (!ret)
		{
			Log.wtf(TAG, "Sensor listener registration failed");
			Toast.makeText(this, "Sensor listener registration failed", Toast.LENGTH_SHORT).show();
		}
	}

	@Override
	protected void onPause()
	{
		super.onPause();
		sensorManager.unregisterListener(sensorEventListener);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		getMenuInflater().inflate(R.menu.main, menu);

		Switch switchSvc = (Switch) menu.findItem(R.id.action_svc_switch).getActionView().findViewById(R.id.switch_svc);
		switchSvc.setChecked(settings.isServiceEnabled());

		switchSvc.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
		{
			@Override
			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
			{
				setSvcEnabled(isChecked);
			}
		});
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item)
	{
		switch (item.getItemId())
		{
			case R.id.action_settings:
				showSettings();
				return true;
		}
		return super.onOptionsItemSelected(item);
	}

	private void initViews()
	{
		View layoutRoot = findViewById(R.id.layout_root);
		layoutRoot.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v)
			{
				reset();
			}
		});

		layoutSensor = findViewById(R.id.layout_sensor);

		graph1 = (LineGraphView) findViewById(R.id.graph1);
		graph2 = (LineGraphView) findViewById(R.id.graph2);
		graph3 = (LineGraphView) findViewById(R.id.graph3);

		graph1.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v)
			{
				graph1.clear();
			}
		});

		graph2.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v)
			{
				graph2.clear();
			}
		});

		graph3.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v)
			{
				graph3.clear();
			}
		});
	}

	private void showSettings()
	{
		SettingsDialog dlg = SettingsDialog.newInstance();
		dlg.show(getFragmentManager(), "settings");
	}

	private void setSvcEnabled(boolean enabled)
	{
		if (enabled) startService(new Intent(this, StabilizationService.class));
		else stopService(new Intent(this, StabilizationService.class));

		settings.setServiceEnabled(enabled);
		settings.saveDeferred();
	}

	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;

		layoutSensor.setTranslationX(0);
		layoutSensor.setTranslationY(0);
	}

	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;

			layoutSensor.setTranslationX(-position[0]);
			layoutSensor.setTranslationY(position[1]);

			graph1.setValue(acc[0]);
			graph2.setValue(acc[1]);
			graph3.setValue(acc[2]);
		}
	};
}