import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart';
//import 'package:gifunavi/widgets/debug_widget.dart';
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
import 'package:gifunavi/pages/destination/destination_controller.dart';
import 'package:gifunavi/pages/permission/permission.dart';

// LocationControllerクラスは、GetxControllerを継承したクラスであり、位置情報の管理を担当しています。
// LocationControllerは以下の機能を提供しています。
// LocationControllerは、アプリ全体で位置情報を一元管理するための重要なコンポーネントです。
//   他のコンポーネントは、LocationControllerから位置情報を取得し、位置情報に関連する機能を実装することができます。
//
// Features:
// * 現在の位置情報を保持し、他のコンポーネントからアクセスできるようにします。
// * 位置情報のストリームを管理し、位置情報の更新を監視します。
// * 位置情報サービスの有効性と権限の確認を行い、適切な処理を行います。
// * 位置情報のストリームを開始、停止、再開する機能を提供します。
// * 位置マーカーの位置情報をStreamControllerを通じて他のコンポーネントに通知します。
//
// Logic:
// 1. startPositionStreamメソッドで、Geolocator.getPositionStreamを使用して位置情報のストリームを開始します。
// 2. ストリームから位置情報を受信すると、LocationMarkerPositionオブジェクトを作成し、locationMarkerPositionStreamControllerに追加します。
// 3. 位置情報が取得できなかった場合や、エラーが発生した場合は、適切な処理を行います。
// 4. stopPositionStreamメソッドで、位置情報のストリームを一時停止することができます。
// 5. resumePositionStreamメソッドで、一時停止中の位置情報のストリームを再開することができます。
// 6. onCloseメソッドで、コントローラーのクローズ時に位置情報のストリームをキャンセルします。
//
class LocationController extends GetxController {
  // Reactive variable to hold the current position
  Rx<Position?> currentPosition = Rx<Position?>(null);	
				// 現在の位置情報を保持するReactive変数です。Rx<Position?>型で宣言されています。

  // Subscription to the position stream
  StreamSubscription<Position>? positionStream;			
				// 位置情報のストリームを保持する変数です。StreamSubscription<Position>型で宣言されています。

  LatLng? lastValidLocation;
  DateTime lastGPSDataReceivedTime = DateTime.now();  // 最後にGPSデータを受け取った時刻

  bool gpsDebugMode = true;
  /*
  // GPSシミュレーション用のメソッドを追加
  void setSimulationMode(bool value) {
    isSimulationMode = value;
  }

  // ====== Akira , GPS信号強度をシミュレート ==== ここから
  //

  //===== Akira Added 2024-4-9 start
  // GPSシミュレーション用の変数を追加　===> 本番では false にする。
  bool isSimulationMode = false;

  // GPS信号強度をシミュレートするための変数
  final Rx<String> _simulatedSignalStrength = Rx<String>('high');

  // GPS信号強度をシミュレートするための関数
  void setSimulatedSignalStrength(String strength) {
    if( strength!='real') {
      isSimulationMode = true;
      _simulatedSignalStrength.value = strength;
      latestSignalStrength.value = strength;
    }else{
      isSimulationMode = false;
      _simulatedSignalStrength.value = strength;
    }
  }

  // シミュレートされた信号強度を取得するための関数
  String getSimulatedSignalStrength() {
    //debugPrint("strength : ${_simulatedSignalStrength.value}");
    return _simulatedSignalStrength.value;
  }

   */

  //
  // ====== Akira , GPS信号強度をシミュレート ==== ここまで


  // GPS信号が弱い場合のフラグ. 本番では、false,high にする。
  bool isGpsSignalWeak = false;
  RxString latestSignalStrength = 'high'.obs;
  //final _latestSignalStrength = 'low'.obs; // 初期値を設定
  //String get latestSignalStrength => _latestSignalStrength.value;
  Stream<String> get gpsSignalStrengthStream => latestSignalStrength.stream;

  bool isRunningBackgroundGPS=false;
  int activeEngineCount = 0;

