ChatRoomsFragment.kt

package chat.rocket.android.chatrooms.ui

import android.os.Bundle
import android.os.Handler
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.domain.model.DeepLinkInfo
import chat.rocket.android.chatrooms.adapter.RoomsAdapter
import chat.rocket.android.chatrooms.adapter.model.RoomUiModel
import chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import chat.rocket.android.chatrooms.presentation.ChatRoomsView
import chat.rocket.android.chatrooms.viewmodel.ChatRoomsViewModel
import chat.rocket.android.chatrooms.viewmodel.ChatRoomsViewModelFactory
import chat.rocket.android.chatrooms.viewmodel.LoadingState
import chat.rocket.android.chatrooms.viewmodel.Query
import chat.rocket.android.servers.ui.ServersBottomSheetFragment
import chat.rocket.android.sortingandgrouping.ui.SortingAndGroupingBottomSheetFragment
import chat.rocket.android.util.extension.onQueryTextListener
import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.ifNotNullNotEmpty
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.widget.DividerItemDecoration
import chat.rocket.core.internal.realtime.socket.model.State
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.app_bar_chat_rooms.*
import kotlinx.android.synthetic.main.fragment_chat_rooms.*
import javax.inject.Inject

internal const val TAG_CHAT_ROOMS_FRAGMENT = "ChatRoomsFragment"

private const val BUNDLE_CHAT_ROOM_ID = "BUNDLE_CHAT_ROOM_ID"

fun newInstance(chatRoomId: String?, deepLinkInfo: DeepLinkInfo?): Fragment =
    ChatRoomsFragment().apply {
        arguments = Bundle(1).apply {
            putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
            putParcelable(
                chat.rocket.android.authentication.domain.model.DEEP_LINK_INFO_KEY,
                deepLinkInfo
            )
        }
    }

class ChatRoomsFragment : Fragment(), ChatRoomsView {
    @Inject lateinit var presenter: ChatRoomsPresenter
    @Inject lateinit var factory: ChatRoomsViewModelFactory
    @Inject lateinit var analyticsManager: AnalyticsManager
    private lateinit var viewModel: ChatRoomsViewModel
    private var chatRoomId: String? = null
    private var deepLinkInfo: DeepLinkInfo? = null

    private var isSortByName = false
    private var isUnreadOnTop = false
    private var isGroupByType = false
    private var isGroupByFavorites = false

    private val handler = Handler()
    private val dismissConnectionState by lazy { text_connection_status.fadeOut() }
    private var lastConnectionState: State? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        AndroidSupportInjection.inject(this)

        arguments?.run {
            chatRoomId = getString(BUNDLE_CHAT_ROOM_ID)

            chatRoomId.ifNotNullNotEmpty {
                presenter.loadChatRoom(it)
                chatRoomId = null
            }
            deepLinkInfo =
                getParcelable(chat.rocket.android.authentication.domain.model.DEEP_LINK_INFO_KEY)
        }

        setHasOptionsMenu(true)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? = container?.inflate(R.layout.fragment_chat_rooms)

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

        with(presenter) {
            getCurrentServerName()
            getSortingAndGroupingPreferences()
        }

        viewModel = ViewModelProviders.of(this, factory).get(ChatRoomsViewModel::class.java)
        subscribeUi()

        deepLinkInfo?.let {
            processDeepLink(it)
        }
        deepLinkInfo = null

        setupListeners()

