package com.waytous.anticollision.ui

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.asynclayoutinflater.view.AsyncLayoutInflater
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.blankj.utilcode.util.LogUtils
import com.mapbox.geojson.Point
import com.mapbox.maps.CameraOptions
import com.mapbox.maps.MapboxMap
import com.mapbox.maps.Style
import com.mapbox.maps.ViewAnnotationAnchor
import com.mapbox.maps.extension.style.layers.properties.generated.IconAnchor
import com.mapbox.maps.extension.style.style
import com.mapbox.maps.plugin.annotation.AnnotationPlugin
import com.mapbox.maps.plugin.annotation.annotations
import com.mapbox.maps.plugin.annotation.generated.PointAnnotation
import com.mapbox.maps.plugin.annotation.generated.PointAnnotationManager
import com.mapbox.maps.plugin.annotation.generated.PointAnnotationOptions
import com.mapbox.maps.plugin.annotation.generated.createPointAnnotationManager
import com.mapbox.maps.plugin.attribution.attribution
import com.mapbox.maps.plugin.compass.compass
import com.mapbox.maps.plugin.logo.logo
import com.mapbox.maps.viewannotation.ViewAnnotationManager
import com.mapbox.maps.viewannotation.viewAnnotationOptions
import com.waytous.anticollision.R
import com.waytous.anticollision.databinding.CurrentVehicleViewAnnotationLayoutBinding
import com.waytous.anticollision.databinding.LayoutMapBoxBinding
import com.waytous.anticollision.databinding.OtherVehicleViewAnnotationLayoutBinding
import com.waytous.anticollision.repository.MainRepository
import com.waytous.anticollision.utils.BitmapUtils
import com.waytous.anticollision.utils.addBarricadeLineLayer
import com.waytous.anticollision.utils.addBarricadeSource
import com.waytous.anticollision.utils.addDiggingWorkAreaFillLayer
import com.waytous.anticollision.utils.addDiggingWorkAreaSource
import com.waytous.anticollision.utils.addDiggingWorkAreaSymbolLayer
import com.waytous.anticollision.utils.addDumpAreaFillLayer
import com.waytous.anticollision.utils.addDumpAreaSource
import com.waytous.anticollision.utils.addDumpAreaSymbolLayer
import com.waytous.anticollision.utils.addElectronicFenceFillLayer
import com.waytous.anticollision.utils.addElectronicFenceSource
import com.waytous.anticollision.utils.addElectronicFenceSymbolLayer
import com.waytous.anticollision.utils.addLaneLayer
import com.waytous.anticollision.utils.addLaneNodeCircleLayer
import com.waytous.anticollision.utils.addLaneNodeSource
import com.waytous.anticollision.utils.addLaneNodeSymbolLayer
import com.waytous.anticollision.utils.addLaneSource
import com.waytous.anticollision.utils.addObstaclesFillLayer
import com.waytous.anticollision.utils.addObstaclesSource
import com.waytous.anticollision.utils.addObstaclesSymbolLayer
import com.waytous.anticollision.utils.addParkSpotFillLayer
import com.waytous.anticollision.utils.addParkSpotSource
import com.waytous.anticollision.utils.addParkSpotSymbolLayer
import com.waytous.anticollision.utils.addRunnableAreaLayer
import com.waytous.anticollision.utils.addRunnableAreaSource
import com.waytous.anticollision.utils.addStaticObjectsFillLayer
import com.waytous.anticollision.utils.addStaticObjectsSource
import com.waytous.anticollision.utils.addStaticObjectsSymbolLayer
import com.waytous.anticollision.utils.addStationAreaFillLayer
import com.waytous.anticollision.utils.addStationAreaSource
import com.waytous.anticollision.utils.addStationAreaSymbolLayer
import com.waytous.anticollision.utils.addTruckPathLineLayer
import com.waytous.anticollision.utils.addTruckPathSource
import com.waytous.anticollision.utils.addWetAreaFillLayer
import com.waytous.anticollision.utils.addWetAreaSource
import com.waytous.anticollision.utils.addWetAreaSymbolLayer
import com.waytous.anticollision.vo.VehicleAnnotation
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.flow.last
import kotlinx.coroutines.flow.single
import kotlinx.coroutines.launch
import java.math.BigDecimal

open class BaseFragment : Fragment() {

    private val mapModel: MainViewModel by viewModels {
        MainViewModel.MainViewModelFactory(MainRepository(requireContext()))
    }

    val iconCurrentNormalVehicleBitmap by lazy {
        BitmapUtils.bitmapFromDrawableRes(requireContext(), R.drawable.my_vehicle_normal)
    }

    private val iconOtherAlarmVehicleBitmap by lazy {
        BitmapUtils.bitmapFromDrawableRes(requireContext(), R.drawable.other_vehicle_normal)
    }