  // GPS信号の強弱を判断するメソッドを追加. 10m 以内：強、30m以内：中、それ以上：弱
  //
  String getGpsSignalStrength(Position? position) {
    if (isSimulationMode.value) {
      return getSimulatedSignalStrength();
    }

    if (position == null) {
      //gpsDebugMode ? debugPrint("getGpsSignalStrength position is null.") : null;
      latestSignalStrength.value = "low";
      isGpsSignalWeak = true;
      return 'low';
    }
    final accuracy = position.accuracy;
    //gpsDebugMode ? debugPrint("getGpsSignalStrength : ${accuracy}") : null;
    /*
    if(isSimulationMode){
      return _simulatedSignalStrength.value;  // GPS信号強度シミュレーション
    }else {
     */
      if (accuracy <= 10) {
        latestSignalStrength.value = "high";
        isGpsSignalWeak = false;
        return 'high';
      } else if (accuracy <= 50) {
        latestSignalStrength.value = "medium";
        isGpsSignalWeak = false;
        return 'medium';
      } else {
        latestSignalStrength.value = "low";
        isGpsSignalWeak = true;
        return 'low';
      }
   // }
  }

  // 現在位置を調整するメソッドを追加
  LatLng? adjustCurrentLocation(Position? position) {
    if (position == null) {   // positionがnullなら、lastValidLocationを使用する。
      if( lastValidLocation!=null ) {
        debugPrint("== 現在位置なし。最後の位置を使用 ==");
        //debugPrint("=== adjustCurrentLocation (Position:Null and using LastValidLocation ${lastValidLocation})===");
        return LatLng(lastValidLocation!.latitude, lastValidLocation!.longitude);
      }else {
        debugPrint("== 現在位置なし。最後の位置も無し ==");
        //print("=== adjustCurrentLocation (Position:Null and No LastValidLocation ... )===");
        return null;
      }
      //return lastValidLocation ?? LatLng(0, 0);
    }
    final signalStrength = getGpsSignalStrength(position);
    if (signalStrength == 'high' || signalStrength == 'medium') {
      debugPrint("== 信号強度 ${signalStrength} ==> 最新位置を使用 ==");
      //debugPrint("=== adjustCurrentLocation (Position:Get and return Valid location:${position} ... )===");
      lastValidLocation = LatLng(position.latitude, position.longitude);
    }else{
      debugPrint("== 信号強度 ${signalStrength} ==> 最後の位置を使用 ==");
    }
    return lastValidLocation ?? LatLng(lastValidLocation!.latitude, lastValidLocation!.longitude);
  }

  //===== Akira Added 2024-4-9 end

  final locationMarkerPositionStreamController =
      StreamController<LocationMarkerPosition?>.broadcast();
				// 位置マーカーの位置情報を送信するためのStreamControllerです。
				// StreamController<LocationMarkerPosition?>型で宣言されています。

  bool isStreamPaused = false;	// 位置情報のストリームが一時停止中かどうかを示すフラグです。bool型で宣言されています。

  // 位置マーカーの位置情報のストリームを取得するゲッター関数です。
  // locationMarkerPositionStreamController.streamを返します。
  //
  Stream<LocationMarkerPosition?> get locationMarkerPositionStream =>
      locationMarkerPositionStreamController.stream;

  // コントローラーの初期化時に呼び出されるライフサイクルメソッドです。
  // startPositionStreamメソッドを呼び出して、位置情報のストリームを開始します。
  //
  @override
  void onInit() {
    super.onInit();
    // Start listening to location updates when the controller is initialized
    _initLocationService();

  }

  Future<void> _initLocationService() async {
    try {
      bool serviceEnabled;
      LocationPermission permission;

      serviceEnabled = await Geolocator.isLocationServiceEnabled();
      if (!serviceEnabled) {
        return Future.error('Location services are disabled.');
      }

      permission = await Geolocator.checkPermission();
      if (permission == LocationPermission.denied) {
        permission = await Geolocator.requestPermission();
        if (permission == LocationPermission.denied) {
          return Future.error('Location permissions are denied');
        }
      }

      if (permission == LocationPermission.deniedForever) {
        return Future.error(
            'Location permissions are permanently denied, we cannot request permissions.');
      }

      startPositionStream();
    } catch( e ){
      print('Error initializing location service: $e');
    }
  }

