package com.grigowashere.aismap.sensors;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;

/**
 * Класс для работы с магнитным компасом устройства
 */
public class CompassSensor implements SensorEventListener {
    private static final String TAG = "CompassSensor";
    
    private SensorManager sensorManager;
    private Sensor accelerometer;
    private Sensor magnetometer;
    
    private float[] accelerometerReading = new float[3];
    private float[] magnetometerReading = new float[3];
    
    private float[] rotationMatrix = new float[9];
    private float[] orientationAngles = new float[3];
    
    private CompassListener compassListener;
    private boolean isListening = false;
    
    // Скользящий фильтр для сглаживания значений
    private static final int FILTER_SIZE = 60;
    private float[] azimuthBuffer = new float[FILTER_SIZE];
    private int bufferIndex = 0;
    private boolean bufferFull = false;
    
    public interface CompassListener {
        void onCompassChanged(float azimuth);
    }
    
    public CompassSensor(Context context) {
        sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        magnetometer = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
    }
    
    public void startListening(CompassListener listener) {
        if (isListening) {
            stopListening();
        }
        
        this.compassListener = listener;
        
        if (accelerometer != null && magnetometer != null) {
            sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME);
            sensorManager.registerListener(this, magnetometer, SensorManager.SENSOR_DELAY_GAME);
            isListening = true;
            Log.d(TAG, "Compass sensor started");
        } else {
            Log.e(TAG, "Compass sensors not available");
        }
    }
    
    public void stopListening() {
        if (isListening) {
            sensorManager.unregisterListener(this);
            isListening = false;
            compassListener = null;
            
            // Сбрасываем фильтр
            resetFilter();
            
            Log.d(TAG, "Compass sensor stopped");
        }
    }
    
    /**
     * Сбрасывает фильтр
     */
    private void resetFilter() {
        bufferIndex = 0;
        bufferFull = false;
        for (int i = 0; i < FILTER_SIZE; i++) {
            azimuthBuffer[i] = 0;
        }
    }
    
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.length);
        } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
            System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.length);
        }
        
        // Обновляем ориентацию
        updateOrientationAngles();
    }
    
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // Можно добавить логику для обработки изменений точности
    }
    
    private void updateOrientationAngles() {
        // Обновляем матрицу вращения
        SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading);
        
        // Получаем углы ориентации
        SensorManager.getOrientation(rotationMatrix, orientationAngles);
        
        // Азимут (направление на север) в радианах, конвертируем в градусы
        float azimuthInRadians = orientationAngles[0];
        float azimuthInDegrees = (float) Math.toDegrees(azimuthInRadians);
        
        // Нормализуем до диапазона 0-360
        if (azimuthInDegrees < 0) {
            azimuthInDegrees += 360;
        }
        
        // Применяем скользящий фильтр
        float filteredAzimuth = applyLowPassFilter(azimuthInDegrees);
        
        // Уведомляем слушателя
        if (compassListener != null) {
            compassListener.onCompassChanged(filteredAzimuth);
        }
    }
    
    /**
     * Применяет скользящий фильтр для сглаживания значений
     */
    private float applyLowPassFilter(float newValue) {
        // Добавляем новое значение в буфер
        azimuthBuffer[bufferIndex] = newValue;
        bufferIndex = (bufferIndex + 1) % FILTER_SIZE;
        
        if (bufferIndex == 0) {
            bufferFull = true;
        }
        
        // Вычисляем среднее значение
        float sum = 0;
        int count = bufferFull ? FILTER_SIZE : bufferIndex;
        
        for (int i = 0; i < count; i++) {
            sum += azimuthBuffer[i];
        }
        
        return sum / count;
    }
    
    public boolean isAvailable() {
        return accelerometer != null && magnetometer != null;
    }
    
    public boolean isListening() {
        return isListening;
    }
}
