package net.pokeranalytics.android.ui.fragment.data

import android.os.Bundle
import android.view.View
import com.google.android.libraries.places.api.model.PlaceLikelihood
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.Location
import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.LocationRow
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import net.pokeranalytics.android.util.NULL_TEXT

/**
 * Custom EditableDataFragment to manage the LOCATE_ME case
 */
class LocationDataFragment : EditableDataFragment(), StaticRowRepresentableDataSource {

	// Return the item as a Location object
	private val location: Location
		get() {
			return this.item as Location
		}

	// Loader boolean
	private var isLookingForPlaces: Boolean = false
	private var placesForRows: HashMap<CustomizableRowRepresentable, PlaceLikelihood> = HashMap()
	private var rowPlaces: ArrayList<CustomizableRowRepresentable> = ArrayList()
	private var locationActivated = false

	val rows = ArrayList<RowRepresentable>()

	override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
		super.onViewCreated(view, savedInstanceState)

		shouldOpenKeyboard = false
		locationActivated = parentActivity?.hasLocationPermissionGranted() ?: false

		if (deleteButtonShouldAppear) {

			// If we update a location, we set the switch to the correct value
			locationActivated = location.latitude != null && location.longitude != null

		} else if (locationActivated) {

			// If we create a new location, we try to locate the user by default
			isLookingForPlaces = true
			getSuggestionsPlaces()

		}

		updateAdapterUI()
	}

	override fun getDataSource(): RowRepresentableDataSource {
		return this
	}

	override fun adapterRows(): List<RowRepresentable>? {
		return rows
	}

	override fun stringForRow(row: RowRepresentable): String {
		return when (row) {
			SimpleRow.NAME -> if (location.name.isNotEmpty())location.name else NULL_TEXT
			else -> return super.stringForRow(row)
		}
	}

	override fun boolForRow(row: RowRepresentable): Boolean {
		return when (row) {
			LocationRow.LOCATION_PERMISSION_SWITCH -> return locationActivated
			else -> super.boolForRow(row)
		}
	}

	override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
		return when (row) {
			SimpleRow.NAME -> row.editingDescriptors(mapOf("defaultValue" to this.location.name))
			else -> null
		}
	}

	override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
		// If we click on a location row, save the location (and finish activity)
		placesForRows[row]?.place?.let { place ->
			location.setPlace(place)
			saveData()
			return
		}
		super.onRowSelected(position, row, fromAction)
	}

	override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
		when (row) {
			LocationRow.LOCATION_PERMISSION_SWITCH -> {
				if (value is Boolean && value != locationActivated) {
					rowPlaces.clear()
					locationActivated = value
					isLookingForPlaces = value
					if (value) {
						updateAdapterUI()
						getSuggestionsPlaces()
					} else {
						clearCurrentLocation()
						updateAdapterUI()
					}
				}
			}
			else -> super.onRowValueChanged(value, row)
		}
	}

	/**
	 * Clear current location
	 */
	private fun clearCurrentLocation() {
		location.latitude = null
		location.longitude = null
	}

	/**
	 * Refresh rows
	 */
	private fun refreshRows() {
		rows.clear()
		rows.add(SimpleRow.NAME)
		rows.add(LocationRow.LOCATION_PERMISSION_SWITCH)
		// Add info row to explain why we need the location permission
		rows.add(CustomizableRowRepresentable(customViewType = RowViewType.INFO, resId = R.string.location_when_in_use_usage_description))

		if (isLookingForPlaces) {
			rows.add(LocationRow.LOCATION_LOADER)
		}

		if (locationActivated && rowPlaces.size > 0) {
			rows.add(CustomizableRowRepresentable(resId = R.string.suggestions))
			for (row in rowPlaces) {
				rows.add(row)
			}
		}
	}

	/**
	 * Update UI adapter
	 */
	private fun updateAdapterUI() {

		val currentRowsSize = rows.size
		refreshRows()
		val newRowsSize = rows.size

		if (currentRowsSize < newRowsSize) {
			rowRepresentableAdapter.notifyItemRangeInserted(currentRowsSize, newRowsSize - currentRowsSize)
		} else {
			rowRepresentableAdapter.notifyItemRangeRemoved(newRowsSize, currentRowsSize - newRowsSize)
		}
	}

	/**
	 * Return the places around the user
	 */
	private fun getSuggestionsPlaces() {

		val maxResults = 5

		parentActivity?.askForPlacesRequest { success, places ->

			if (success) {
				// Try to get the location of the user
				parentActivity?.findCurrentLocation {currentLocation ->
					currentLocation?.let {
						location.latitude = currentLocation.latitude
						location.longitude = currentLocation.longitude
					}
				}
			}

			if (success && places.size > 0) {
				rowPlaces.clear()
				placesForRows.clear()

				for ((index, place) in places.withIndex()) {
					if (index < maxResults) {
						val row = CustomizableRowRepresentable(customViewType = RowViewType.LOCATION_TITLE, title = place.place.name, isSelectable = true)
						rowPlaces.add(row)
						placesForRows[row] = place
					}
				}

				locationActivated = true
				isLookingForPlaces = false
				updateAdapterUI()
			} else {
				isLookingForPlaces = false
				locationActivated = false
				updateAdapterUI()
			}
		}
	}

}