  // 位置情報のストリームを開始するメソッドです。 
  // 位置情報サービスが有効か確認し、無効な場合はダイアログを表示します。
  // 位置情報の権限を確認し、必要な権限がない場合は権限をリクエストします。
  // 既存の位置情報のストリームをキャンセルします。
  // Geolocator.getPositionStreamを使用して、新しい位置情報のストリームを開始します。
  // ストリームから受信した位置情報をlocationMarkerPositionStreamControllerに追加します。
  // エラーが発生した場合は、locationMarkerPositionStreamControllerにエラーを追加します。
  // ストリームが一時停止中の場合は、ストリームを再開します。
  //
  // 2024-4-8 Akira : See 2809
  // stopPositionStreamメソッドを追加して、既存のストリームをキャンセルするようにしました。また、ストリームが完了したらnullに設定し、エラー発生時にストリームをキャンセルするようにしました。
  //
  void startPositionStream() {
    positionStream = Geolocator.getPositionStream(
      locationSettings: const LocationSettings(
        accuracy: LocationAccuracy.high,
        distanceFilter: 5,  //10,   10mから5mに変更
      ),
    ).listen((Position position) {
      currentPosition.value = position;
      //debugPrint("== startPositionStream: ${position} ==");
      locationMarkerPositionStreamController.add(
        LocationMarkerPosition(
          latitude: position.latitude,
          longitude: position.longitude,
          accuracy: position.accuracy,
        ),
      );
    });
  }

  void startPositionStream_old() async {
    // Check for location service and permissions before starting the stream
    // 位置情報サービスの有効性をチェックし、無効な場合はエラーハンドリングを行います。
    //
    bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      Get.snackbar('位置情報サービスが無効です', '設定から位置情報サービスを有効にしてください');
      return;
    }

    await PermissionController.checkAndRequestPermissions();

    // 位置情報の設定を行います。z11
    // Set up the location options
    const locationOptions =
        LocationSettings(accuracy: LocationAccuracy.medium, distanceFilter: 0);

    // 既存の位置情報のストリームをキャンセルします。
    await positionStream?.cancel();

    // 新しい位置情報のストリームを開始します。
    //
    positionStream = Geolocator.getPositionStream(locationSettings: locationOptions).listen(
      (Position? position) {
        //gpsDebugMode ? debugPrint("Position = ${position}"):null;
        final signalStrength = getGpsSignalStrength(position);
        if (signalStrength == 'low') {
          isGpsSignalWeak = true;
          //gpsDebugMode ? debugPrint("LocationController.getPositionStream : isGpsSignalWeak = ${isGpsSignalWeak}"):null;
        } else {
          isGpsSignalWeak = false;
          //gpsDebugMode ? debugPrint("LocationController.getPositionStream : isGpsSignalWeak = ${isGpsSignalWeak}"):null;
        }

        final DestinationController destinationController = Get.find<DestinationController>();

        // ロゲ開始前、終了後、GPS=low の場合は更新しない。
        //
        if (isGpsSignalWeak == false) {
          //if (destinationController.isInRog.value && isGpsSignalWeak == false) {
            final adjustedLocation = adjustCurrentLocation(position);
            if (adjustedLocation != null) {
              final locationMarkerPosition = LocationMarkerPosition(
                latitude: adjustedLocation.latitude,
                longitude: adjustedLocation.longitude,
                accuracy: position?.accuracy ?? 0,
              );
              handleLocationUpdate(locationMarkerPosition);
              //locationMarkerPositionStreamController.add(locationMarkerPosition); // 位置データ送信
            } else {
              // 位置情報が取得できなかった場合、
              // locationMarkerPositionStreamControllerにnullを追加します。
              locationMarkerPositionStreamController.add(null); // null 送信？
              //forceUpdateLocation(Position? position);

            }
            //}else{
            //  debugPrint("GPS処理対象外");

        }

      },
        onError: (e) {
          // エラーが発生した場合、locationMarkerPositionStreamControllerにエラーを追加します。
          locationMarkerPositionStreamController.addError(e);
        },
        onDone: () {
          positionStream = null; // ストリームが完了したらnullに設定
        },
        cancelOnError: true  // エラー発生時にストリームをキャンセル
    );

