/*
 * Copyright (C) 2014 [email protected] <[email protected]>
 *
 * This program 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 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */
package com.achep.acdisplay.services;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.util.Log;

import com.achep.acdisplay.Config;
import com.achep.acdisplay.R;
import com.achep.base.AppHeap;
import com.achep.base.utils.FileUtils;
import com.achep.base.utils.power.PowerUtils;

import java.io.File;
import java.util.LinkedList;

import static com.achep.base.Build.DEBUG;

/**
 * Created by achep on 24.08.14.
 */
public class SensorsDumpService extends BathService.ChildService implements
        SensorEventListener {

    private static final String TAG = "SensorsDumpService";

    private static final char DIVIDER = ';';
    private static final char NEW_LINE = '\n';

    private static final int MAX_SIZE = 2500;

    private SensorManager mSensorManager;
    private final int[] mSensorTypes = new int[]{
            Sensor.TYPE_GYROSCOPE, Sensor.TYPE_ACCELEROMETER,
    };

    private final LinkedList<Event> mEventList = new LinkedList<>();

    private static class Event {
        long timestamp;
        float[] values;
        int sensor;
    }

    private Handler mHandler = new Handler();
    private Receiver mReceiver = new Receiver();

    private class Receiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            switch (intent.getAction()) {
                case Intent.ACTION_SCREEN_ON:
                    startListening();

                    // Stop listening after some minutes to keep battery.
                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            synchronized (mEventList) {
                                stopListening();
                                mEventList.clear();
                            }
                        }
                    }, 120 * 1000);
                    break;
                case Intent.ACTION_SCREEN_OFF:
                    stopListening();
                    dropToStorage();
                    break;
            }
        }
    }

    /**
     * Starts or stops this service as required by settings and device's state.
     */
    public static void handleState(@NonNull Context context) {
        Config config = Config.getInstance();

        boolean onlyWhileChangingOption = !config.isEnabledOnlyWhileCharging()
                || PowerUtils.isPlugged(context);

        if (config.isEnabled()
                && config.isDevSensorsDumpEnabled()
                && onlyWhileChangingOption) {
            BathService.startService(context, SensorsDumpService.class);
        } else {
            BathService.stopService(context, SensorsDumpService.class);
        }
    }

    @Override
    public void onCreate() {
        Context context = getContext();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
        intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY - 1);
        context.registerReceiver(mReceiver, intentFilter);

        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
    }

    @Override
    public void onDestroy() {
        getContext().unregisterReceiver(mReceiver);
        stopListening();

        // Watch for the leaks
        AppHeap.getRefWatcher().watch(this);
    }

    @Override
    public String getLabel() {
        return getContext().getString(R.string.service_bath_active_mode_dump);
    }

    private void startListening() {
        for (int type : mSensorTypes) {
            Sensor sensor = mSensorManager.getDefaultSensor(type);
            if (sensor != null) {
                if (DEBUG) Log.d(TAG, "Listening to " + sensor.getName() + " sensor...");
                mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME);
            }
        }
    }

    private void stopListening() {
        if (DEBUG) Log.d(TAG, "Stopping listening...");
        mSensorManager.unregisterListener(this);
        mHandler.removeCallbacksAndMessages(null);
    }

    private void dropToStorage() {
        synchronized (mEventList) {
            if (DEBUG) Log.d(TAG, "Dumping sensors data to file...");
            if (mEventList.size() == 0) {
                return;
            }

            StringBuilder sb = new StringBuilder();
            for (Event event : mEventList) {
                sb.append(event.timestamp).append(DIVIDER);
                sb.append(event.sensor).append(DIVIDER);
                for (float f : event.values) sb.append(f).append(DIVIDER);
                sb.append(NEW_LINE);
            }

            String filename = "dump_sensors_" + SystemClock.elapsedRealtime() + ".txt";
            File file = new File(getContext().getFilesDir(), filename);
            FileUtils.writeToFile(file, sb);

            mEventList.clear();
        }
    }

    @Override
    public void onSensorChanged(SensorEvent sensorEvent) {
        synchronized (mEventList) {
            Event event = new Event();
            event.timestamp = SystemClock.elapsedRealtime();
            event.values = sensorEvent.values.clone();
            event.sensor = sensorEvent.sensor.getType();
            mEventList.add(event);

            int size = mEventList.size();
            if (size > MAX_SIZE) {
                mEventList.remove(0);
            }
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int i) { /* unused */ }

}