package com.aaron.kakao.hospitalmapfortube.ui.main

import android.Manifest
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.support.v4.app.ActivityCompat
import android.support.v7.app.AlertDialog
import android.widget.ArrayAdapter
import android.widget.Toast
import com.aaron.kakao.hospitalmapfortube.R
import com.aaron.kakao.hospitalmapfortube.data.remote.model.CATEGORY_HOSPITAL
import com.aaron.kakao.hospitalmapfortube.data.remote.model.CATEGORY_OIL
import com.aaron.kakao.hospitalmapfortube.data.remote.model.CATEGORY_PHARMACY
import com.aaron.kakao.hospitalmapfortube.data.remote.model.Document
import com.aaron.kakao.hospitalmapfortube.databinding.ActivityMainBinding
import com.aaron.kakao.hospitalmapfortube.ui.BindingActivity
import com.aaron.kakao.hospitalmapfortube.util.DividerItemDecoration
import com.aaron.kakao.hospitalmapfortube.util.LocationUtil
import com.tedpark.tedpermission.rx2.TedRx2Permission
import net.daum.mf.map.api.MapPOIItem
import net.daum.mf.map.api.MapPoint
import net.daum.mf.map.api.MapView
import org.koin.android.viewmodel.ext.android.getViewModel

private const val NAVI_TYPE_CAR = "CAR"
private const val NAVI_TYPE_PUBLICTRANSIT = "PUBLICTRANSIT"
private const val NAVI_TYPE_FOOT = "FOOT"

class MainActivity : BindingActivity<ActivityMainBinding, MainViewModel>() {

    private lateinit var mapView: MapView

    // 런타임 퍼미션 체크 필요 여부
    private val needPermission: Boolean
        get() {
            return ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
                    && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
        }

    override fun getLayoutResId() = R.layout.activity_main

