package com.grigowashere.aismap.controllers;

import android.content.Context;
import android.location.LocationManager;
import android.location.OnNmeaMessageListener;
import android.location.LocationListener;
import android.os.Build;
import android.util.Log;
import android.location.GnssStatus;
import android.location.GpsStatus;
import android.location.GpsStatus.Listener;
import android.os.Looper;
import android.os.Handler;


/**
 * Контроллер для Android NMEA Listener
 * Использует встроенный GPS для получения NMEA сообщений
 */
public class AndroidNMEAListener implements OnNmeaMessageListener {
    
    private static final String TAG = "AndroidNMEAListener";
    
    private Context context;
    private LocationManager locationManager;
    private NMEAMessageCallback callback;
    private boolean isListening;
    private int satelliteCount;
    private LocationListener locationListener;
    private GpsStatus.Listener gpsStatusListener;
    private GnssStatus.Callback gnssStatusCallback;
    private Handler activationHandler;
    private boolean hasReceivedNMEA;
    
    public interface NMEAMessageCallback {
        void onNMEAMessage(String message, long timestamp);
        void onGPSStatusChanged(int status);
        void onError(String error);
    }
    
    public AndroidNMEAListener(Context context) {
        this.context = context;
        this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        this.isListening = false;
        this.activationHandler = new Handler(Looper.getMainLooper());
        this.hasReceivedNMEA = false;
    }
    
    public void setCallback(NMEAMessageCallback callback) {
        this.callback = callback;
    }
    
    /**
     * Запускает прослушивание NMEA сообщений
     */
    public boolean startListening() {
        if (isListening) {
            //Log.w(TAG, "NMEA слушатель уже запущен");
            return true;
        }
        
        if (locationManager == null) {
            //Log.e(TAG, "LocationManager недоступен");
            if (callback != null) {
                callback.onError("LocationManager недоступен");
            }
            return false;
        }
        
        // Проверяем все доступные провайдеры
        //Log.i(TAG, "=== ДИАГНОСТИКА GPS ===");
        //Log.i(TAG, "Доступные провайдеры:");
        for (String provider : locationManager.getAllProviders()) {
            boolean enabled = locationManager.isProviderEnabled(provider);
            //Log.i(TAG, "  " + provider + ": " + (enabled ? "включен" : "отключен"));
        }
        
        if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            //Log.w(TAG, "GPS провайдер отключен");
            if (callback != null) {
                callback.onError("GPS провайдер отключен");
            }
            return false;
        }
        