    private val asyncInflater by lazy {
        AsyncLayoutInflater(requireContext())
    }

    lateinit var layoutMapBoxBinding: LayoutMapBoxBinding

    lateinit var mapboxMap: MapboxMap

    private lateinit var viewAnnotation: View

    private lateinit var annotationPlugin: AnnotationPlugin

    private lateinit var viewAnnotationManager: ViewAnnotationManager

    private lateinit var pointAnnotationManager: PointAnnotationManager

    private lateinit var pointAnnotation: PointAnnotation

    private val cameraOptions: CameraOptions by lazy {
        CameraOptions.Builder().center(
            Point.fromLngLat(
                MainViewModel.LONGITUDE,
                MainViewModel.LATITUDE
            )
        ).zoom(14.0).build()
    }

    companion object {
        private val scaleLengths = listOf(
            5,
            10,
            20,
            50,
            100,
            200,
            500,
            1000,
            2000,
            5000,
            10000,
            20000,
            50000,
            100000,
            200000,
            500000,
            1000000,
            2000000
        )
        const val MINIMUM_ZOOM_LEVEL = 11.0
        const val MAXIMUM_ZOOM_LEVEL = 26.0
        const val DEFAULT_ZOOM_LEVEL = 14.0
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        layoutMapBoxBinding = LayoutMapBoxBinding.inflate(LayoutInflater.from(requireContext()))
    }

    fun setupMapBox() {
        layoutMapBoxBinding.mapView.run {
            logo.enabled = false
            compass.enabled = false
            attribution.enabled = false
        }
        mapboxMap = layoutMapBoxBinding.mapView.getMapboxMap().also {
            it.setCamera(cameraOptions)
        }
        layoutMapBoxBinding.zoomIn.setOnClickListener {
            val zoomLevel = BigDecimal(mapboxMap.cameraState.zoom).minus(BigDecimal(0.1.toString()))
            when (BigDecimal(MINIMUM_ZOOM_LEVEL.toString()).compareTo(zoomLevel)) {
                -1 -> {
                    mapboxMap.setCamera(CameraOptions.Builder().zoom(zoomLevel.toDouble()).build())
                }

                else -> {
                    mapboxMap.setCamera(CameraOptions.Builder().zoom(DEFAULT_ZOOM_LEVEL).build())
                }
            }
        }
        layoutMapBoxBinding.zoomOut.setOnClickListener {
            val zoomLevel = BigDecimal(mapboxMap.cameraState.zoom).add(BigDecimal(0.1.toString()))
            when (BigDecimal(MAXIMUM_ZOOM_LEVEL.toString()).compareTo(zoomLevel)) {
                -1 -> {
                    mapboxMap.setCamera(CameraOptions.Builder().zoom(DEFAULT_ZOOM_LEVEL).build())
                }

                else -> {
                    mapboxMap.setCamera(CameraOptions.Builder().zoom(zoomLevel.toDouble()).build())
                }
            }
        }

        mapboxMap.loadStyle(style(MainViewModel.MY_MAP_BOX_STYLE) {
        }) {
            initAnnotationManager()
            setupStyle(it)
            addTempAnnotations()
        }
    }

