/* Copyright 2018 Conny Duck
 *
 * This file is a part of lecjoa.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * lecjoa is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with lecjoa; if not,
 * see <http://www.gnu.org/licenses>. */

package net.letime.lecjoa

import android.animation.ArgbEvaluator
import android.app.Activity
import android.app.AlertDialog
import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Build
import android.os.Bundle
import android.preference.PreferenceManager
import android.support.annotation.AttrRes
import android.support.annotation.ColorInt
import android.support.annotation.Px
import android.support.design.widget.*
import android.support.text.emoji.EmojiCompat
import android.support.v4.app.Fragment
import android.support.v4.content.ContextCompat
import android.support.v4.view.ViewCompat
import android.support.v4.widget.TextViewCompat
import android.support.v7.widget.LinearLayoutManager
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import net.letime.lecjoa.adapter.AccountFieldAdapter
import net.letime.lecjoa.di.ViewModelFactory
import net.letime.lecjoa.entity.Account
import net.letime.lecjoa.entity.Relationship
import net.letime.lecjoa.interfaces.ActionButtonActivity
import net.letime.lecjoa.interfaces.LinkListener
import net.letime.lecjoa.pager.AccountPagerAdapter
import net.letime.lecjoa.util.*
import net.letime.lecjoa.view.RoundedTransformation
import net.letime.lecjoa.viewmodel.AccountViewModel
import com.squareup.picasso.Picasso
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector
import kotlinx.android.synthetic.main.activity_account.*
import kotlinx.android.synthetic.main.view_account_moved.*
import java.text.NumberFormat
import javax.inject.Inject

class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportFragmentInjector, LinkListener {

    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
    @Inject
    lateinit var viewModelFactory: ViewModelFactory

    private lateinit var viewModel: AccountViewModel

    private val accountFieldAdapter = AccountFieldAdapter(this)

    private lateinit var accountId: String
    private var followState: FollowState? = null
    private var blocking: Boolean = false
    private var muting: Boolean = false
    private var showingReblogs: Boolean = false
    private var isSelf: Boolean = false
    private var loadedAccount: Account? = null

    // fields for scroll animation
    private var hideFab: Boolean = false
    private var oldOffset: Int = 0
    @ColorInt
    private var toolbarColor: Int = 0
    @ColorInt
    private var backgroundColor: Int = 0
    @ColorInt
    private var statusBarColorTransparent: Int = 0
    @ColorInt
    private var statusBarColorOpaque: Int = 0
    @ColorInt
    private var textColorPrimary: Int = 0
    @ColorInt
    private var textColorSecondary: Int = 0
    @Px
    private var avatarSize: Float = 0f
    @Px
    private var titleVisibleHeight: Int = 0

    private enum class FollowState {
        NOT_FOLLOWING,
        FOLLOWING,
        REQUESTED
    }

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

        viewModel = ViewModelProviders.of(this, viewModelFactory)[AccountViewModel::class.java]

        viewModel.accountData.observe(this, Observer<Resource<Account>> {
            when (it) {
                is Success -> onAccountChanged(it.data)
                is Error -> {
                    Snackbar.make(accountCoordinatorLayout, R.string.error_generic, Snackbar.LENGTH_LONG)
                            .setAction(R.string.action_retry) { reload() }
                            .show()
                }
            }
        })
        viewModel.relationshipData.observe(this, Observer<Resource<Relationship>> {
            val relation = it?.data
            if (relation != null) {
                onRelationshipChanged(relation)
            }

            if (it is Error) {
                Snackbar.make(accountCoordinatorLayout, R.string.error_generic, Snackbar.LENGTH_LONG)
                        .setAction(R.string.action_retry) { reload() }
                        .show()
            }

        })

