/*
 * Copyright 2021 Jeremy Jamet / Kunzisoft.
 *     
 * This file is part of KeePassDX.
 *
 *  KeePassDX 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.
 *
 *  KeePassDX 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 KeePassDX.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package com.kunzisoft.keepass.view

import android.content.Context
import android.text.InputFilter
import android.text.util.Linkify
import android.util.AttributeSet
import android.util.TypedValue
import android.view.ContextThemeWrapper
import android.view.View
import androidx.annotation.StringRes
import androidx.appcompat.widget.AppCompatImageButton
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.ContextCompat
import androidx.core.text.util.LinkifyCompat
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.model.AppOriginEntryField.APPLICATION_ID_FIELD_NAME
import com.kunzisoft.keepass.utils.AppUtil.openExternalApp


open class TextFieldView @JvmOverloads constructor(context: Context,
                                              attrs: AttributeSet? = null,
                                              defStyle: Int = 0)
    : ProtectedTextFieldView(context, attrs, defStyle) {

    protected var labelViewId = ViewCompat.generateViewId()
    private var valueViewId = ViewCompat.generateViewId()
    private var showButtonId = ViewCompat.generateViewId()
    private var copyButtonId = ViewCompat.generateViewId()

    protected val labelView = AppCompatTextView(context).apply {
        setTextAppearance(context,
            R.style.KeepassDXStyle_TextAppearance_LabelTextStyle)
        layoutParams = LayoutParams(
            LayoutParams.MATCH_PARENT,
            LayoutParams.WRAP_CONTENT
        ).also {
            it.leftMargin = TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,
                4f,
                resources.displayMetrics
            ).toInt()
            it.marginStart = TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,
                4f,
                resources.displayMetrics
            ).toInt()
            
        }
    }
    protected val valueView = AppCompatTextView(context).apply {
        setTextAppearance(context,
            R.style.KeepassDXStyle_TextAppearance_TextNode)
        layoutParams = LayoutParams(
            LayoutParams.MATCH_PARENT,
            LayoutParams.WRAP_CONTENT).also {
            it.topMargin = TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,
                4f,
                resources.displayMetrics
            ).toInt()
            it.leftMargin = TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,
                8f,
                resources.displayMetrics
            ).toInt()
            it.marginStart = TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,
                8f,
                resources.displayMetrics
            ).toInt()
        }
        setTextIsSelectable(true)
    }
    private var showButton = AppCompatImageButton(
        ContextThemeWrapper(context, R.style.KeepassDXStyle_ImageButton_Simple), null, 0).apply {
        layoutParams = LayoutParams(
            LayoutParams.WRAP_CONTENT,
            LayoutParams.WRAP_CONTENT)
        setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_visibility_state))
        contentDescription = context.getString(R.string.menu_showpass)
    }
    private var copyButton = AppCompatImageButton(
        ContextThemeWrapper(context, R.style.KeepassDXStyle_ImageButton_Simple), null, 0).apply {
        layoutParams = LayoutParams(
            LayoutParams.WRAP_CONTENT,
            LayoutParams.WRAP_CONTENT)
        setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_content_copy_white_24dp))
        contentDescription = context.getString(R.string.menu_copy)
    }

    init {
        buildViews()
        addView(copyButton)
        addView(showButton)
        addView(labelView)
        addView(valueView)
    }

    private fun buildViews() {
        copyButton.apply {
            id = copyButtonId
            layoutParams = (layoutParams as LayoutParams?)?.also {
                it.addRule(ALIGN_PARENT_RIGHT)
                it.addRule(ALIGN_PARENT_END)
            }
        }
        showButton.apply {
            id = showButtonId
            layoutParams = (layoutParams as LayoutParams?)?.also {
                if (copyButton.isVisible) {
                    it.addRule(LEFT_OF, copyButtonId)
                    
                    it.addRule(START_OF, copyButtonId)
                    
                } else {
                    it.addRule(ALIGN_PARENT_RIGHT)
                    
                    it.addRule(ALIGN_PARENT_END)
                    
                }
            }
        }
        labelView.apply {
            id = labelViewId
            layoutParams = (layoutParams as LayoutParams?)?.also {
                it.addRule(LEFT_OF, showButtonId)
                it.addRule(START_OF, showButtonId)
            }
        }
        valueView.apply {
            id = valueViewId
            layoutParams = (layoutParams as LayoutParams?)?.also {
                it.addRule(LEFT_OF, showButtonId)
                it.addRule(START_OF, showButtonId)
                it.addRule(BELOW, labelViewId)
            }
        }
    }

    override fun applyFontVisibility(fontInVisibility: Boolean) {
        if (fontInVisibility)
            valueView.applyFontVisibility()
    }

    override var label: String
        get() {
            return labelView.text.toString()
        }
        set(value) {
            labelView.text = value
        }

    open fun setLabel(@StringRes labelId: Int) {
        labelView.setText(labelId)
    }

    override var value: CharArray
        get() {
            val sequence = valueView.text
            val valueChars = CharArray(sequence.length)
            android.text.TextUtils.getChars(sequence, 0, sequence.length, valueChars, 0)
            return valueChars
        }
        set(value) {
            valueView.setCharArray(value)
            changeProtectedValueParameters()
        }

    open fun setValue(@StringRes valueId: Int) {
        value = resources.getString(valueId).toCharArray()
    }

    override var default: CharArray = CharArray(0)

    fun setMaxChars(numberChars: Int) {
        when {
            numberChars <= 0 -> {
                valueView.filters += InputFilter.LengthFilter(MAX_CHARS_LIMIT)
            }
            else -> {
                val chars = if (numberChars > MAX_CHARS_LIMIT) MAX_CHARS_LIMIT else numberChars
                valueView.filters += InputFilter.LengthFilter(chars)
            }
        }
    }

    override fun setProtection(
        protection: Boolean,
        isCurrentlyProtected: Boolean,
        onUnprotectClickListener: OnClickListener?
    ) {
        super.setProtection(protection, isCurrentlyProtected, onUnprotectClickListener)
        showButton.isVisible = isProtected
        if (isProtected) {
            showButton.setOnClickListener {
                onUnprotectClickListener?.onClick(this@TextFieldView)
            }
        }
    }

    override fun changeProtectedValueParameters() {
        val isCurrentlyProtected = isCurrentlyProtected()
        showButton.isSelected = isCurrentlyProtected
        valueView.apply {
            if (showButton.isVisible) {
                applyHiddenStyle(isCurrentlyProtected)
                setCopyButtonState(mButtonState)
            } else {
                linkify()
                isFocusable = true
                setTextIsSelectable(true)
            }
        }
        invalidate()
    }

    private fun linkify() {
        when {
            labelView.text.contains(APPLICATION_ID_FIELD_NAME) -> {
                val packageName = valueView.text.toString()
                // TODO #996 if (UriUtil.isExternalAppInstalled(context, packageName)) {
                    valueView.customLink {
                        context.openExternalApp(packageName)
                    }
                //}
            }
            else -> {
                LinkifyCompat.addLinks(valueView, Linkify.WEB_URLS or Linkify.EMAIL_ADDRESSES)
            }
        }
    }

    fun getCopyButtonView(): View? {
        if (copyButton.isVisible) {
            return copyButton
        }
        return null
    }

    private var mButtonState: ButtonState = ButtonState.DEACTIVATE

    fun setCopyButtonState(buttonState: ButtonState) {
        val isCurrentlyProtected = isCurrentlyProtected()
        mButtonState = buttonState
        when (buttonState) {
            ButtonState.ACTIVATE -> {
                copyButton.apply {
                    visibility = VISIBLE
                    isActivated = false
                }
                valueView.apply {
                    isFocusable = !showButton.isVisible || !isCurrentlyProtected
                    setTextIsSelectable(!showButton.isVisible || !isCurrentlyProtected)
                }
            }
            ButtonState.DEACTIVATE -> {
                copyButton.apply {
                    visibility = VISIBLE
                    // Reverse because isActivated show custom color and allow click
                    isActivated = true
                }
                valueView.apply {
                    isFocusable = false
                    setTextIsSelectable(false)
                }
            }
            ButtonState.GONE -> {
                copyButton.apply {
                    visibility = GONE
                    setOnClickListener(null)
                }
                valueView.apply {
                    isFocusable = false
                    setTextIsSelectable(false)
                }
            }
        }
        invalidate()
    }

    fun setCopyButtonClickListener(onActionClickListener: ((label: String, value: CharArray) -> Unit)?) {
        val clickListener = if (onActionClickListener != null)
            OnClickListener { onActionClickListener.invoke(label, value) }
        else
            null
        setOnActionClickListener(clickListener, null)
    }

    override fun setOnActionClickListener(
        onActionClickListener: OnClickListener?,
        actionImageId: Int?
    ) {
        copyButton.setOnClickListener(onActionClickListener)
        copyButton.isVisible = onActionClickListener != null
        invalidate()
    }

    override var isFieldVisible: Boolean
        get() {
            return isVisible
        }
        set(value) {
            isVisible = value
        }

    override fun invalidate() {
        super.invalidate()
        buildViews()
    }

    enum class ButtonState {
        ACTIVATE, DEACTIVATE, GONE
    }

    companion object {
        const val MAX_CHARS_LIMIT = Integer.MAX_VALUE
    }
}
