package it.danieleverducci.openweddingapp.ui.places

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import com.android.volley.VolleyError
import it.danieleverducci.openweddingapp.BuildConfig
import it.danieleverducci.openweddingapp.Config
import it.danieleverducci.openweddingapp.R
import it.danieleverducci.openweddingapp.databinding.FragmentPlacesBinding
import it.danieleverducci.openweddingapp.entities.MapPlace
import it.danieleverducci.openweddingapp.networking.PlacesNet
import it.danieleverducci.openweddingapp.utils.SharedPreferencesManager
import org.json.JSONObject
import org.osmdroid.config.Configuration
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.overlay.Marker
import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider
import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay
import java.util.*


class PlacesFragment : Fragment() {
    companion object {
        private val TAG = "PlacesFragment"
        private val PERMISSION_REQUEST_CODE = 4262
    }
    private lateinit var binding: FragmentPlacesBinding

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentPlacesBinding.inflate(inflater, container, false)

        // If first time
        if (!SharedPreferencesManager.hasPlacesHintBeenDisplayed(requireContext())) {
            // Show hint
            val hintView = binding.placesHint
            hintView.visibility = View.VISIBLE
            // Allow dismissing by click
            hintView.setOnClickListener { v: View? -> v?.visibility = View.GONE }
            // Also dismiss after 3 secs just to be sure
            Handler(Looper.getMainLooper()).postDelayed({
                hintView.visibility = View.GONE
            }, 3000)
        }

        // Setup map
        val osmdroidConfig = Configuration.getInstance()
        osmdroidConfig.load(
            requireContext(),
            PreferenceManager.getDefaultSharedPreferences(requireContext())
        )
        osmdroidConfig.userAgentValue = BuildConfig.APPLICATION_ID
        binding.map.setMultiTouchControls(true)
        binding.map.setTilesScaledToDpi(true)
        binding.map.maxZoomLevel = 22.0

        loadPlaces()

        showUserPosition()

        return binding.getRoot()
    }

    fun showUserPosition() {
        // Check if user granted location permission
        if (context != null && ActivityCompat.checkSelfPermission(
                requireContext(),
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
                requireContext(),
                Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            // User didn't grant permission. Ask it.
            requestPermissions(
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                PERMISSION_REQUEST_CODE
            )
            return
        }

        val mLocationOverlay = MyLocationNewOverlay(GpsMyLocationProvider(context), binding.map)
        mLocationOverlay.enableMyLocation()
        binding.map.getOverlays().add(mLocationOverlay)
    }

    /**
     * Loads Places from shared preferences, if any.
     * Then, tries to update location from static online service.
     */
    private fun loadPlaces() {
        val plJo = SharedPreferencesManager.loadPlaces(requireContext())
        clearAndShowMarkers(plJo)

        // Update data
        val pn = PlacesNet(requireContext())
        pn.getItem(object: PlacesNet.OnItemObtainedListener {
            override fun OnItemObtained(item: JSONObject) {
                if (context == null)
                    return
                // Update map
                if (item.getInt("version") > plJo.getInt("version")) {
                    // Save cache
                    SharedPreferencesManager.savePlaces(requireContext(), item)
                    clearAndShowMarkers(item)
                }
            }

            override fun OnError(error: VolleyError) {
                Log.e(TAG, "Unable to update places list: $error")
            }

        })
    }

    fun clearAndShowMarkers(plJo: JSONObject) {
        // Clear map
        clearMap()

        // Center map
        centerMap(plJo.getDouble("mapCenterLat"), plJo.getDouble("mapCenterLon"), plJo.getDouble("mapZoom"))

        // Show pins
        var locale = Locale.getDefault().language
        if (!plJo.getJSONObject("localized").has(locale))
            locale = Config.STATIC_CONTENT_DEFAULT_LOCALE

        val localizedMapPositions = plJo.getJSONObject("localized").getJSONArray(locale)
        for (i in 0..localizedMapPositions.length() - 1) {
            addMapPlace(MapPlace(localizedMapPositions.getJSONObject(i)))
        }
    }

    fun centerMap(lat: Double, lon: Double, zoom: Double) {
        // Center map
        val position = GeoPoint(lat, lon)
        val mapController = binding.map.controller
        mapController.setZoom(zoom)
        mapController.setCenter(position)
    }

    fun clearMap() {
        // Removes all the markers
        binding.map.overlays.clear()
    }

    fun addMapPlace(item: MapPlace) {
        // Set pin
        val mapMarker = Marker(binding.map)
        mapMarker.setPosition(GeoPoint(item.lat, item.lon))
        val drawable = AppCompatResources.getDrawable(
            requireContext(),
            getIcon(item)
        )
        if (android.os.Build.VERSION.SDK_INT >= 21)
            drawable!!.setTint(ContextCompat.getColor(requireContext(), R.color.colorAccent))
        mapMarker.setIcon(drawable)
        mapMarker.title = item.name
        mapMarker.snippet = item.time
        mapMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
        mapMarker.setOnMarkerClickListener { marker, mapView ->
            // Scroll to position and show details
            mapView.controller.animateTo(marker.position)
            showPlaceDetails(item)
            true
        }
        binding.map.overlays.add(mapMarker)
    }

    private fun showPlaceDetails(item: MapPlace) {
        binding.placesInfoPanel.visibility = View.VISIBLE
        binding.placesInfoIcon.setImageResource(getIcon(item))
        binding.placesInfoTime.text = item.time
        binding.placesInfoTitle.text = item.name
        binding.placesInfoDescr.text = item.descr
    }

    private fun getIcon(item: MapPlace): Int {
        return when (item.type) {
            "ceremony" -> R.drawable.ic_map_ceremony
            "appetizer" -> R.drawable.ic_map_appetizer
            "lunch" -> R.drawable.ic_map_lunch
            "mate" -> R.drawable.ic_map_mate
            "liquor" -> R.drawable.ic_map_liquor
            "cigars" -> R.drawable.ic_map_cigars
            "parking" -> R.drawable.ic_map_parking
            "ecology" -> R.drawable.ic_map_ecology
            "photo" -> R.drawable.ic_map_photo
            "book" -> R.drawable.ic_map_book
            "games" -> R.drawable.ic_map_games
            "etnic" -> R.drawable.ic_map_etnic
            else -> R.drawable.ic_map_pin
        }
    }

    override fun onResume() {
        super.onResume()
        binding.map.onResume()
    }

    override fun onPause() {
        binding.map.onPause()
        super.onPause()
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == PERMISSION_REQUEST_CODE && permissions[0] == Manifest.permission.ACCESS_FINE_LOCATION) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                showUserPosition()
            }
        }
    }
}