        val decorView = window.decorView
        decorView.systemUiVisibility = decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            window.statusBarColor = Color.TRANSPARENT
        }

        setContentView(R.layout.activity_account)

        val intent = intent
        accountId = intent.getStringExtra(KEY_ACCOUNT_ID)
        followState = FollowState.NOT_FOLLOWING
        blocking = false
        muting = false

        loadedAccount = null

        // set toolbar top margin according to system window insets
        ViewCompat.setOnApplyWindowInsetsListener(accountCoordinatorLayout) { _, insets ->
            val top = insets.systemWindowInsetTop

            val toolbarParams = accountToolbar.layoutParams as CollapsingToolbarLayout.LayoutParams
            toolbarParams.topMargin = top

            insets.consumeSystemWindowInsets()
        }

        // Setup the toolbar.
        setSupportActionBar(accountToolbar)
        supportActionBar?.title = null
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
        supportActionBar?.setDisplayShowHomeEnabled(true)

        hideFab = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("fabHide", false)

        toolbarColor = ThemeUtils.getColor(this, R.attr.toolbar_background_color)
        backgroundColor = ThemeUtils.getColor(this, android.R.attr.colorBackground)
        statusBarColorTransparent = ContextCompat.getColor(this, R.color.header_background_filter)
        statusBarColorOpaque = ThemeUtils.getColor(this, R.attr.colorPrimaryDark)
        textColorPrimary = ThemeUtils.getColor(this, android.R.attr.textColorPrimary)
        textColorSecondary = ThemeUtils.getColor(this, android.R.attr.textColorSecondary)
        avatarSize = resources.getDimensionPixelSize(R.dimen.account_activity_avatar_size).toFloat()
        titleVisibleHeight = resources.getDimensionPixelSize(R.dimen.account_activity_scroll_title_visible_height)

        ThemeUtils.setDrawableTint(this, accountToolbar.navigationIcon, R.attr.account_toolbar_icon_tint_uncollapsed)
        ThemeUtils.setDrawableTint(this, accountToolbar.overflowIcon, R.attr.account_toolbar_icon_tint_uncollapsed)

        // Add a listener to change the toolbar icon color when it enters/exits its collapsed state.
        accountAppBarLayout.addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener {
            @AttrRes var priorAttribute = R.attr.account_toolbar_icon_tint_uncollapsed

            override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) {

                @AttrRes val attribute = if (titleVisibleHeight + verticalOffset < 0) {
                    accountToolbar.setTitleTextColor(textColorPrimary)
                    accountToolbar.setSubtitleTextColor(textColorSecondary)

                    R.attr.account_toolbar_icon_tint_collapsed
                } else {
                    accountToolbar.setTitleTextColor(Color.TRANSPARENT)
                    accountToolbar.setSubtitleTextColor(Color.TRANSPARENT)

                    R.attr.account_toolbar_icon_tint_uncollapsed
                }
                if (attribute != priorAttribute) {
                    priorAttribute = attribute
                    val context = accountToolbar.context
                    ThemeUtils.setDrawableTint(context, accountToolbar.navigationIcon, attribute)
                    ThemeUtils.setDrawableTint(context, accountToolbar.overflowIcon, attribute)
                }

                if (hideFab && !isSelf && !blocking) {
                    if (verticalOffset > oldOffset) {
                        accountFloatingActionButton.show()
                    }
                    if (verticalOffset < oldOffset) {
                        accountFloatingActionButton.hide()
                    }
                }
                oldOffset = verticalOffset

                val scaledAvatarSize = (avatarSize + verticalOffset) / avatarSize

                accountAvatarImageView.scaleX = scaledAvatarSize
                accountAvatarImageView.scaleY = scaledAvatarSize

                accountAvatarImageView.visible(scaledAvatarSize > 0)

                var transparencyPercent = Math.abs(verticalOffset) / titleVisibleHeight.toFloat()
                if (transparencyPercent > 1) transparencyPercent = 1f

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    window.statusBarColor = argbEvaluator.evaluate(transparencyPercent, statusBarColorTransparent, statusBarColorOpaque) as Int
                }

                val evaluatedToolbarColor = argbEvaluator.evaluate(transparencyPercent, Color.TRANSPARENT, toolbarColor) as Int
                val evaluatedTabBarColor = argbEvaluator.evaluate(transparencyPercent, backgroundColor, toolbarColor) as Int
                accountToolbar.setBackgroundColor(evaluatedToolbarColor)
                accountHeaderInfoContainer.setBackgroundColor(evaluatedTabBarColor)
                accountTabLayout.setBackgroundColor(evaluatedTabBarColor)
            }
        })

        // Initialise the default UI states.
        accountFloatingActionButton.hide()
        accountFollowButton.hide()
        accountFollowsYouTextView.hide()

        // Obtain information to fill out the profile.
        viewModel.obtainAccount(accountId)

        val activeAccount = accountManager.activeAccount

        if (accountId == activeAccount?.accountId) {
            isSelf = true
            updateButtons()
        } else {
            isSelf = false
            viewModel.obtainRelationship(accountId)
        }

        // setup the RecyclerView for the account fields
        accountFieldList.isNestedScrollingEnabled = false
        accountFieldList.layoutManager = LinearLayoutManager(this)
        accountFieldList.adapter = accountFieldAdapter

        // Setup the tabs and timeline pager.
        val adapter = AccountPagerAdapter(supportFragmentManager, accountId)
        val pageTitles = arrayOf(getString(R.string.title_statuses), getString(R.string.title_statuses_with_replies), getString(R.string.title_media))
        adapter.setPageTitles(pageTitles)
        accountFragmentViewPager.pageMargin = resources.getDimensionPixelSize(R.dimen.tab_page_margin)
        val pageMarginDrawable = ThemeUtils.getDrawable(this, R.attr.tab_page_margin_drawable,
                R.drawable.tab_page_margin_dark)
        accountFragmentViewPager.setPageMarginDrawable(pageMarginDrawable)
        accountFragmentViewPager.adapter = adapter
        accountFragmentViewPager.offscreenPageLimit = 2
        accountTabLayout.setupWithViewPager(accountFragmentViewPager)

        val accountListClickListener = { v: View ->
            val type = when (v.id) {
                R.id.accountFollowers-> AccountListActivity.Type.FOLLOWERS
                R.id.accountFollowing -> AccountListActivity.Type.FOLLOWING
                else -> throw AssertionError()
            }
            val accountListIntent = AccountListActivity.newIntent(this, type, accountId)
            startActivity(accountListIntent)
        }
        accountFollowers.setOnClickListener(accountListClickListener)
        accountFollowing.setOnClickListener(accountListClickListener)

        accountStatuses.setOnClickListener {
            // Make nice ripple effect on tab
            accountTabLayout.getTabAt(0)!!.select()
            val poorTabView = (accountTabLayout.getChildAt(0) as ViewGroup).getChildAt(0)
            poorTabView.isPressed = true
            accountTabLayout.postDelayed({ poorTabView.isPressed = false }, 300)
        }
    }

    private fun onAccountChanged(account: Account?) {
        if (account != null) {
            loadedAccount = account
            val usernameFormatted = getString(R.string.status_username_format, account.username)
            accountUsernameTextView.text = usernameFormatted
            accountDisplayNameTextView.text = CustomEmojiHelper.emojifyString(account.name, account.emojis, accountDisplayNameTextView)
            if (supportActionBar != null) {
                try {
                    supportActionBar?.title = EmojiCompat.get().process(account.name)
                } catch (e: IllegalStateException) {
                    // some Android versions seem to have problems with custom emoji fonts
                    supportActionBar?.title = account.name
                }

                val subtitle = String.format(getString(R.string.status_username_format),
                        account.username)
                supportActionBar?.subtitle = subtitle
            }
            val emojifiedNote = CustomEmojiHelper.emojifyText(account.note, account.emojis, accountNoteTextView)
            LinkHelper.setClickableText(accountNoteTextView, emojifiedNote, null, this)

            accountLockedImageView.visible(account.locked)
            accountBadgeTextView.visible(account.bot)

            Picasso.with(this)
                    .load(account.avatar)
                    .transform(RoundedTransformation(25f))
                    .placeholder(R.drawable.avatar_default)
                    .into(accountAvatarImageView)
            Picasso.with(this)
                    .load(account.header)
                    .into(accountHeaderImageView)

            accountFieldAdapter.fields = account.fields ?: emptyList()
            accountFieldAdapter.emojis = account.emojis ?: emptyList()
            accountFieldAdapter.notifyDataSetChanged()

            if (account.moved != null) {
                val movedAccount = account.moved

                accountMovedView.show()

                // necessary because accountMovedView is now replaced in layout hierachy
                findViewById<View>(R.id.accountMovedView).setOnClickListener {
                    onViewAccount(movedAccount.id)
                }

                accountMovedDisplayName.text = movedAccount.name
                accountMovedUsername.text = getString(R.string.status_username_format, movedAccount.username)

                Picasso.with(this)
                        .load(movedAccount.avatar)
                        .transform(RoundedTransformation(25f))
                        .placeholder(R.drawable.avatar_default)
                        .into(accountMovedAvatar)

                accountMovedText.text = getString(R.string.account_moved_description, movedAccount.displayName)

                // this is necessary because API 19 can't handle vector compound drawables
                val movedIcon = ContextCompat.getDrawable(this, R.drawable.ic_briefcase)?.mutate()
                val textColor = ThemeUtils.getColor(this, android.R.attr.textColorTertiary)
                movedIcon?.setColorFilter(textColor, PorterDuff.Mode.SRC_IN)

                TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(accountMovedText, movedIcon, null, null, null)

                accountFollowers.hide()
                accountFollowing.hide()
                accountStatuses.hide()
                accountTabLayout.hide()
                accountFragmentViewPager.hide()
                accountTabBottomShadow.hide()
            }

            val numberFormat = NumberFormat.getNumberInstance()
            accountFollowersTextView.text = numberFormat.format(account.followersCount)
            accountFollowingTextView.text = numberFormat.format(account.followingCount)
            accountStatusesTextView.text = numberFormat.format(account.statusesCount)

            accountFloatingActionButton.setOnClickListener { _ -> mention() }

            accountFollowButton.setOnClickListener { _ ->
                if (isSelf) {
                    val intent = Intent(this@AccountActivity, EditProfileActivity::class.java)
                    startActivityForResult(intent, EDIT_ACCOUNT)
                    return@setOnClickListener
                }
                when (followState) {
                    AccountActivity.FollowState.NOT_FOLLOWING -> {
                        viewModel.changeFollowState(accountId)
                    }
                    AccountActivity.FollowState.REQUESTED -> {
                        showFollowRequestPendingDialog()
                    }
                    AccountActivity.FollowState.FOLLOWING -> {
                        showUnfollowWarningDialog()
                    }
                }
                updateFollowButton()
            }
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        //reload account when returning from EditProfileActivity
        if(requestCode == EDIT_ACCOUNT && resultCode == Activity.RESULT_OK) {
            viewModel.obtainAccount(accountId, true)
        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        outState.putString(KEY_ACCOUNT_ID, accountId)
        super.onSaveInstanceState(outState)
    }

    private fun onRelationshipChanged(relation: Relationship) {
        followState = when {
            relation.following -> FollowState.FOLLOWING
            relation.requested -> FollowState.REQUESTED
            else -> FollowState.NOT_FOLLOWING
        }
        blocking = relation.blocking
        muting = relation.muting
        showingReblogs = relation.showingReblogs

        accountFollowsYouTextView.visible(relation.followedBy)

        updateButtons()
    }

    private fun reload() {
        viewModel.obtainAccount(accountId, true)
        viewModel.obtainRelationship(accountId)
    }

    private fun updateFollowButton() {
        if(isSelf) {
            accountFollowButton.setText(R.string.action_edit_own_profile)
            return
        }
        when (followState) {
            AccountActivity.FollowState.NOT_FOLLOWING -> {
                accountFollowButton.setText(R.string.action_follow)
            }
            AccountActivity.FollowState.REQUESTED -> {
                accountFollowButton.setText(R.string.state_follow_requested)
            }
            AccountActivity.FollowState.FOLLOWING -> {
                accountFollowButton.setText(R.string.action_unfollow)
            }
        }
    }

    private fun updateButtons() {
        invalidateOptionsMenu()

        if (!blocking && loadedAccount?.moved == null) {

            accountFollowButton.show()
            updateFollowButton()

            if(isSelf) {
                accountFloatingActionButton.hide()
            } else {
                accountFloatingActionButton.show()
            }

        } else {
            accountFloatingActionButton.hide()
            accountFollowButton.hide()
        }
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.account_toolbar, menu)
        return super.onCreateOptionsMenu(menu)
    }

    override fun onPrepareOptionsMenu(menu: Menu): Boolean {
        if (!isSelf) {
            val follow = menu.findItem(R.id.action_follow)
            follow.title = if (followState == FollowState.NOT_FOLLOWING) {
                getString(R.string.action_follow)
            } else {
                getString(R.string.action_unfollow)
            }

            follow.isVisible = followState != FollowState.REQUESTED

            val block = menu.findItem(R.id.action_block)
            block.title = if (blocking) {
                getString(R.string.action_unblock)
            } else {
                getString(R.string.action_block)
            }

            val mute = menu.findItem(R.id.action_mute)
            mute.title = if (muting) {
                getString(R.string.action_unmute)
            } else {
                getString(R.string.action_mute)
            }

            if (followState == FollowState.FOLLOWING) {
                val showReblogs = menu.findItem(R.id.action_show_reblogs)
                showReblogs.title = if (showingReblogs) {
                    getString(R.string.action_hide_reblogs)
                } else {
                    getString(R.string.action_show_reblogs)
                }

            } else {
                menu.removeItem(R.id.action_show_reblogs)
            }

        } else {
            // It shouldn't be possible to block or follow yourself.
            menu.removeItem(R.id.action_follow)
            menu.removeItem(R.id.action_block)
            menu.removeItem(R.id.action_mute)
            menu.removeItem(R.id.action_show_reblogs)
        }
        return super.onPrepareOptionsMenu(menu)
    }

    private fun showFollowRequestPendingDialog() {
        AlertDialog.Builder(this)
                .setMessage(R.string.dialog_message_cancel_follow_request)
                .setPositiveButton(android.R.string.ok) { _, _ -> viewModel.changeFollowState(accountId) }
                .setNegativeButton(android.R.string.cancel, null)
                .show()
    }

    private fun showUnfollowWarningDialog() {
        AlertDialog.Builder(this)
                .setMessage(R.string.dialog_unfollow_warning)
                .setPositiveButton(android.R.string.ok) { _, _ -> viewModel.changeFollowState(accountId) }
                .setNegativeButton(android.R.string.cancel, null)
                .show()
    }

    private fun mention() {
        loadedAccount?.let {
            val intent = ComposeActivity.IntentBuilder()
                    .mentionedUsernames(setOf(it.username))
                    .build(this)
            startActivity(intent)
        }
    }

    override fun onViewTag(tag: String) {
        val intent = Intent(this, ViewTagActivity::class.java)
        intent.putExtra("hashtag", tag)
        startActivity(intent)
    }

    override fun onViewAccount(id: String) {
        val intent = Intent(this, AccountActivity::class.java)
        intent.putExtra("id", id)
        startActivity(intent)
    }

    override fun onViewUrl(url: String) {
        viewUrl(url)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            android.R.id.home -> {
                onBackPressed()
                return true
            }
            R.id.action_mention -> {
                mention()
                return true
            }
            R.id.action_open_in_web -> {
                // If the account isn't loaded yet, eat the input.
                if (loadedAccount != null) {
                    LinkHelper.openLink(loadedAccount?.url, this)
                }
                return true
            }
            R.id.action_follow -> {
                viewModel.changeFollowState(accountId)
                return true
            }
            R.id.action_block -> {
                viewModel.changeBlockState(accountId)
                return true
            }
            R.id.action_mute -> {
                viewModel.changeMuteState(accountId)
                return true
            }

            R.id.action_show_reblogs -> {
                viewModel.changeShowReblogsState(accountId)
                return true
            }
        }
        return super.onOptionsItemSelected(item)
    }

    override fun getActionButton(): FloatingActionButton? {
        return if (!isSelf && !blocking) {
            accountFloatingActionButton
        } else null
    }

    override fun supportFragmentInjector(): AndroidInjector<Fragment> {
        return dispatchingAndroidInjector
    }

    companion object {

        private const val EDIT_ACCOUNT = 1457

        private const val KEY_ACCOUNT_ID = "id"
        private val argbEvaluator = ArgbEvaluator()

        @JvmStatic
        fun getIntent(context: Context, accountId: String): Intent {
            val intent = Intent(context, AccountActivity::class.java)
            intent.putExtra(KEY_ACCOUNT_ID, accountId)
            return intent
        }
    }

}
