import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:async/async.dart';
import 'package:diplomaticquarterapp/config/config.dart';
import 'package:diplomaticquarterapp/core/model/pharmacies/order_detail.dart';
import 'package:diplomaticquarterapp/core/model/pharmacies/order_model.dart';
import 'package:diplomaticquarterapp/core/service/parmacyModule/order-preview-service.dart';
import 'package:diplomaticquarterapp/locator.dart';
import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart';
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_animarker/lat_lng_interpolation.dart';
import 'package:flutter_animarker/models/lat_lng_delta.dart';
import 'package:flutter_animarker/models/lat_lng_info.dart';
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
import 'package:flutter_animarker/streams/lat_lng_stream.dart';

class TrackDriver extends StatefulWidget {
  final OrderDetailModel order;
  TrackDriver({this.order});

  @override
  State<TrackDriver> createState() => _TrackDriverState();
}

class _TrackDriverState extends State<TrackDriver> {
  OrderPreviewService _orderServices = locator<OrderPreviewService>();
  OrderDetailModel _order;

  Completer<GoogleMapController> _controller = Completer();


  double CAMERA_ZOOM = 14;
  double CAMERA_TILT = 0;
  double CAMERA_BEARING = 30;
  LatLng SOURCE_LOCATION = null;
  LatLng DEST_LOCATION = null;

  // for my drawn routes on the map
  Set<Polyline> _polylines = Set<Polyline>();
  List<LatLng> polylineCoordinates = [];
  PolylinePoints polylinePoints;

  Set<Marker> _markers = Set<Marker>();

  BitmapDescriptor sourceIcon; // for my custom marker pins
  BitmapDescriptor destinationIcon; // for my custom marker pins
  Location location;// wrapper around the location API


  int locationUpdateFreq = 2;
  LatLngInterpolationStream _latLngStream;
  StreamGroup<LatLngDelta> subscriptions;

  @override
  void initState() {
    super.initState();

    _order = widget.order;
    DEST_LOCATION = _order.shippingAddress.getLocation();
    location = new Location();
    polylinePoints = PolylinePoints();
    setSourceAndDestinationIcons();

    initMarkerUpdateStream();
    startUpdatingDriverLocation();

  }

  @override
  void dispose() {
    super.dispose();
    subscriptions.close();
    _latLngStream.cancel();
    stopUpdatingDriverLocation();
  }

  initMarkerUpdateStream(){
    _latLngStream = LatLngInterpolationStream(movementDuration: Duration(seconds: locationUpdateFreq+1));
    subscriptions = StreamGroup<LatLngDelta>();

    subscriptions.add(_latLngStream.getAnimatedPosition('sourcePin'));
    subscriptions.stream.listen((LatLngDelta delta) {
      //Update the marker with animation
      setState(() {
        //Get the marker Id for this animation
        var markerId = MarkerId(delta.markerId);
        Marker sourceMarker = Marker(
          markerId: markerId,
          // rotation: delta.rotation,
          icon: sourceIcon,
          position: LatLng(
            delta.from.latitude,
            delta.from.longitude,
          ),
            onTap: onSourceMarkerTap
        );

        _markers.removeWhere((m) => m.markerId.value == 'sourcePin');
        _markers.add(sourceMarker);
      });
    });
  }

  @override
  Widget build(BuildContext context) {

    return AppScaffold(
      appBarTitle: TranslationBase.of(context).deliveryDriverTrack,
      isShowAppBar: true,
      isPharmacy: true,
      showPharmacyCart: false,
      showHomeAppBarIcon: false,
      body: GoogleMap(
        myLocationEnabled: true,
        compassEnabled: true,
        markers:  _markers,
        polylines: _polylines,
        mapType: MapType.normal,
        initialCameraPosition: CameraPosition(target: DEST_LOCATION, zoom: 4),
        onMapCreated: (GoogleMapController controller) {
          _controller.complete(controller);
          // showPinsOnMap();
        },
      ),
      // floatingActionButton: FloatingActionButton.extended(
      //   onPressed: _goToDriver,
      //   label: Text('To the lake!'),
      //   icon: Icon(Icons.directions_boat),
      // ),
    );
  }


  void setSourceAndDestinationIcons() async {
    final Uint8List srcMarkerBytes =  await getBytesFromAsset('assets/images/map_markers/source_map_marker.png', getMarkerIconSize());
    final Uint8List destMarkerBytes =  await getBytesFromAsset('assets/images/map_markers/destination_map_marker.png',  getMarkerIconSize());
    sourceIcon = await BitmapDescriptor.fromBytes(srcMarkerBytes);
    destinationIcon = await BitmapDescriptor.fromBytes(destMarkerBytes);
  }

  CameraPosition _orderDeliveryLocationCamera(){
    if(DEST_LOCATION != null){
      final CameraPosition orderDeliveryLocCamera = CameraPosition(
          bearing: CAMERA_BEARING,
          target: DEST_LOCATION,
          tilt: CAMERA_TILT,
          zoom: CAMERA_ZOOM);
      return orderDeliveryLocCamera;
    }
    return null;
  }

  CameraPosition _driverLocationCamera(){
    if(DEST_LOCATION != null) {
      final CameraPosition driverLocCamera = CameraPosition(
          bearing: CAMERA_BEARING,
          target: SOURCE_LOCATION,
          tilt: CAMERA_TILT,
          zoom: CAMERA_ZOOM);
      return driverLocCamera;
    }
    return null;
  }


