package oppen.filters.ui

/*

    Lätt - fast image filtering app for Android
    Copyright (C) 2020  Öppenlab

    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.

    This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.

 */

import android.content.res.Resources
import android.util.DisplayMetrics
import android.view.View
import androidx.core.view.children
import androidx.core.view.isVisible
import androidx.preference.PreferenceManager
import kotlinx.android.synthetic.main.levels.view.*
import oppen.filters.*
import oppen.filters.io.Levels
import oppen.filters.ui.floating.FloatingTouchListener


/**
 *
 * Encapsulates all filter ui, default preferences for showing/hiding different controls should be set in res/xml/app_preferences.xml
 * Supplied default values when reading prefs here should ∴ be redundant
 * The app doesn't have a pipeline (yet/never?), everything is static, so this UI class just mutates static values in Levels.kt
 *
 */
class LevelsWindow(val view: View, val onChange: () -> Unit) {

    var isFloating = true
    var isDocked = true
    var hasVisibleControls = true
    var floatingTouchListener = FloatingTouchListener()

    init {

        view.setOnTouchListener(floatingTouchListener)

        //BRIGHTNESS
        val brightnessMax = 1.0f
        val brightnessMin = -1.0f
        view.brightness_slider.progress = valueToPercent(Levels.brightness, brightnessMin, brightnessMax)
        view.brightness_slider.setOnSeekBarChangeListener(SeekListener { value ->
            Levels.brightness = percentToValue(value, brightnessMin, brightnessMax)
            if(!view.brightness_toggle.isChecked) view.brightness_toggle.isChecked = true
            sliderLog("Brightness: $value")
            onChange()
        })

        view.brightness_toggle.setOnCheckedChangeListener { _, isChecked ->
            Levels.applyBrightness = isChecked
            onChange()
        }

        //CONTRAST
        val contrastMax = 1.0f
        val contrastMin = -1.0f
        view.contrast_slider.progress = valueToPercent(Levels.contrast, contrastMin, contrastMax)
        view.contrast_slider.setOnSeekBarChangeListener(SeekListener { value ->
            Levels.contrast = percentToValue(value, contrastMin, contrastMax) * 100
            if(!view.contrast_toggle.isChecked) view.contrast_toggle.isChecked = true
            sliderLog("Contrast: ${Levels.contrast.toInt()}")
            onChange()
        })

        view.contrast_toggle.setOnCheckedChangeListener { _, isChecked ->
            Levels.applyContrast = isChecked
            onChange()
        }

        //SHADOWS
        val shadowMax = 256f
        val shadowMin = -800f
        view.shadows_slider.progress = valueToPercent(Levels.shadows, shadowMin, shadowMax)
        view.shadows_slider.setOnSeekBarChangeListener(SeekListener { value ->
            Levels.shadows = percentToValue(value, shadowMin, shadowMax)
            if(!view.shadows_toggle.isChecked) view.shadows_toggle.isChecked = true
            sliderLog("Shadows: $value")
            onChange()
        })

        view.shadows_toggle.setOnCheckedChangeListener { _, isChecked ->
            Levels.applyShadows = isChecked
            onChange()
        }

        //SATURATION
        val saturationMax = 1.5f
        val saturationMin = 0f
        view.saturation_slider.progress = valueToPercent(Levels.saturation, saturationMin, saturationMax)
        view.saturation_slider.setOnSeekBarChangeListener(SeekListener { value ->
            Levels.saturation = percentToValue(value, saturationMin, saturationMax)
            if(!view.saturation_toggle.isChecked) view.saturation_toggle.isChecked = true
            sliderLog("Saturation: ${"%.2f".format(Levels.saturation)}")
            onChange()
        })

        view.saturation_toggle.setOnCheckedChangeListener { _, isChecked ->
            Levels.applySaturation = isChecked
            onChange()
        }

        //EXPOSURE
        val exposureMax = Levels.exposureMax
        val exposureMin = Levels.exposureMin
        view.exposure_slider.progress = valueToPercent(Levels.exposure, exposureMin, exposureMax)
        view.exposure_slider.setOnSeekBarChangeListener(SeekListener { value ->
            Levels.exposure = percentToValue(value, exposureMin, exposureMax)
            if(!view.exposure_toggle.isChecked) view.exposure_toggle.isChecked = true
            sliderLog("Exposure: $value")
            onChange()
        })

        view.exposure_toggle.setOnCheckedChangeListener { _, isChecked ->
            Levels.applyExposure = isChecked
            onChange()
        }

        //GRAIN
        view.grain_slider.progress = (Levels.grain * 100).toInt()
        view.grain_slider.setOnSeekBarChangeListener(SeekListener { value ->
            if(value == 0){
                view.grain_toggle.isChecked = false
                return@SeekListener
            }
            Levels.grain = value / 100f
            if(!view.grain_toggle.isChecked) view.grain_toggle.isChecked = true
            sliderLog("Grain: $value")
            onChange()
        })

        view.grain_toggle.setOnCheckedChangeListener { _, isChecked ->
            Levels.applyGrain = isChecked
            onChange()
        }

        //VIGNETTE
        val vignetteMax = 1.0f
        val vignetteMin = 0.0f
        view.vignette_slider.progress = valueToPercent(Levels.vignette, vignetteMin, vignetteMax)
        view.vignette_slider.setOnSeekBarChangeListener(SeekListener { value ->
            if(value == 0){
                view.vignette_toggle.isChecked = false
                return@SeekListener
            }
            Levels.vignette = value / 100f
            if(!view.vignette_toggle.isChecked) view.vignette_toggle.isChecked = true
            sliderLog("Vignette: ${Levels.vignette}")
            onChange()
        })

        view.vignette_toggle.setOnCheckedChangeListener { _, isChecked ->
            Levels.applyVignette = isChecked
            onChange()
        }
    }

