import QtQuick 2.0

Item { id: _root
    property alias source:_image.source

    property real minScale  : 1
    property real maxScale  : 4
    property real startScale: 1
    property bool pinching  : false

    onSourceChanged: reset()

    function snapBack() {
        _flickable.contentX = Math.max( 0, Math.min(_flickable.contentWidth  - _flickable.width,  _flickable.contentX) )
        _flickable.contentY = Math.max( 0, Math.min(_flickable.contentHeight - _flickable.height, _flickable.contentY) )
    }

    function rubberClamp() {
        let minX = 0
        let maxX = Math.max(0, _flickable.contentWidth - _flickable.width)
        let minY = 0
        let maxY = Math.max(0, _flickable.contentHeight - _flickable.height)
        let resistance = 0.35

        _flickable.contentX = (_flickable.contentX < minX) ? minX + (_flickable.contentX - minX) * resistance
                            : (_flickable.contentX > maxX) ? maxX + (_flickable.contentX - maxX) * resistance
                            : _flickable.contentX

        _flickable.contentY = (_flickable.contentY < minY) ? minY + (_flickable.contentY - minY) * resistance
                            : (_flickable.contentY > maxY) ? maxY + (_flickable.contentY - maxY) * resistance
                            : _flickable.contentY
    }

    function reset() {
        _scaleTransform.xScale = 1
        _scaleTransform.yScale = 1
        _flickable.contentX = 0
        _flickable.contentY = 0
    }

    Flickable { id: _flickable
        anchors.fill    : parent
        contentWidth    : _image.width  * _scaleTransform.xScale
        contentHeight   : _image.height * _scaleTransform.yScale
        clip            : true
        interactive     : ! pinching

        PinchArea { id: _pinchArea
            anchors.fill   : parent
            pinch.target: _image

            Image { id: _image
                fillMode    : Image.PreserveAspectFit

                transform: Scale { id: _scaleTransform
                    origin.x: 0
                    origin.y: 0
                    xScale  : 1
                    yScale  : 1

                    // smooth zoom feel
                    Behavior on xScale { NumberAnimation { duration: 80; easing.type: Easing.OutQuad } }
                    Behavior on yScale { NumberAnimation { duration: 80; easing.type: Easing.OutQuad } }
                }

                // ✅ bottom-right when not zoomed
                x: (_flickable.contentWidth <= _flickable.width)    ? (_flickable.width  - width  * _scaleTransform.xScale) : -_flickable.contentX
                y: (_flickable.contentHeight <= _flickable.height)  ? (_flickable.height - height * _scaleTransform.yScale) : -_flickable.contentY

                // smooth snap
                Behavior on x { NumberAnimation { duration: 80; easing.type: Easing.OutCubic } }
                Behavior on y { NumberAnimation { duration: 80; easing.type: Easing.OutCubic } }
            }

            onPinchStarted: (pinch) => {
                pinching        = true
                startScale      = _scaleTransform.xScale
            }

            onPinchUpdated: (pinch) => {
                if (Math.abs(pinch.scale - 1) < 0.01) return

                // smooth scale
                let newScale = startScale * pinch.scale
                newScale = Math.max(minScale, Math.min(maxScale, newScale))

                // 🔑 ratio between scales
                let scaleRatio = newScale / _flickable.xScale

                // 🔑 adjust content so zoom follows fingers
                let cx = pinch.center.x
                let cy = pinch.center.y

                _flickable.contentX = (_flickable.contentX + cx) * scaleRatio - cx
                _flickable.contentY = (_flickable.contentY + cy) * scaleRatio - cy

                // apply scale AFTER adjusting content
                _scaleTransform.xScale = newScale
                _scaleTransform.yScale = newScale

                // 🔑 now apply drag (correct direction)
                let dx = pinch.center.x - pinch.previousCenter.x
                let dy = pinch.center.y - pinch.previousCenter.y

                _flickable.contentX -= dx * 0.8
                _flickable.contentY -= dy * 0.8

                rubberClamp()
            }

            onPinchFinished: {
                reset()
                snapBack()
                Qt.callLater(() => pinching = false)
            }
        }
    }
}
