/*
    blueROCK - for digital rock
    Copyright (C) 2019  Dorian Zedler

    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 <http://www.gnu.org/licenses/>.
*/

import QtQuick 2.9
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.12
import QtGraphicalEffects 1.0

Item {
    id: control

    state: "idle"

    property var target                         // targeted ListView

    property bool autoConfigureTarget: true     // should the target be automaticaly be configured?

    property int postRefreshDelay: 1000         // delay after reload funcion has finished
    property int preRefreshDelay: 1000          // delay before reload funcion is called

    property int refreshPosition: height * 1.7  // position of the item when refreshing
    property int dragOutPosition: height * 1.8  // maximum drag out

    property double dragRefreshPositionMultiplier: 0.6 // position of the item when starting to refresh

    readonly property double dragProgress: Math.min( userPosition / dragOutPosition, 1)

    property Component background: Item {
        RectangularGlow {
            anchors.fill: backgroundRe

            scale: 0.8 * backgroundRe.scale
            cornerRadius: backgroundRe.radius
            color: "black"

            glowRadius: 0.001
            spread: 0.2
        }

        Rectangle {
            id: backgroundRe

            anchors.fill: parent

            radius: width * 0.5
            color: Material.dialogColor
        }
    }
    property Component busyIndicator: BusyIndicator { running: true }
    property Component pullIndicator: Canvas {

        property double drawProgress: control.dragProgress

        rotation: drawProgress > control.dragRefreshPositionMultiplier ? 180:0

        onDrawProgressChanged: {
            requestPaint()
        }

        onPaint: {
            var ctx = getContext("2d");

            var topMargin = height * 0.1
            var bottomMargin = topMargin
            var rightMargin = 0
            var leftMargin = 0

            var arrowHeight = height - topMargin - bottomMargin

            var peakHeight = arrowHeight * 0.35
            var peakWidth = peakHeight

            var lineWidth = 2

            var progress = drawProgress * 1 / control.dragRefreshPositionMultiplier > 1 ? 1 : drawProgress * 1 / control.dragRefreshPositionMultiplier
            // modify all values to math the progress

            arrowHeight = arrowHeight * progress
            if(progress > 0.3){
                peakHeight = peakHeight * (progress - 0.3) * 1/0.7
                peakWidth = peakWidth * (progress - 0.3) * 1/0.7
            }
            else {
                peakHeight = 0
                peakWidth = 0
            }

            // clear canvas
            ctx.reset()

            ctx.lineWidth = lineWidth;
            ctx.strokeStyle = control.Material.foreground;

            // middle line
            ctx.moveTo(width/2, topMargin);
            ctx.lineTo(width/2, arrowHeight + topMargin);

            // right line
            ctx.moveTo(width/2 - lineWidth * 0.3, arrowHeight + topMargin);
            ctx.lineTo(width/2 + peakWidth,arrowHeight + topMargin - peakHeight);
            // left line
            ctx.moveTo(width/2 + lineWidth * 0.3, arrowHeight + topMargin);
            ctx.lineTo(width/2 - peakWidth,arrowHeight + topMargin - peakHeight);

            ctx.stroke();
        }

        Behavior on rotation {
            NumberAnimation {
                duration: 100
            }
        }

    }

    signal refreshRequested

    // internal properties
    property int minimumPosition: 0
    property int maximumPosition: 0
    property int userPosition: 0
    property int position: Math.max( minimumPosition, Math.min(maximumPosition, userPosition))

    height: 50
    width: height

    Component.onCompleted: {
        if(control.autoConfigureTarget){
            target.boundsBehavior = Flickable.DragOverBounds
            target.boundsMovement = Flickable.StopAtBounds
        }
    }

    function refresh() {
        control.refreshRequested()
        postRefreshTimer.start()
    }

    anchors {
        top: control.target.top
        horizontalCenter: control.target.horizontalCenter
        topMargin: control.position - height
    }

    Connections {
        target: control.target
        function onDragEnded() {
            if(userPosition >= control.dragOutPosition * control.dragRefreshPositionMultiplier){
                control.state = "refreshing"
                preRefreshTimer.start()
            }
        }
    }

    Loader {
        id: backgroundLd

        anchors.fill: parent

        sourceComponent: control.background
    }

    Loader {
        id: pullIndicatorLd

        anchors.centerIn: parent

        height: parent.height * 0.6
        width: height

        rotation: 180

        sourceComponent: control.pullIndicator
    }

    Loader {
        id: busyIndicatorLd

        anchors.centerIn: parent

        height: parent.height * 0.7
        width: height

        opacity: 0

        sourceComponent: control.busyIndicator
    }

    Timer {
        id: preRefreshTimer
        interval: control.preRefreshDelay <= 0 ? 1:control.preRefreshDelay
        running: false
        repeat: false
        onTriggered: {
            control.refresh()
        }
    }

    Timer {
        id: postRefreshTimer
        interval: control.postRefreshDelay <= 0 ? 1:control.postRefreshDelay
        running: false
        repeat: false
        onTriggered: {
            control.state = "hidden"
        }
    }

    Behavior on minimumPosition {
        enabled: !control.target.dragging && state !== "idle"
        NumberAnimation {
            duration: 100
        }
    }

    states: [
        State {
            name: "idle"

            PropertyChanges {
                target: control
                minimumPosition: userPosition > maximumPosition ? maximumPosition:userPosition
                userPosition: -1 / (Math.abs( target.verticalOvershoot * 0.001 + 0.003 ) + 1 / control.dragOutPosition * 0.001) + control.dragOutPosition // Math.abs( target.verticalOvershoot )
                maximumPosition: control.dragOutPosition
            }

            PropertyChanges {
                target: pullIndicatorLd
                rotation: 0
            }
        },
        State {
            name: "refreshing"
            PropertyChanges {
                target: control
                minimumPosition: control.refreshPosition
                userPosition: 0
                maximumPosition: control.refreshPosition

            }

            PropertyChanges {
                target: pullIndicatorLd
                opacity: 0
            }

            PropertyChanges {
                target: busyIndicatorLd
                opacity: 1
            }
        },
        State {
            name: "hidden"
            PropertyChanges {
                target: control
                minimumPosition: control.refreshPosition
                userPosition: 0
                maximumPosition: control.refreshPosition
                scale: 0
            }

            PropertyChanges {
                target: pullIndicatorLd
                opacity: 0
            }

            PropertyChanges {
                target: busyIndicatorLd
                opacity: 1
            }
        }
    ]

    transitions: [
        Transition {
            NumberAnimation {
                duration: 100
                properties: "rotation, opacity"
            }
        },

        Transition {
            from: "refreshing"
            to: "hidden"

            PauseAnimation {
                duration: 200
            }

            NumberAnimation {
                duration: 200
                properties: "scale"
            }

            onRunningChanged: {
                if(control.state === "hidden" && !running){
                    control.state = "idle"
                }
            }
        },

        Transition {
            from: "hidden"
            to: "idle"
        }

    ]

}