        // Проверяем разрешения
        if (context.checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) 
                != android.content.pm.PackageManager.PERMISSION_GRANTED) {
            //Log.e(TAG, "Нет разрешения ACCESS_FINE_LOCATION");
            if (callback != null) {
                callback.onError("Нет разрешения ACCESS_FINE_LOCATION");
            }
            return false;
        }
        
        //Log.i(TAG, "Разрешения GPS получены");
        
        try {
            //Log.i(TAG, "=== РЕГИСТРАЦИЯ СЛУШАТЕЛЕЙ ===");
            
            // Регистрируем NMEA слушатель с минимальным интервалом
            locationManager.addNmeaListener(this, new Handler(Looper.getMainLooper()));
            //Log.i(TAG, "✅ NMEA слушатель зарегистрирован");
            
            // Регистрируем GPS статус слушатель (для старых версий Android)
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
                gpsStatusListener = new GpsStatus.Listener() {
                    @Override
                    public void onGpsStatusChanged(int event) {
                        String eventName = getGpsEventName(event);
                        //Log.d(TAG, "GPS статус изменился: " + event + " (" + eventName + ")");
                        if (callback != null) {
                            callback.onGPSStatusChanged(event);
                        }
                        
                        // При получении первого фиксирования запрашиваем обновления
                        if (event == GpsStatus.GPS_EVENT_FIRST_FIX) {
                            //Log.i(TAG, "🎯 Получено первое GPS фиксирование, активируем NMEA");
                            requestLocationUpdates();
                        }
                    }
                };
                locationManager.addGpsStatusListener(gpsStatusListener);
                //Log.i(TAG, "✅ GPS статус слушатель зарегистрирован (старый API)");
            }
            
            // Регистрируем GNSS статус callback (для новых версий Android)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                gnssStatusCallback = new GnssStatus.Callback() {
                    @Override
                    public void onStarted() {
                        //Log.i(TAG, "🚀 GNSS начал работу");
                        if (callback != null) {
                            callback.onGPSStatusChanged(1); // GPS_EVENT_STARTED
                        }
                        // При старте GNSS сразу запрашиваем обновления
                        requestLocationUpdates();
                    }
                    
                    @Override
                    public void onStopped() {
                        //Log.i(TAG, "⏹️ GNSS остановлен");
                        if (callback != null) {
                            callback.onGPSStatusChanged(2); // GPS_EVENT_STOPPED
                        }
                    }
                    
                    @Override
                    public void onFirstFix(int ttffMillis) {
                        //Log.i(TAG, "🎯 GNSS получил первое фиксирование за " + ttffMillis + "мс");
                        if (callback != null) {
                            callback.onGPSStatusChanged(3); // GPS_EVENT_FIRST_FIX
                        }
                        // При первом фиксировании также запрашиваем обновления
                        requestLocationUpdates();
                    }
                    
                    @Override
                    public void onSatelliteStatusChanged(GnssStatus status) {
                        int count = status.getSatelliteCount();
                        //Log.d(TAG, "GNSS статус спутников изменился, количество: " + count);
                        
                        // Подсчитываем количество спутников
                        satelliteCount = count;
                        
                        // Логируем детали по спутникам
                        for (int i = 0; i < count; i++) {
                            int constellationType = status.getConstellationType(i);
                            float cn0DbHz = status.getCn0DbHz(i);
                            boolean usedInFix = status.usedInFix(i);
                            String constellationName = getConstellationName(constellationType);
                            //Log.d(TAG, "  Спутник " + i + ": " + constellationName + 
        //                          ", сигнал=" + cn0DbHz + "dB-Hz, используется=" + usedInFix);
                        }
                        
                        if (callback != null) {
                            callback.onGPSStatusChanged(4); // GPS_EVENT_SATELLITE_STATUS
                        }
                        
                        // Если есть спутники, но нет NMEA - принудительно активируем GPS
                        if (count > 0 && !isListening) {
                            //Log.i(TAG, "Спутники видны, но слушатель не активен. Активируем GPS...");
                 //           requestLocationUpdates();
                        }
                    }
                };
                locationManager.registerGnssStatusCallback(gnssStatusCallback, new Handler(Looper.getMainLooper()));
                //Log.i(TAG, "✅ GNSS статус callback зарегистрирован (новый API)");
            }
            
            // Для старых версий Android сразу запрашиваем обновления
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
                //Log.i(TAG, "📱 Старая версия Android, сразу запрашиваем location updates");
                requestLocationUpdates();
            }
            
            // ВАЖНО: Принудительно активируем GPS для получения NMEA
            //Log.i(TAG, "🔧 Принудительно активируем GPS для получения NMEA...");
            requestLocationUpdates();
            
            // Запускаем таймер для принудительной повторной активации
            startActivationTimer();
            
            isListening = true;
            //Log.i(TAG, "🎉 NMEA слушатель успешно запущен!");
            //Log.i(TAG, "💡 Теперь GPS должен начать отправлять NMEA сообщения");
            return true;
            
        } catch (SecurityException e) {
            //Log.e(TAG, "❌ Нет разрешений для доступа к GPS: " + e.getMessage());
            if (callback != null) {
                callback.onError("Нет разрешений для доступа к GPS");
            }
            return false;
        } catch (Exception e) {
            //Log.e(TAG, "❌ Ошибка при запуске NMEA слушателя: " + e.getMessage());
            e.printStackTrace();
            if (callback != null) {
                callback.onError("Ошибка запуска: " + e.getMessage());
            }
            return false;
        }
    }
    
    /**
     * Запрашивает обновления локации для активации GPS
     */
    private void requestLocationUpdates() {
        try {
            //Log.i(TAG, "🔧 Запрашиваем обновления локации для активации NMEA...");
            
            // Создаем слушатель локации с минимальными интервалами
            locationListener = new LocationListener() {
                @Override 
                public void onLocationChanged(android.location.Location location) {
                    //Log.d(TAG, "📍 Location обновлен: " + location.getLatitude() + ", " + location.getLongitude());
                    //Log.d(TAG, "📍 Точность: " + location.getAccuracy() + "м, время: " + location.getTime());
                }
                
                @Override 
                public void onStatusChanged(String provider, int status, android.os.Bundle extras) {
                    String statusName = getLocationStatusName(status);
                    //Log.d(TAG, "📍 Location статус изменился: " + provider + " = " + status + " (" + statusName + ")");
                }
                
                @Override 
                public void onProviderEnabled(String provider) {
                    //Log.i(TAG, "📍 Location провайдер включен: " + provider);
                }
                
                @Override 
                public void onProviderDisabled(String provider) {
                    //Log.w(TAG, "📍 Location провайдер отключен: " + provider);
                }
            };
            
            // Запрашиваем обновления с минимальными интервалами для активации GPS
            locationManager.requestLocationUpdates(
                LocationManager.GPS_PROVIDER, 
                100L,  // минимальный интервал в мс (long вместо int)
                0.0f,  // минимальное расстояние в метрах (float вместо int)
                locationListener,
                Looper.getMainLooper()  // Looper вместо Handler
            );
            
            //Log.i(TAG, "✅ Location updates запрошены с минимальным интервалом (100мс)");
            
            // Дополнительно запрашиваем одиночное обновление для принудительной активации
            try {
                locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, 
                    new LocationListener() {
                        @Override public void onLocationChanged(android.location.Location location) {
                            //Log.i(TAG, "🎯 Одиночное обновление получено: " + location.getLatitude() + ", " + location.getLongitude());
                        }
                        @Override public void onStatusChanged(String provider, int status, android.os.Bundle extras) {}
                        @Override public void onProviderEnabled(String provider) {}
                        @Override public void onProviderDisabled(String provider) {}
                    }, Looper.getMainLooper());  // Looper вместо Handler
                //Log.i(TAG, "✅ Одиночное обновление запрошено");
            } catch (Exception e) {
                //Log.w(TAG, "⚠️ Не удалось запросить одиночное обновление: " + e.getMessage());
            }
            
            // Дополнительно запрашиваем обновления от всех доступных провайдеров
            try {
                if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
                    locationManager.requestLocationUpdates(
                        LocationManager.NETWORK_PROVIDER,
                        1000L,  // 1 секунда
                        0.0f,
                        locationListener,
                        Looper.getMainLooper()
                    );
                    //Log.i(TAG, "✅ Network провайдер также активирован");
                }
            } catch (Exception e) {
                //Log.w(TAG, "⚠️ Не удалось активировать network провайдер: " + e.getMessage());
            }
            
        } catch (Exception e) {
            //Log.e(TAG, "❌ Ошибка при запросе location updates: " + e.getMessage());
        }
    }
    
    /**
     * Останавливает прослушивание NMEA сообщений
     */
    public void stopListening() {
        if (!isListening) {
            return;
        }
        
        try {
            if (locationManager != null) {
                // Убираем NMEA слушатель
                locationManager.removeNmeaListener(this);
                
                // Убираем GNSS статус callback
                if (gnssStatusCallback != null) {
                    locationManager.unregisterGnssStatusCallback(gnssStatusCallback);
                    gnssStatusCallback = null;
                }
                
                // Убираем GPS статус слушатель (старый API)
                if (gpsStatusListener != null) {
                    locationManager.removeGpsStatusListener(gpsStatusListener);
                    gpsStatusListener = null;
                }
                
                // Убираем Location updates
                if (locationListener != null) {
                    locationManager.removeUpdates(locationListener);
                    locationListener = null;
                    //Log.i(TAG, "Location updates остановлены");
                }
            }
            
            // Останавливаем таймер активации
            stopActivationTimer();
            
            isListening = false;
            hasReceivedNMEA = false;
            //Log.i(TAG, "NMEA слушатель остановлен");
            
        } catch (Exception e) {
            //Log.e(TAG, "Ошибка при остановке NMEA слушателя: " + e.getMessage());
        }
    }
    
    /**
     * Callback для NMEA сообщений (API 24+)
     */
    @Override
    public void onNmeaMessage(String message, long timestamp) {
        if (message == null || message.trim().isEmpty()) {
            //Log.w(TAG, "⚠️ Получено пустое NMEA сообщение");
            return;
        }
        
        // Отмечаем, что NMEA получено
        hasReceivedNMEA = true;
        
        // Анализируем тип NMEA сообщения
        String messageType = getNMEAMessageType(message);
        String LogPrefix = "🎯 NMEA [" + messageType + "]";
        
        //Log.i(TAG, //LogPrefix + " получено: " + message);
        //Log.d(TAG, //LogPrefix + " timestamp: " + timestamp + " (" + new java.util.Date(timestamp) + ")");
        
        // Дополнительная диагностика для RMC сообщений
        if ("RMC".equals(messageType)) {
            //Log.i(TAG, //LogPrefix + " 🚢 RMC сообщение получено - GPS активен!");
            // Можно добавить парсинг RMC для получения дополнительной информации
            parseRMC(message);
        }
        
        // Останавливаем таймер активации, так как NMEA приходит
        stopActivationTimer();
        
        if (callback != null) {
            //Log.d(TAG, //LogPrefix + " Отправляем сообщение в callback");
            callback.onNMEAMessage(message, timestamp);
        } else {
            //Log.w(TAG, //LogPrefix + " ❌ Callback не установлен!");
        }
    }
    
    /**
     * Определяет тип NMEA сообщения
     */
    private String getNMEAMessageType(String message) {
        if (message == null || message.length() < 6) {
            return "UNKNOWN";
        }
        
        // NMEA сообщения начинаются с $ и содержат тип после $
        if (message.startsWith("$")) {
            String[] parts = message.split(",");
            if (parts.length > 0) {
                // Убираем $ и берем первые 3 символа
                String type = parts[0].substring(1);
                if (type.length() >= 3) {
                    return type.substring(0, 3);
                }
            }
        }
        
        return "UNKNOWN";
    }
    
    /**
     * Парсит RMC сообщение для диагностики
     */
    private void parseRMC(String message) {
        try {
            String[] parts = message.split(",");
            if (parts.length >= 12) {
                String time = parts[1];
                String status = parts[2];
                String lat = parts[3];
                String latDir = parts[4];
                String lon = parts[5];
                String lonDir = parts[6];
                String speed = parts[7];
                String course = parts[8];
                String date = parts[9];
                
                //Log.d(TAG, "🚢 RMC детали:");
                //Log.d(TAG, "  Время: " + time + ", Дата: " + date);
                //Log.d(TAG, "  Статус: " + (status.equals("A") ? "Активен" : "Неактивен"));
                //Log.d(TAG, "  Координаты: " + lat + latDir + ", " + lon + lonDir);
                //Log.d(TAG, "  Скорость: " + speed + " узлов, Курс: " + course + "°");
            }
        } catch (Exception e) {
            //Log.w(TAG, "⚠️ Не удалось распарсить RMC: " + e.getMessage());
        }
    }
    
    /**
     * Проверяет, запущен ли слушатель
     */
    public boolean isListening() {
        return isListening;
    }
    
    /**
     * Проверяет доступность GPS
     */
    public boolean isGPSAvailable() {
        if (locationManager == null) {
            return false;
        }
        
        boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        boolean networkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
        boolean passiveEnabled = locationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER);
        
        //Log.d(TAG, "GPS провайдер: " + (gpsEnabled ? "включен" : "отключен"));
        //Log.d(TAG, "Network провайдер: " + (networkEnabled ? "включен" : "отключен"));
        //Log.d(TAG, "Passive провайдер: " + (passiveEnabled ? "включен" : "отключен"));
        
        return gpsEnabled;
    }
    
    /**
     * Получает детальную информацию о состоянии GPS
     */
    public String getGPSStatusInfo() {
        if (locationManager == null) {
            return "LocationManager недоступен";
        }
        
        StringBuilder info = new StringBuilder();
        info.append("GPS провайдер: ").append(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) ? "включен" : "отключен").append("\n");
        info.append("Network провайдер: ").append(locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) ? "включен" : "отключен").append("\n");
        info.append("Passive провайдер: ").append(locationManager.isProviderEnabled(LocationManager.PASSIVE_PROVIDER) ? "включен" : "отключен").append("\n");
        info.append("Спутников: ").append(satelliteCount).append("\n");
        info.append("Слушатель активен: ").append(isListening ? "да" : "нет");
        
        return info.toString();
    }
    
    /**
     * Принудительно активирует GPS для получения NMEA
     */
    public void forceGPSActivation() {
        if (!isListening) {
            //Log.w(TAG, "Слушатель не запущен, запускаем...");
            startListening();
            return;
        }
        
        //Log.i(TAG, "Принудительно активируем GPS...");
        
        // Запрашиваем обновления локации с минимальными интервалами
        try {
            if (locationListener != null) {
                locationManager.removeUpdates(locationListener);
            }
            
            requestLocationUpdates();
            
            // Дополнительно запрашиваем одиночное обновление
            locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, 
                new LocationListener() {
                    @Override public void onLocationChanged(android.location.Location location) {
                        //Log.i(TAG, "Одиночное обновление получено: " + location.getLatitude() + ", " + location.getLongitude());
                    }
                    @Override public void onStatusChanged(String provider, int status, android.os.Bundle extras) {}
                    @Override public void onProviderEnabled(String provider) {}
                    @Override public void onProviderDisabled(String provider) {}
                }, Looper.getMainLooper());  // Looper вместо Handler
                
            //Log.i(TAG, "Принудительная активация GPS выполнена");
            
        } catch (Exception e) {
            //Log.e(TAG, "Ошибка при принудительной активации GPS: " + e.getMessage());
        }
    }
    
    /**
     * Запускает таймер для принудительной повторной активации GPS
     */
    private void startActivationTimer() {
        // Останавливаем предыдущий таймер
        activationHandler.removeCallbacksAndMessages(null);
        
        // Запускаем таймер на 5 секунд
        activationHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (isListening && !hasReceivedNMEA) {
                    //Log.w(TAG, "⏰ Таймер активации: NMEA не получено, принудительно активируем GPS...");
                    forceGPSActivation();
                    
                    // Повторяем каждые 10 секунд, пока не получим NMEA
                    activationHandler.postDelayed(this, 10000);
                }
            }
        }, 5000);
        
        //Log.i(TAG, "⏰ Таймер активации GPS запущен (5 сек)");
    }
    
    /**
     * Останавливает таймер активации
     */
    private void stopActivationTimer() {
        activationHandler.removeCallbacksAndMessages(null);
        //Log.d(TAG, "⏰ Таймер активации GPS остановлен");
    }
    
    /**
     * Получает количество спутников
     */
    public int getSatelliteCount() {
        return satelliteCount;
    }
    
    /**
     * Освобождает ресурсы
     */
    public void cleanup() {
        if (isListening) {
            stopListening();
        }
        
        // Останавливаем таймер активации
        stopActivationTimer();
        
        locationManager = null;
        activationHandler = null;
    }

    /**
     * Получает название GPS события
     */
    private String getGpsEventName(int event) {
        switch (event) {
            case GpsStatus.GPS_EVENT_STARTED: return "GPS_EVENT_STARTED";
            case GpsStatus.GPS_EVENT_STOPPED: return "GPS_EVENT_STOPPED";
            case GpsStatus.GPS_EVENT_FIRST_FIX: return "GPS_EVENT_FIRST_FIX";
            case GpsStatus.GPS_EVENT_SATELLITE_STATUS: return "GPS_EVENT_SATELLITE_STATUS";
            default: return "UNKNOWN(" + event + ")";
        }
    }
    
    /**
     * Получает название созвездия спутников
     */
    private String getConstellationName(int constellationType) {
        switch (constellationType) {
            case GnssStatus.CONSTELLATION_GPS: return "GPS";
            case GnssStatus.CONSTELLATION_GLONASS: return "GLONASS";
            case GnssStatus.CONSTELLATION_BEIDOU: return "BeiDou";
            case GnssStatus.CONSTELLATION_GALILEO: return "Galileo";
            case GnssStatus.CONSTELLATION_QZSS: return "QZSS";
            case GnssStatus.CONSTELLATION_SBAS: return "SBAS";
            case GnssStatus.CONSTELLATION_IRNSS: return "IRNSS";
            default: return "Unknown(" + constellationType + ")";
        }
    }

    /**
     * Получает название статуса локации
     */
    private String getLocationStatusName(int status) {
        switch (status) {
            case android.location.LocationProvider.AVAILABLE: return "AVAILABLE";
            case android.location.LocationProvider.TEMPORARILY_UNAVAILABLE: return "TEMPORARILY_UNAVAILABLE";
            case android.location.LocationProvider.OUT_OF_SERVICE: return "OUT_OF_SERVICE";
            default: return "UNKNOWN(" + status + ")";
        }
    }
}