    private fun sliderLog(message: String){
        view.levels_label.text = message
    }

    fun show() {
        applyVisibilityPreferences()
        view.transitionIn()
    }

    private fun applyVisibilityPreferences(){
        val prefs = PreferenceManager.getDefaultSharedPreferences(view.context)

        val showBrightness = prefs.getBoolean("show_control_brightness", true)
        view.brightness.visible(showBrightness)

        val showShadows = prefs.getBoolean("show_control_shadows", true)
        view.shadows.visible(showShadows)

        val showSaturation = prefs.getBoolean("show_control_saturation", true)
        view.saturation.visible(showSaturation)

        val showExposure = prefs.getBoolean("show_control_exposure", true)
        view.exposure.visible(showExposure)

        val showContrast = prefs.getBoolean("show_control_contrast", true)
        view.contrast.visible(showContrast)

        val showGrain = prefs.getBoolean("show_control_grain", true)
        view.grain.visible(showGrain)

        val showVignette = prefs.getBoolean("show_control_vignette", true)
        view.vignette.visible(showVignette)

        hasVisibleControls = false
        view.controls_layout.children.iterator().forEach { view ->
            if(view.isVisible) hasVisibleControls = true
        }

        val isFloating = prefs.getBoolean("isControlsFloating", true)
        updateMode(isFloating)
        sliderLog("")
    }

    private fun updateMode(isFloating: Boolean){
        val screenWidth = Resources.getSystem().displayMetrics.widthPixels
        val screenHeight = Resources.getSystem().displayMetrics.heightPixels

        //If changing from docked to floating - make sure window is entirely on-screen
        if(isFloating && !this.isFloating){
            view.animate()
                .x(screenWidth.toFloat() - view.width)
                .y((screenHeight - view.height) / 2f)
                .setDuration(100)
                .start()
        }
        this.isFloating = isFloating

        when {
            isFloating -> {
                if(hasVisibleControls){
                    view.no_controls_label.hide()
                }else{
                    view.no_controls_label.show()
                }
                view.docked_expand_button.hide()
                view.levels_label.show()
                view.setOnClickListener(null)
                view.setOnTouchListener(floatingTouchListener)
                view.float_icon.show()
                delay(5000){
                    view.float_icon.fadeOutRetainSpace(500)
                }
            }
            else -> {


                val peekWidth = convertDpToPixel(60f)

                view.setOnTouchListener(null)
                view.docked_expand_button.setImageResource(R.drawable.vector_show)
                view.docked_expand_button.show()
                view.float_icon.hide()
                if(hasVisibleControls){
                    view.no_controls_label.hide()
                }else{
                    view.no_controls_label.invisible()
                }

                view.docked_expand_button.setOnClickListener {
                    if(isDocked) {
                        view.docked_expand_button.setImageResource(R.drawable.vector_hide)
                        view.levels_label.show()
                        if(!hasVisibleControls) view.no_controls_label.show()
                        isDocked = false
                        view.animate()
                            .x(screenWidth.toFloat() - view.width)
                            .y((screenHeight - view.height) / 2f)
                            .setDuration(100)
                            .start()
                    }else{
                        view.docked_expand_button.setImageResource(R.drawable.vector_show)
                        view.levels_label.hide()
                        if(!hasVisibleControls) view.no_controls_label.invisible()
                        isDocked = true
                        view.animate()
                            .x(screenWidth - peekWidth)
                            .y((screenHeight - view.height)/2f)
                            .setDuration(200)
                            .start()
                    }
                }

                view.animate()
                    .x(screenWidth - peekWidth)
                    .y((screenHeight - view.height)/2f)
                    .setDuration(0)
                    .start()

                isDocked = true
            }
        }

    }

    fun hide() = view.fadeOut(250L)

    fun isVisible(): Boolean{
        return view.isVisible
    }

    private fun convertDpToPixel(dp: Float): Float {
        return dp * (Resources.getSystem().displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)
    }
}