    // Resume stream if it was paused previously
    // ストリームが一時停止中の場合、ストリームを再開します。
    //
    if (isStreamPaused) {
      isStreamPaused = false;
      positionStream!.resume();
    }
  }

  // Method to stop the position stream
  // 位置情報のストリームを停止するメソッドです。
  // positionStreamが存在する場合、ストリームを一時停止します。
  // isStreamPausedフラグをtrueに設定します。
  //
  void stopPositionStream() async {
    if (positionStream != null) {
      // updated Akira 2024-4-8
      await positionStream!.cancel();
      positionStream = null;

      //positionStream!.pause();
      //isStreamPaused = true;
    }
  }

  // Method to resume the position stream
  // 位置情報のストリームを再開するメソッドです。
  // positionStreamが存在し、ストリームが一時停止中の場合、ストリームを再開します。
  // isStreamPausedフラグをfalseに設定します。
  //
  void resumePositionStream() {
    if (positionStream != null && isStreamPaused) {
      positionStream!.resume();
      isStreamPaused = false;
    }
  }


  void handleLocationUpdate(LocationMarkerPosition? position) async {
    //debugPrint("locationController.handleLocationUpdate");
    try {
      if (position != null) {
        double currentLat = position.latitude;
        double currentLon = position.longitude;
        //debugPrint("Flutter: Received GPS signal. Latitude: $currentLat, Longitude: $currentLon");

        //debugPrint("position = ${position}");
        /*
      currentPosition.value = position;
      final locationMarkerPosition = LocationMarkerPosition(
        latitude: position.latitude,
        longitude: position.longitude,
        accuracy: position.accuracy,
      );
       */
        lastGPSDataReceivedTime = DateTime.now(); // 最後にGPS信号を受け取った時刻
        locationMarkerPositionStreamController.add(position);
      }else{
        gpsDebugMode ? debugPrint("Flutter: No GPS signal received."):null;
      }
    } catch( e ) {
      debugPrint("Flutter: Error in handleLocationUpdate: $e");
    }
  }

  // このメソッドは、現在の位置情報を locationMarkerPositionStreamController に送信します。
  //
  void forceUpdateLocation(Position? position) {
    if (position != null) {
      final adjustedLocation = adjustCurrentLocation(position);
      if (adjustedLocation != null) {
        final locationMarkerPosition = LocationMarkerPosition(
          latitude: adjustedLocation.latitude,
          longitude: adjustedLocation.longitude,
          accuracy: position.accuracy,
        );
        locationMarkerPositionStreamController.add(locationMarkerPosition);
      }
    }
  }

  // コントローラーのクローズ時に呼び出されるライフサイクルメソッドです。
  // positionStreamをキャンセルします。
  //
  @override
  void onClose() {
    // Cancel the position stream subscription when the controller is closed
    positionStream?.cancel();
    super.onClose();
  }

  // シミュレーションモードのフラグ
  RxBool isSimulationMode = RxBool(false);

  // シミュレーションモードを切り替えるための関数
  void setSimulationMode(bool value) {
    isSimulationMode.value = value;
  }

  // GPS信号強度をシミュレートするための変数
  final Rx<String> _simulatedSignalStrength = Rx<String>('high');

  // GPS信号強度をシミュレートするための関数
  void setSimulatedSignalStrength(String strength) {
    _simulatedSignalStrength.value = strength;
  }

  // シミュレートされた信号強度を取得するための関数
  String getSimulatedSignalStrength() {
    return _simulatedSignalStrength.value;
  }

}