    private fun setupStyle(style: Style) {
        lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                mapModel.runnableAreaSource().last()?.let { source ->
                    style.addRunnableAreaSource(source)
                    style.addRunnableAreaLayer()
                }
                mapModel.laneSource().last()?.let { source ->
                    style.addLaneSource(source)
                    style.addLaneLayer()
                }
                mapModel.laneNodeSource().last()?.let { source ->
                    style.addLaneNodeSource(source)
                    style.addLaneNodeCircleLayer()
                    style.addLaneNodeSymbolLayer()
                }
                mapModel.diggingWorkAreaSource().last()?.let { source ->
                    style.addDiggingWorkAreaSource(source)
                    style.addDiggingWorkAreaFillLayer()
                    style.addDiggingWorkAreaSymbolLayer()
                }
                mapModel.obstaclesSource().last()?.let { source ->
                    style.addObstaclesSource(source)
                    style.addObstaclesFillLayer()
                    style.addObstaclesSymbolLayer()
                }
                mapModel.electronicFenceSource().last()?.let { source ->
                    style.addElectronicFenceSource(source)
                    style.addElectronicFenceFillLayer()
                    style.addElectronicFenceSymbolLayer()
                }
                mapModel.wetAreaSource().last()?.let { source ->
                    style.addWetAreaSource(source)
                    style.addWetAreaFillLayer()
                    style.addWetAreaSymbolLayer()
                }
                mapModel.dumpAreaSource().last()?.let { source ->
                    style.addDumpAreaSource(source)
                    style.addDumpAreaFillLayer()
                    style.addDumpAreaSymbolLayer()
                }
                mapModel.barricadeSource().last()?.let { source ->
                    style.addBarricadeSource(source)
                    style.addBarricadeLineLayer()
                }
                mapModel.stationAreaSource().last()?.let { source ->
                    style.addStationAreaSource(source)
                    style.addStationAreaFillLayer()
                    style.addStationAreaSymbolLayer()
                }
                mapModel.staticObjectsSource().last()?.let { source ->
                    style.addStaticObjectsSource(source)
                    style.addStaticObjectsFillLayer()
                    style.addStaticObjectsSymbolLayer()
                }
                mapModel.parkSpotSource().last()?.let { source ->
                    style.addParkSpotSource(source)
                    style.addParkSpotFillLayer()
                    style.addParkSpotSymbolLayer()
                }
                style.addTruckPathSource()
                style.addTruckPathLineLayer()
            }
        }
    }

    fun addTempAnnotations() {
        val otherAnnotation = VehicleAnnotation(
            "KK-01", 36, 25,
            Point.fromLngLat(MainViewModel.LONGITUDE, MainViewModel.LATITUDE), true
        )
        updateCurrentVehicleAnnotation(otherAnnotation)
        val otherAnnotation1 =
            VehicleAnnotation("KK-02", 24, 25, Point.fromLngLat(119.76739, 49.37722), true)
        val otherAnnotation2 =
            VehicleAnnotation("KK-03", 36, 20, Point.fromLngLat(119.75694, 49.38632), true)
        val others = listOf(otherAnnotation1, otherAnnotation2)
        addOtherVehicleAnnotations(others)
    }

    private fun initAnnotationManager() {
        annotationPlugin = layoutMapBoxBinding.mapView.annotations
        viewAnnotationManager = layoutMapBoxBinding.mapView.viewAnnotationManager
        pointAnnotationManager = annotationPlugin.createPointAnnotationManager()
    }

    fun updateCurrentVehicleAnnotation(otherAnnotation: VehicleAnnotation) {
        val pointAnnotationOptions: PointAnnotationOptions = PointAnnotationOptions()
            .withPoint(otherAnnotation.point)
            .withIconImage(iconCurrentNormalVehicleBitmap!!)
            .withIconSize(0.25)
            .withIconAnchor(IconAnchor.BOTTOM)
            .withDraggable(false)
        val pointAnnotation = pointAnnotationManager.create(pointAnnotationOptions)
        pointAnnotation.symbolSortKey = Double.MAX_VALUE
        val viewAnnotation = viewAnnotationManager.addViewAnnotation(
            resId = R.layout.current_vehicle_view_annotation_layout,
            options = viewAnnotationOptions {
                geometry(otherAnnotation.point)
                associatedFeatureId(pointAnnotation.featureIdentifier)
                anchor(ViewAnnotationAnchor.BOTTOM)
                offsetY((pointAnnotation.iconImageBitmap?.height!! * 0.25).toInt())
                allowOverlap(true)
            }
        )
        val binding = CurrentVehicleViewAnnotationLayoutBinding.bind(viewAnnotation)
        binding.annotationCurrentVehicleNo.text =
            context?.getString(R.string.annotation_vehicle_no, "KK-001")
        binding.annotationCurrentVehicleSpeed.text =
            context?.getString(R.string.annotation_vehicle_speed, 36)
    }

    fun addOtherVehicleAnnotations(others: List<VehicleAnnotation>) {
        others.forEach { vehicle ->
            val pointAnnotationOptions: PointAnnotationOptions = PointAnnotationOptions()
                .withPoint(vehicle.point)
                .withIconImage(iconOtherAlarmVehicleBitmap!!)
                .withIconSize(0.25)
                .withIconAnchor(IconAnchor.BOTTOM)
                .withDraggable(false)
            val pointAnnotation = pointAnnotationManager.create(pointAnnotationOptions)
            pointAnnotation.symbolSortKey = Double.MAX_VALUE
            val viewAnnotation = viewAnnotationManager.addViewAnnotation(
                resId = R.layout.other_vehicle_view_annotation_layout,
                options = viewAnnotationOptions {
                    geometry(vehicle.point)
//                associatedFeatureId(pointAnnotation.featureIdentifier)
                    anchor(ViewAnnotationAnchor.BOTTOM)
                    offsetY((pointAnnotation.iconImageBitmap?.height!! * 0.25).toInt())
                    allowOverlap(true)
                }
            )
            val binding = OtherVehicleViewAnnotationLayoutBinding.bind(viewAnnotation)
            binding.annotationOtherVehicleNo.text =
                context?.getString(R.string.annotation_vehicle_no, vehicle.vehicleNo)
            binding.annotationOtherVehicleSpeed.text = vehicle.speed.toString()
            binding.annotationOtherVehicleDistance.text = vehicle.distance.toString()
        }
    }
}