package cash.z.android.wallet.ui.fragment

import android.animation.Animator
import android.app.Activity
import android.os.Bundle
import android.text.SpannableString
import android.text.Spanned
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.AccelerateInterpolator
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.ChangeBounds
import androidx.transition.Transition
import androidx.transition.TransitionInflater
import androidx.transition.TransitionSet
import cash.z.android.wallet.R
import cash.z.android.wallet.extention.Toaster
import cash.z.android.wallet.extention.toAppColor
import cash.z.android.wallet.extention.toAppString
import cash.z.android.wallet.extention.tryIgnore
import cash.z.android.wallet.ui.activity.MainActivity
import cash.z.android.wallet.ui.adapter.TransactionAdapter
import cash.z.android.wallet.ui.presenter.HomePresenter
import cash.z.android.wallet.ui.util.AlternatingRowColorDecoration
import cash.z.android.wallet.ui.util.TopAlignedSpan
import cash.z.android.wallet.vo.WalletTransaction
import com.leinardi.android.speeddial.SpeedDialActionItem
import dagger.Module
import dagger.android.ContributesAndroidInjector
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.include_home_content.*
import kotlinx.android.synthetic.main.include_home_header.*
import kotlinx.coroutines.launch
import kotlin.random.Random
import com.google.android.material.snackbar.Snackbar


/**
 * Fragment representing the home screen of the app. This is the screen most often seen by the user when launching the
 * application.
 */
class HomeFragment : BaseFragment(), HomePresenter.HomeView {

    lateinit var homePresenter: HomePresenter
    lateinit var transactionAdapter: TransactionAdapter
    var viewsInitialized = false

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        viewsInitialized = false
        setupSharedElementTransitions()