    override fun getMyViewModel(): MainViewModel = getViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding.vm = viewModel
        initMap()
        initView()
        initListener()
    }

    /**
     * 뷰 초기화
     */
    private fun initView() {
        binding.locationRecyclerView.addItemDecoration(
            DividerItemDecoration(
                this,
                R.drawable.line_divider
            )
        )
        binding.locationRecyclerView.adapter = LocationAdapter()
    }

    /**
     * 콜백 프로퍼티 초기화
     */
    private fun initListener() {
        /**
         * 병원, 약국, 주유 카테고리 버튼 클릭
         */
        viewModel.categoryClickEvent = {
            viewModel.getCategories(mapView.mapCenterPoint)
        }

        /**
         * 카테고리 검색 API 요청 결과
         */
        viewModel.getCategories = { docList, isMore ->
            viewModel.isRefreshEnabled.set(false)
            viewModel.isShowMore.set(docList.isNotEmpty())

            if (docList.isNotEmpty()) {
                viewModel.isShowLocationList.set(true)

                if (isMore) {
                    viewModel.isMore = false
                } else {
                    mapView.removeAllPOIItems()
                }
                (binding.locationRecyclerView.adapter as LocationAdapter).setData(docList, isMore)
                addPOIItems(docList)
            } else {
                viewModel.run {
                    removePOIItems()
                    if (binding.locationRecyclerView.adapter.itemCount < 1 || isAllCategoryDisabled) {
                        isShowLocationList.set(false)
                    } else {
                        isShowLocationList.set(true)
                    }
                }
            }
        }

        /**
         * 새로고침 클릭
         */
        viewModel.refreshClickEvent = {
            viewModel.getCategories(mapView.mapCenterPoint)
        }

        /**
         * 위치 리스트 아이템 클릭
         */
        (binding.locationRecyclerView.adapter as LocationAdapter).itemClickEvent = {
            val selectedPOIItem = mapView.findPOIItemByTag(it)
            mapView.selectPOIItem(selectedPOIItem, true)
            mapView.setMapCenterPoint(selectedPOIItem.mapPoint, true)
        }
    }

    /**
     * 활성화되지 않은 카테고리 POI 아이템 삭제
     */
    private fun removePOIItems() {
        if (viewModel.isAllCategoryDisabled) {
            mapView.removeAllPOIItems()
        } else {
            val removeItems = arrayListOf<MapPOIItem>()
            var removeCategory = ""
            mapView.poiItems.forEach {
                when (it.markerType) {
                    MapPOIItem.MarkerType.BluePin -> {
                        if (!viewModel.isHospitalEnabled.get()) {
                            removeItems.add(it)
                            removeCategory = CATEGORY_HOSPITAL
                        }
                    }
                    MapPOIItem.MarkerType.YellowPin -> {
                        if (!viewModel.isPharmacyEnabled.get()) {
                            removeItems.add(it)
                            removeCategory = CATEGORY_PHARMACY
                        }
                    }
                    MapPOIItem.MarkerType.RedPin -> {
                        if (!viewModel.isOilEnabled.get()) {
                            removeItems.add(it)
                            removeCategory = CATEGORY_OIL
                        }
                    }
                    else -> {}
                }
            }
            val resultList = mutableListOf<Document>().apply { addAll((binding.locationRecyclerView.adapter as LocationAdapter).locationList) }
            (binding.locationRecyclerView.adapter as LocationAdapter).locationList.forEach {
                if (it.category_group_code == removeCategory) {
                    resultList.remove(it)
                }
            }
            (binding.locationRecyclerView.adapter as LocationAdapter).setData(resultList, false)
            mapView.removePOIItems(removeItems.toTypedArray())
            mapView.fitMapViewAreaToShowAllPOIItems()
        }
    }

    /**
     * 활성화된 카테고리 POI 아이템 추가
     */
    private fun addPOIItems(docList: List<Document>) {
        val poiItems = arrayOfNulls<MapPOIItem>(docList.size)

        docList.forEachIndexed { index, document ->
            val poiItem = MapPOIItem().apply {
                itemName = document.place_name
                mapPoint =
                    MapPoint.mapPointWithGeoCoord(document.y.toDouble(), document.x.toDouble())
                tag = document.id.toInt()
                markerType = when (document.category_group_code) {
                    CATEGORY_HOSPITAL -> MapPOIItem.MarkerType.BluePin
                    CATEGORY_PHARMACY -> MapPOIItem.MarkerType.YellowPin
                    else -> MapPOIItem.MarkerType.RedPin
                }
                selectedMarkerType = MapPOIItem.MarkerType.RedPin
            }
            poiItems[index] = poiItem
        }
        mapView.addPOIItems(poiItems)
        mapView.fitMapViewAreaToShowAllPOIItems()
    }

    /**
     * 맵 초기화
     */
    private fun initMap() {
        binding.mapLayout.removeAllViews()
        mapView = MapView(this).apply {
            setMapViewEventListener(mapEventListener)
            setPOIItemEventListener(poiEventListener)
        }
        binding.mapLayout.addView(mapView)
    }

    /**
     * 현재 위치 트래킹 On
     */
    private fun showCurrentLocationMarker() {
        try {
            if (mapView.currentLocationTrackingMode == MapView.CurrentLocationTrackingMode.TrackingModeOff) {
                mapView.currentLocationTrackingMode =
                    MapView.CurrentLocationTrackingMode.TrackingModeOnWithMarkerHeadingWithoutMapMoving
                mapView.setCustomCurrentLocationMarkerTrackingImage(
                    R.drawable.red_circle,
                    MapPOIItem.ImageOffset(16, 16)
                )
            }
        } catch (ex: NullPointerException) {
            initMap()
        }
    }

    /**
     * 맵 이벤트 리스너
     */
    private val mapEventListener = object : MapView.MapViewEventListener {
        override fun onMapViewDoubleTapped(p0: MapView?, p1: MapPoint?) {
        }

        override fun onMapViewInitialized(mv: MapView?) {
            mapView.moveCurrentLocation(this@MainActivity)
        }

        override fun onMapViewDragStarted(p0: MapView?, p1: MapPoint?) {
        }

        override fun onMapViewMoveFinished(p0: MapView?, p1: MapPoint?) {
        }

        override fun onMapViewCenterPointMoved(mapView: MapView?, movedPoint: MapPoint?) {
            if (!viewModel.isAllCategoryDisabled) {
                viewModel.isRefreshEnabled.set(true)
            } else {
                viewModel.initCategoryInfo()
            }
        }

        override fun onMapViewDragEnded(p0: MapView?, p1: MapPoint?) {
        }

        override fun onMapViewSingleTapped(p0: MapView?, p1: MapPoint?) {
        }

        override fun onMapViewZoomLevelChanged(p0: MapView?, p1: Int) {
        }

        override fun onMapViewLongPressed(mv: MapView?, p1: MapPoint?) {
            mapView.moveCurrentLocation(this@MainActivity)
            if (!needPermission) {
                Toast.makeText(this@MainActivity, R.string.message_move_current, Toast.LENGTH_SHORT)
                    .show()
            }
        }

    }

    /**
     * POI 아이템 리스너
     */
    private val poiEventListener = object : MapView.POIItemEventListener {
        override fun onCalloutBalloonOfPOIItemTouched(p0: MapView?, p1: MapPOIItem?) {
        }

        override fun onCalloutBalloonOfPOIItemTouched(
            p0: MapView?,
            mapPOIItem: MapPOIItem?,
            p2: MapPOIItem.CalloutBalloonButtonType?
        ) {
            if (!needPermission) {
                showNaviDialog(mapPOIItem!!.mapPoint)
            } else {
                Toast.makeText(
                    this@MainActivity,
                    getString(R.string.message_navi_permission),
                    Toast.LENGTH_SHORT
                ).show()
            }
        }

        override fun onDraggablePOIItemMoved(p0: MapView?, p1: MapPOIItem?, p2: MapPoint?) {
        }

        override fun onPOIItemSelected(mv: MapView?, mapPoiItem: MapPOIItem?) {
            mv?.setMapCenterPoint(mapPoiItem?.mapPoint, true)
        }

    }

    /**
     * 길찾기 다이얼로그 팝업
     */
    private fun showNaviDialog(mapPoint: MapPoint) {
        val arrayAdapter =
            ArrayAdapter<String>(this@MainActivity, android.R.layout.select_dialog_item)
                .apply {
                    add(getString(R.string.navi_car))
                    add(getString(R.string.navi_publictransit))
                    add(getString(R.string.navi_foot))
                }
        AlertDialog.Builder(this@MainActivity).run {
            setTitle(getString(R.string.navi_title))
            setIcon(R.drawable.tube_icon)
            setAdapter(arrayAdapter) { dialog, id ->
                val naviType = when (id) {
                    0 -> NAVI_TYPE_CAR
                    1 -> NAVI_TYPE_PUBLICTRANSIT
                    else -> NAVI_TYPE_FOOT
                }

                var url = LocationUtil.getCurrentLocation(this@MainActivity).let {
                    String.format(
                        getString(R.string.url_daummap),
                        it!!.latitude,
                        it.longitude,
                        mapPoint.mapPointGeoCoord.latitude,
                        mapPoint.mapPointGeoCoord.longitude,
                        naviType
                    )
                }

                try {
                    startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
                } catch (ex: ActivityNotFoundException) {
                    Toast.makeText(this@MainActivity, getString(R.string.message_need_daummap), Toast.LENGTH_SHORT).show()
                    url = getString(R.string.market_url_daummap)
                    startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
                }
                dialog.dismiss()
            }
        }.show()
    }

    /**
     * 현재 위치로 이동
     */
    private fun MapView.moveCurrentLocation(context: Context) {
        if (needPermission) {
            viewModel.addToDisposable(
                TedRx2Permission.with(context)
                    .setRationaleTitle(R.string.title_permission_request)
                    .setRationaleMessage(R.string.message_permission_request)
                    .setPermissions(Manifest.permission.ACCESS_FINE_LOCATION)
                    .request()
                    .subscribe({ permissionResult ->
                        if (permissionResult.isGranted) {
                            initMap()
                        } else {
                            Toast.makeText(
                                context,
                                R.string.message_permission_denied,
                                Toast.LENGTH_SHORT
                            ).show()
                        }
                    }, {})
            )
        } else {
            LocationUtil.getCurrentLocation(context)?.let {
                setMapCenterPoint(
                    MapPoint.mapPointWithGeoCoord(
                        LocationUtil.getCurrentLocation(context)!!.latitude,
                        LocationUtil.getCurrentLocation(context)!!.longitude
                    ), true
                )
            }
            showCurrentLocationMarker()
        }
    }
}