        analyticsManager.logScreenView(ScreenViewEvent.ChatRooms)
    }

    override fun setupToolbar(serverName: String) {
        with((activity as AppCompatActivity)) {
            with(toolbar) {
                setSupportActionBar(this)
                supportActionBar?.setDisplayShowTitleEnabled(false)
                setNavigationOnClickListener { presenter.toSettings() }
            }
        }
        text_server_name.text = serverName
    }

    override fun setupSortingAndGrouping(
        isSortByName: Boolean,
        isUnreadOnTop: Boolean,
        isGroupByType: Boolean,
        isGroupByFavorites: Boolean
    ) {
        this.isSortByName = isSortByName
        this.isUnreadOnTop = isUnreadOnTop
        this.isGroupByType = isGroupByType
        this.isGroupByFavorites = isGroupByFavorites

        if (isSortByName) {
            text_sort_by.text =
                getString(
                    R.string.msg_sort_by_placeholder,
                    getString(R.string.msg_sort_by_name).toLowerCase()
                )
        } else {
            text_sort_by.text = getString(
                R.string.msg_sort_by_placeholder,
                getString(R.string.msg_sort_by_activity).toLowerCase()
            )
        }
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        super.onCreateOptionsMenu(menu, inflater)
        inflater.inflate(R.menu.chatrooms, menu)

        val searchMenuItem = menu.findItem(R.id.action_search)
        val searchView = searchMenuItem?.actionView as SearchView

        with(searchView) {
            setIconifiedByDefault(false)
            maxWidth = Integer.MAX_VALUE
            onQueryTextListener { queryChatRoomsByName(it) }
        }

        searchMenuItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
            override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
                // We need to show all the menu items here by invalidating the options to recreate the entire menu.
                activity?.invalidateOptionsMenu()
                queryChatRoomsByName(null)
                hideDirectoryView()
                return true
            }

            override fun onMenuItemActionExpand(item: MenuItem): Boolean {
                // We need to hide the all the menu items here.
                menu.findItem(R.id.action_new_channel).isVisible = false
                showDirectoryView()
                return true
            }
        })
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.action_new_channel -> presenter.toCreateChannel()
        }
        return super.onOptionsItemSelected(item)
    }

    private fun showNoChatRoomsToDisplay() {
//        ui { text_no_data_to_display.isVisible = true }
    }

    override fun showLoading() {
        ui {
            view_loading.isVisible = true
        }
    }

    override fun hideLoading() {
        ui {
            view_loading.isVisible = false
        }
    }

    override fun showMessage(resId: Int) {
        ui { showToast(resId) }
    }

    override fun showMessage(message: String) {
        ui { showToast(message) }
    }

    override fun showGenericErrorMessage() {
        ui {
            showMessage(getString(R.string.msg_generic_error))
        }
    }

    private fun showConnectionState(state: State) {
        ui {
            if (state != lastConnectionState) {
                text_connection_status.fadeIn()
                handler.removeCallbacks { dismissConnectionState }
                text_connection_status.text = when (state) {
                    is State.Connected -> {
                        handler.postDelayed({ dismissConnectionState }, 1000)
                        getString(R.string.status_connected)
                    }
                    is State.Disconnected -> getString(R.string.status_disconnected)
                    is State.Connecting -> getString(R.string.status_connecting)
                    is State.Authenticating -> getString(R.string.status_authenticating)
                    is State.Disconnecting -> getString(R.string.status_disconnecting)
                    is State.Waiting -> getString(R.string.status_waiting, state.seconds)
                    is State.Created -> "" // Show nothing
                }
                lastConnectionState = state
            }
        }
    }

    private fun subscribeUi() {
        ui {
            val adapter = RoomsAdapter { room ->
                presenter.loadChatRoom(room)
            }

            with(recycler_view) {
                layoutManager = LinearLayoutManager(it)
                addItemDecoration(
                    DividerItemDecoration(
                        it,
                        resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_start),
                        resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_end)
                    )
                )
                itemAnimator = DefaultItemAnimator()
            }

            viewModel.getChatRooms().observe(viewLifecycleOwner, Observer { rooms ->
                rooms?.let {
                    adapter.values = it
                    if (recycler_view.adapter != adapter) {
                        recycler_view.adapter = adapter
                    }
                    if (rooms.isNotEmpty()) {
//                        text_no_data_to_display.isVisible = false
                    }
                }
            })

            viewModel.loadingState.observe(viewLifecycleOwner, Observer { state ->
                when (state) {
                    is LoadingState.Loading -> if (state.count == 0L) showLoading()
                    is LoadingState.Loaded -> {
                        hideLoading()
                        if (state.count == 0L) showNoChatRoomsToDisplay()
                    }
                    is LoadingState.Error -> {
                        hideLoading()
                        showGenericErrorMessage()
                    }
                    is LoadingState.AuthError -> {
                        hideLoading()
                        showMessage(R.string.msg_invalid_session)
                    }
                }
            })

            viewModel.getStatus().observe(viewLifecycleOwner, Observer {
                showConnectionState(it)
            })

            showAllChats()
        }
    }

    private fun setupListeners() {
        if (getString(R.string.server_url).isEmpty()) {
            text_server_name.setOnClickListener {
                ServersBottomSheetFragment().show(
                    activity?.supportFragmentManager,
                    chat.rocket.android.servers.ui.TAG
                )
            }
        }

        text_sort_by.setOnClickListener {
            SortingAndGroupingBottomSheetFragment().show(
                activity?.supportFragmentManager,
                chat.rocket.android.sortingandgrouping.ui.TAG
            )
        }

        text_directory.setOnClickListener { presenter.toDirectory() }
    }

    fun sortChatRoomsList(
        isSortByName: Boolean,
        isUnreadOnTop: Boolean,
        isGroupByType: Boolean,
        isGroupByFavorites: Boolean
    ) {
        this.isSortByName = isSortByName
        this.isUnreadOnTop = isUnreadOnTop
        this.isGroupByType = isGroupByType
        this.isGroupByFavorites = isGroupByFavorites

        if (isSortByName) {
            viewModel.setQuery(Query.ByName(isGroupByType, isUnreadOnTop))
            changeSortByTitle(getString(R.string.msg_sort_by_name))
        } else {
            viewModel.setQuery(Query.ByActivity(isGroupByType, isUnreadOnTop))
            changeSortByTitle(getString(R.string.msg_sort_by_activity))
        }
    }

    private fun changeSortByTitle(text: String) {
        text_sort_by.text = getString(R.string.msg_sort_by_placeholder, text.toLowerCase())
    }

    private fun queryChatRoomsByName(name: String?): Boolean {
        if (name.isNullOrEmpty()) {
            showAllChats()
        } else {
            viewModel.setQuery(Query.Search(name))
        }
        return true
    }

    fun processDeepLink(deepLinkInfo: DeepLinkInfo) {
        val username = deepLinkInfo.roomName
        username.ifNotNullNotEmpty {
            val localRooms = viewModel.getUsersRoomListLocal(username!!)
            val filteredLocalRooms =
                localRooms.filter { itemHolder -> itemHolder.data is RoomUiModel && (itemHolder.data as RoomUiModel).username == username }

            if (filteredLocalRooms.isNotEmpty()) {
                presenter.loadChatRoom(filteredLocalRooms.first().data as RoomUiModel)
            } else {
                loadRoomFromSpotlight(username)
            }
        }
    }

    private fun loadRoomFromSpotlight(username: String) {
        //check from spotlight when connected
        val statusLiveData = viewModel.getStatus()
        statusLiveData.observe(viewLifecycleOwner, object : Observer<State> {
            override fun onChanged(status: State?) {
                if (status is State.Connected) {
                    val rooms = viewModel.getUsersRoomListSpotlight(username)
                    val filteredRooms =
                        rooms?.filter { itemHolder -> itemHolder.data is RoomUiModel && (itemHolder.data as RoomUiModel).username == username }

                    filteredRooms?.let {
                        if (filteredRooms.isNotEmpty()) {
                            presenter.loadChatRoom(filteredRooms.first().data as RoomUiModel)
                        }
                    }
                    statusLiveData.removeObserver(this)
                }
            }
        })
    }

    private fun showAllChats() {
        if (isSortByName) {
            viewModel.setQuery(Query.ByName(isGroupByType, isUnreadOnTop))
        } else {
            viewModel.setQuery(Query.ByActivity(isGroupByType, isUnreadOnTop))
        }
    }

    private fun showDirectoryView() {
        text_directory.isVisible = true
        text_sort_by.isGone = true
    }

    private fun hideDirectoryView() {
        text_directory.isGone = true
        text_sort_by.isVisible = true
    }
}