        return inflater.inflate(R.layout.fragment_home, container, false)
    }

    private fun setupSharedElementTransitions() {
        val enterTransitionSet =
            TransitionInflater.from(mainActivity).inflateTransition(R.transition.transition_zec_sent).apply {
                duration = 300L
            }.addListener(object : Transition.TransitionListener {
                override fun onTransitionEnd(transition: Transition) {
                    // fixes a bug where the translation gets lost, during animation. As a nice side effect, visually, it makes the view appear to settle in to position
                    header_active_transaction.translationZ = 10.0f
                }

                override fun onTransitionResume(transition: Transition) {
                }

                override fun onTransitionPause(transition: Transition) {
                }

                override fun onTransitionCancel(transition: Transition) {
                }

                override fun onTransitionStart(transition: Transition) {
                }

            })

        this.sharedElementEnterTransition = enterTransitionSet
        this.sharedElementReturnTransition = enterTransitionSet
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        (activity as MainActivity).let { mainActivity ->
            mainActivity.setSupportActionBar(home_toolbar)
            mainActivity.setupNavigation()
            mainActivity.supportActionBar?.setTitle(R.string.destination_title_home)
        }
        headerFullViews = arrayOf(text_balance_usd, text_balance_includes_info, text_balance_zec, image_zec_symbol_balance_shadow, image_zec_symbol_balance)
        headerEmptyViews = arrayOf(text_balance_zec_info, text_balance_zec_empty, image_zec_symbol_balance_shadow_empty, image_zec_symbol_balance_empty)

        // toggling determines visibility. hide it all.
        headerFullViews.forEach { container_home_header.removeView(it) }
        headerEmptyViews.forEach { container_home_header.removeView(it) }
        group_empty_view_items.visibility = View.GONE
        group_full_view_items.visibility = View.GONE

        if (MainActivity.DEV_MODE) {
            image_logo.setOnClickListener {
                mainActivity.navController.navigate(R.id.nav_send_fragment)
                //            forceRedraw()
                //            toggleViews(false)

            }
        }

        button_active_transaction_cancel.setOnClickListener {
            onCancelActiveTransaction()
        }
    }

    override fun onResume() {
        super.onResume()
        val isEmpty = (recycler_transactions?.adapter?.itemCount ?: 0).let { it == 0 }
//        Log.e("TWIG-t", "Resuming and isEmpty == $isEmpty")
//        toggleViews(isEmpty)

        launch {
            homePresenter.start()
        }
    }

    override fun onPause() {
        super.onPause()
        homePresenter.stop()
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        homePresenter = HomePresenter(this, mainActivity.synchronizer)
        initFab(activity!!)

        recycler_transactions.apply {
            layoutManager = LinearLayoutManager(activity, RecyclerView.VERTICAL, false)
            adapter = TransactionAdapter().also { transactionAdapter = it }
            addItemDecoration(AlternatingRowColorDecoration())
        }
    }


    //
    // View API
    //
    // TODO: pull some of this logic into the presenter, particularly the part that deals with ZEC <-> USD price conversion
    override fun updateBalance(old: Long, new: Long) {
        //TODO: remove this kind of thing
        snackbar?.dismiss()

        Log.e("TWIG-t", "updating balance from $old to $new")
        val zecValue = new/1e8
        swapEmptyViewsForBalance(zecValue)

        // TODO: animate the change in value
        setZecValue(zecValue)
        setUsdValue(MainActivity.USD_PER_ZEC * zecValue)
    }

    override fun setTransactions(transactions: List<WalletTransaction>) {
        Log.e("TWIG-t", "submitList called with ${transactions.size} transactions")
        transactionAdapter.submitList(transactions)
        recycler_transactions.smoothScrollToPosition(0)
    }

    var snackbar: Snackbar? = null
    override fun showProgress(progress: Int) {
        Log.e("TWIG", "showing progress of $progress")
        // TODO: improve this with Lottie animation. but for now just use the empty view for downloading...
//        var hasEmptyViews = group_empty_view_items.visibility == View.VISIBLE
//        if(!viewsInitialized) toggleViews(true)
//
        val message = if(progress >=  100) "Download complete! Processing...\n(this may take a while)" else "Downloading blocks ($progress%)"
//        text_wallet_message.text = message

        if (snackbar == null && progress <= 50) {
            snackbar = Snackbar.make(view!!, "$message", Snackbar.LENGTH_INDEFINITE)
                .setAction("OK") {
                    snackbar?.dismiss()
                }
            snackbar?.show()
        } else {
            snackbar?.setText(message)
            if(progress == 100 && snackbar?.isShownOrQueued != true) snackbar?.show()
        }
    }


    //
    // Private View API
    //

    /**
     * Initialize the Fab button and all its action items
     *
     * @param activity a helper parameter that forces this method to be called after the activity is created and not null
     */
    private fun initFab(activity: Activity) {
        val speedDial = sd_fab
        val nav = (activity as MainActivity).navController

        HomeFab.values().forEach {
            speedDial.addActionItem(it.createItem())
        }

        speedDial.setOnActionSelectedListener { item ->
            HomeFab.fromId(item.id)?.destination?.apply { nav.navigate(this) }
            false
        }
    }

    private val createItem: HomeFab.() -> SpeedDialActionItem = {
        SpeedDialActionItem.Builder(id, icon)
            .setFabBackgroundColor(bgColor.toAppColor())
            .setFabImageTintColor(R.color.zcashWhite.toAppColor())
            .setLabel(label.toAppString())
            .setLabelClickable(true)
            .create()
    }

    private fun setUsdValue(value: Double) {
        val valueString = String.format("$ %,.2f",value)
        val hairSpace = "\u200A"
//        val adjustedValue = "$$hairSpace$valueString"
        val textSpan = SpannableString(valueString)
        textSpan.setSpan(TopAlignedSpan(), 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        textSpan.setSpan(TopAlignedSpan(), valueString.length - 3, valueString.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        text_balance_usd.text = textSpan
    }

    private fun setZecValue(value: Double) {
        text_balance_zec.text = if(value == 0.0) "0" else String.format("%.3f",value)
//        // bugfix: there is a bug in motionlayout that causes text to flicker as it is resized because the last character doesn't fit. Padding both sides with a thin space works around this bug.
//        val hairSpace = "\u200A"
//        val adjustedValue = "$hairSpace$valueString$hairSpace"
//        text_balance_zec.text = adjustedValue
    }

    /**
     * If the balance goes to zero, the wallet is now empty so show the empty view.
     * If the balance changes from zero, the wallet is no longer empty so hide the empty view.
     * But don't do either of these things if the situation has not changed.
     */
    private fun swapEmptyViewsForBalance(value: Double) {
        val isEmpty = value <= 0.0
        // wasEmpty isn't enough info. it must be considered along with whether these views were ever initialized
        val wasEmpty = group_empty_view_items.visibility == View.VISIBLE
        // situation has changed when we weren't initialized but now we have a balance or emptiness has changed
        val situationHasChanged = !viewsInitialized || (isEmpty != wasEmpty)

        Log.e("TWIG-t", "updateEmptyViews called with value: $value  initialized: $viewsInitialized  isEmpty: $isEmpty  wasEmpty: $wasEmpty")
        if (situationHasChanged) {
            Log.e("TWIG-t", "The situation has changed! toggling views!")
            toggleViews(isEmpty)
        }
    }

    private fun onCancelActiveTransaction() {
        button_active_transaction_cancel.isEnabled = false
        button_active_transaction_cancel.text = "canceled"
        header_active_transaction.animate().apply {
            translationZ(0f)
            duration = 200L
            interpolator = AccelerateInterpolator()
            setListener(object : Animator.AnimatorListener {
                override fun onAnimationRepeat(animation: Animator?) {
                }

                override fun onAnimationEnd(animation: Animator?) {
                    header_active_transaction.setBackgroundResource(0)
                }

                override fun onAnimationCancel(animation: Animator?) {
                }

                override fun onAnimationStart(animation: Animator?) {
                }

            } )
        }

        homePresenter.onCancelActiveTransaction()
    }

    /**
     * Defines the basic properties of each FAB button for use while initializing the FAB
     */
    enum class HomeFab(
        @IdRes val id:Int,
        @DrawableRes val icon:Int,
        @ColorRes val bgColor:Int,
        @StringRes val label:Int,
        @IdRes val destination:Int
    ) {
        /* ordered by when they need to be added to the speed dial (i.e. reverse display order) */
        REQUEST(
            R.id.fab_request,
            R.drawable.ic_receipt_24dp,
            R.color.icon_request,
            R.string.destination_menu_label_request,
            R.id.nav_request_fragment
        ),
        RECEIVE(
            R.id.fab_receive,
            R.drawable.ic_qrcode_24dp,
            R.color.icon_receive,
            R.string.destination_menu_label_receive,
            R.id.nav_receive_fragment
        ),
        SEND(
            R.id.fab_send,
            R.drawable.ic_menu_send,
            R.color.icon_send,
            R.string.destination_menu_label_send,
            R.id.nav_send_fragment
        );

        companion object {
            fun fromId(id: Int): HomeFab? = values().firstOrNull { it.id == id }
        }
    }



// ---------------------------------------------------------------------------------------------------------------------
// TODO: Delete these test functions
// ---------------------------------------------------------------------------------------------------------------------

    var empty = false
    val delay = 50L
    lateinit var headerEmptyViews: Array<View>
    lateinit var headerFullViews: Array<View>

    fun shrink(): Double {
        return text_balance_zec.text.toString().trim().toDouble() - Random.nextDouble(5.0)
    }
    fun grow(): Double {
        return text_balance_zec.text.toString().trim().toDouble() + Random.nextDouble(5.0)
    }
    fun reduceValue() {
        shrink().let {
            if(it < 0) { setZecValue(0.0); toggleViews(empty); forceRedraw() }
            else view?.postDelayed({
                setZecValue(it)
                setUsdValue(it*75.0)
                reduceValue()
            }, delay)
        }
    }
    fun increaseValue(target: Double) {
        grow().let {
            if(it > target) { setZecValue(target); setUsdValue(target*75.0); toggleViews(empty) }
            else view?.postDelayed({
                setZecValue(it)
                setUsdValue(it*75.0)
                increaseValue(target)
                if (headerFullViews[0].parent == null || headerEmptyViews[0].parent != null) toggleViews(false)
                forceRedraw()
            }, delay)
        }
    }
    fun forceRedraw() {
        view?.postDelayed({
            container_home_header.progress = container_home_header.progress - 0.1f
        }, delay * 2)
    }
    internal fun toggle(isEmpty: Boolean) {
        toggleValues(isEmpty)
    }

    // TODO: get rid of all of this and consider two different fragments for the header, instead
    internal fun toggleViews(isEmpty: Boolean) {
        Log.e("TWIG-t", "toggling views to isEmpty == $isEmpty")
        var action: () -> Unit
        if (isEmpty) {
            action = {
                group_empty_view_items.visibility = View.VISIBLE
                group_full_view_items.visibility = View.GONE
                headerFullViews.forEach { container_home_header.removeView(it) }
                headerEmptyViews.forEach {
                    tryIgnore {
                        container_home_header.addView(it)
                    }
                }
            }
        } else {
            action = {
                group_empty_view_items.visibility = View.GONE
                group_full_view_items.visibility = View.VISIBLE
                headerEmptyViews.forEach { container_home_header.removeView(it) }
                headerFullViews.forEach {
                    tryIgnore {
                        container_home_header.addView(it)
                    }
                }
            }
        }
        view?.postDelayed({
            action()
            viewsInitialized = true
        }, delay)
        // TODO: the motion layout does not begin in the  right state for some reason. Debug this later.
        view?.postDelayed(::forceRedraw, delay * 2)
    }

    internal fun toggleValues(isEmpty: Boolean) {
        empty = isEmpty
        if(empty) {
            reduceValue()
        } else {
            increaseValue(Random.nextDouble(20.0, 100.0))
        }
    }
}

@Module
abstract class HomeFragmentModule {
    @ContributesAndroidInjector
    abstract fun contributeHomeFragment(): HomeFragment
}