  Future<void> _goToOrderDeliveryLocation() async {
    final GoogleMapController controller = await _controller.future;
    final CameraPosition orderDeliveryLocCamera = _orderDeliveryLocationCamera();
    controller.animateCamera(CameraUpdate.newCameraPosition(orderDeliveryLocCamera));
  }

  Future<void> _goToDriver() async {
    final GoogleMapController controller = await _controller.future;
    final CameraPosition driverLocCamera = _driverLocationCamera();
    controller.animateCamera(CameraUpdate.newCameraPosition(driverLocCamera));
  }


  void showPinsOnMap() {
    // source pin
    if(SOURCE_LOCATION != null){
      setState(() {
        var pinPosition = SOURCE_LOCATION;
        _markers.removeWhere((m) => m.markerId.value == 'sourcePin');
        _markers.add(Marker(
            markerId: MarkerId('sourcePin'),
            position: pinPosition,
            icon: sourceIcon,
            infoWindow:  InfoWindow(title: TranslationBase.of(context).driver),
            onTap: onSourceMarkerTap
        ));
      });
    }

    // destination pin
    if(DEST_LOCATION != null){
      setState(() {
        var destPosition = DEST_LOCATION;
        _markers.removeWhere((m) => m.markerId.value == 'destPin');
        _markers.add(Marker(
            markerId: MarkerId('destPin'),
            position: destPosition,
            icon: destinationIcon,
            infoWindow:  InfoWindow(title: TranslationBase.of(context).deliveryLocation),
            onTap: onDestinationMarkerTap
        ));
      });
    }
    // set the route lines on the map from source to destination
    // for more info follow this tutorial
    // drawRoute();
  }

  void updatePinOnMap() async {
    _latLngStream.addLatLng(LatLngInfo(SOURCE_LOCATION.latitude, SOURCE_LOCATION.longitude, "sourcePin"));
    drawRoute();
  }

  void drawRoute() async {
    return; // Ignore draw Route
    
    List<PointLatLng> result = await polylinePoints.getRouteBetweenCoordinates(
        GOOGLE_API_KEY,
        SOURCE_LOCATION.latitude,
        SOURCE_LOCATION.longitude,
        DEST_LOCATION.latitude,
        DEST_LOCATION.longitude);
    if(result.isNotEmpty){
      result.forEach((PointLatLng point){
        polylineCoordinates.add(
            LatLng(point.latitude,point.longitude)
        );
      });
      setState(() {
        _polylines.add(Polyline(
            width: 5, // set the width of the polylines
            polylineId: PolylineId('poly'),
            color: Color.fromARGB(255, 40, 122, 198),
            points: polylineCoordinates
        ));
      });
    }
  }

  bool isLocationUpdating = false;
  startUpdatingDriverLocation({int frequencyInSeconds = 2}) async{
    isLocationUpdating = true;
    int driverId = int.tryParse(_order.driverID);

    Future.doWhile(() async{
      if(isLocationUpdating){

        await Future.delayed(Duration(seconds: frequencyInSeconds));

        showLoading();
        LatLng driverLocation = (await _orderServices.getDriverLocation(driverId));
        hideLoading();

        if(driverLocation != null){
          if(SOURCE_LOCATION == null || DEST_LOCATION == null){
            SOURCE_LOCATION = driverLocation;
            DEST_LOCATION = _order.shippingAddress.getLocation();
            showPinsOnMap();
          }
          SOURCE_LOCATION = driverLocation;
          updatePinOnMap();
          updateMapCamera();
        }else{
          GifLoaderDialogUtils.hideDialog(context);
        }
      }
      return isLocationUpdating;

    });
  }

  showLoading(){
    if(SOURCE_LOCATION == null){
      GifLoaderDialogUtils.showMyDialog(context);
    }
  }

  hideLoading(){
    if(SOURCE_LOCATION == null){
      GifLoaderDialogUtils.hideDialog(context);
    }
  }

  stopUpdatingDriverLocation(){
    isLocationUpdating = false;
  }

  Future<Uint8List> getBytesFromAsset(String path, int width) async {
    ByteData data = await rootBundle.load(path);
    ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), targetWidth: width);
    ui.FrameInfo fi = await codec.getNextFrame();
    return (await fi.image.toByteData(format: ui.ImageByteFormat.png)).buffer.asUint8List();
  }

  int getMarkerIconSize(){
    return 140;
  }

  updateMapCamera() async{
    if(SOURCE_LOCATION != null && DEST_LOCATION != null){

      // 'package:google_maps_flutter_platform_interface/src/types/location.dart': Failed assertion: line 72 pos 16: 'southwest.latitude <= northeast.latitude': is not true.
      LatLngBounds bound;
      if(SOURCE_LOCATION.latitude <= DEST_LOCATION.latitude){
        bound = LatLngBounds(southwest: SOURCE_LOCATION, northeast: DEST_LOCATION);
      }else{
        bound = LatLngBounds(southwest: DEST_LOCATION, northeast: SOURCE_LOCATION);
      }

      if(bound == null)
        return;

      CameraUpdate camera = CameraUpdate.newLatLngBounds(bound, 50);
      final GoogleMapController controller = await _controller.future;
      controller.animateCamera(camera);
    }
  }

  bool showSrcMarkerTitle = false;
  onSourceMarkerTap() async{
    // showSrcMarkerTitle = !showSrcMarkerTitle;
  }

  bool showDestMarkerTitle = false;
  onDestinationMarkerTap() async{
    // showDestMarkerTitle = !showDestMarkerTitle;
    // Marker m = _markers.firstWhere((m) => m.markerId.value == 'destPin');
    // if(showDestMarkerTitle){
    // }
  }
}
