package org.thoughtcrime.securesms.home

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListUpdateCallback
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.NO_ID
import network.loki.messenger.R
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.mms.GlideRequests

class HomeAdapter(
    private val context: Context,
    private val listener: ConversationClickListener
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), ListUpdateCallback {

    companion object {
        private const val HEADER = 0
        private const val ITEM = 1
    }

    var header: View? = null

    private var _data: List<ThreadRecord> = emptyList()
    var data: List<ThreadRecord>
        get() = _data.toList()
        set(newData) {
            val previousData = _data.toList()
            val diff = HomeDiffUtil(previousData, newData, context)
            val diffResult = DiffUtil.calculateDiff(diff)
            _data = newData
            diffResult.dispatchUpdatesTo(this as ListUpdateCallback)
        }

    fun hasHeaderView(): Boolean = header != null

    private val headerCount: Int
        get() = if (header == null) 0 else 1

    override fun onInserted(position: Int, count: Int) {
        notifyItemRangeInserted(position + headerCount, count)
    }

    override fun onRemoved(position: Int, count: Int) {
        notifyItemRangeRemoved(position + headerCount, count)
    }

    override fun onMoved(fromPosition: Int, toPosition: Int) {
        notifyItemMoved(fromPosition + headerCount, toPosition + headerCount)
    }

    override fun onChanged(position: Int, count: Int, payload: Any?) {
        notifyItemRangeChanged(position + headerCount, count, payload)
    }

    override fun getItemId(position: Int): Long  {
        if (hasHeaderView() && position == 0) return NO_ID
        val offsetPosition = if (hasHeaderView()) position-1 else position
        return _data[offsetPosition].threadId
    }

    lateinit var glide: GlideRequests
    var typingThreadIDs = setOf<Long>()
        set(value) {
            if (field == value) { return }

            field = value
            // TODO: replace this with a diffed update or a partial change set with payloads
            notifyDataSetChanged()
        }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
        when (viewType) {
            HEADER -> {
                HeaderFooterViewHolder(header!!)
            }
            ITEM -> {
                val conversationView = LayoutInflater.from(parent.context).inflate(R.layout.view_conversation, parent, false) as ConversationView
                val viewHolder = ConversationViewHolder(conversationView)
                viewHolder.view.setOnClickListener { viewHolder.view.thread?.let { listener.onConversationClick(it) } }
                viewHolder.view.setOnLongClickListener {
                    viewHolder.view.thread?.let { listener.onLongConversationClick(it) }
                    true
                }
                viewHolder
            }
            else -> throw Exception("viewType $viewType isn't valid")
        }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (holder is ConversationViewHolder) {
            val offset = if (hasHeaderView()) position - 1 else position
            val thread = data[offset]
            val isTyping = typingThreadIDs.contains(thread.threadId)
            holder.view.bind(thread, isTyping, glide)
        }
    }

    override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
        if (holder is ConversationViewHolder) {
            holder.view.recycle()
        } else {
            super.onViewRecycled(holder)
        }
    }

    override fun getItemViewType(position: Int): Int =
        if (hasHeaderView() && position == 0) HEADER
        else ITEM

    override fun getItemCount(): Int = data.size + if (hasHeaderView()) 1 else 0

    class ConversationViewHolder(val view: ConversationView) : RecyclerView.ViewHolder(view)

    class HeaderFooterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

}