/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

import QtQuick      2.3
import QtLocation   5.3

import QGroundControl               1.0
import QGroundControl.ScreenTools   1.0
import QGroundControl.Controls      1.0


/// Polygon drawing item. Add to your control and call methods to get support for polygon drawing and adjustment.
Item {
    id: _root

    // These properties must be provided by the consumer
    property var    map            ///< Map control
    property var    callbackObject ///< Callback item

    // These properties can be queried by the consumer
    property bool   drawingPolygon:     false
    property bool   adjustingPolygon:   false
    property bool   polygonReady:       _currentPolygon ? _currentPolygon.path.length > 2 : false   ///< true: enough points have been captured to create a closed polygon

    property var    _helpLabel                                  ///< Dynamically added help label component
    property var    _newPolygon                                 ///< Dynamically added polygon which represents all polygon points including the one currently being drawn
    property var    _currentPolygon                             ///< Dynamically added polygon which represents the currently completed polygon
    property var    _nextPointLine                              ///< Dynamically added line which goes from last polygon point to the new one being drawn
    property var    _mobileSegment                              ///< Dynamically added line between first and second polygon point for mobile
    property var    _mobilePoint                                ///< Dynamically added point showing first polygon point on mobile
    property var    _mouseArea                                  ///< Dynamically added MouseArea which handles all clicking and mouse movement
    property var    _vertexDragList:    [ ]                     ///< Dynamically added vertex drag points
    property bool   _mobile:            ScreenTools.isMobile

    /// Begin capturing a new polygon
    ///     polygonCaptureStarted will be signalled through callbackObject
    function startCapturePolygon() {
        _helpLabel =        helpLabelComponent.createObject     (map)
        _newPolygon =       newPolygonComponent.createObject    (map)
        _currentPolygon =   currentPolygonComponent.createObject(map)
        _nextPointLine =    nextPointComponent.createObject     (map)
        _mobileSegment =    mobileSegmentComponent.createObject (map)
        _mobilePoint =      mobilePointComponent.createObject   (map)
        _mouseArea =        mouseAreaComponent.createObject     (map)

        map.addMapItem(_newPolygon)
        map.addMapItem(_currentPolygon)
        map.addMapItem(_nextPointLine)
        map.addMapItem(_mobileSegment)
        map.addMapItem(_mobilePoint)

        drawingPolygon = true
        callbackObject.polygonCaptureStarted()
    }

    /// Finish capturing the polygon
    ///     polygonCaptureFinished will be signalled through callbackObject
    /// @return true: polygon completed, false: not enough points to complete polygon
    function finishCapturePolygon() {
        if (!polygonReady) {
            return false
        }
        var polygonPath = _currentPolygon.path
        _cancelCapturePolygon()
        callbackObject.polygonCaptureFinished(polygonPath)
        return true
    }

    function startAdjustPolygon(vertexCoordinates) {
        adjustingPolygon = true
        for (var i=0; i<vertexCoordinates.length; i++) {
            var dragItem = Qt.createQmlObject(
                        "import QtQuick                     2.3; " +
                        "import QtLocation                  5.3; " +
                        "import QGroundControl.ScreenTools  1.0; " +
                        "" +
                        "Rectangle {" +
                        "   id:     vertexDrag; " +
                        "   width:  _sideLength + _expandMargin; " +
                        "   height: _sideLength + _expandMargin; " +
                        "   color:  'red'; " +
                        "" +
                        "   property var coordinate; " +
                        "   property int index; " +
                        "" +
                        "   readonly property real _sideLength:     ScreenTools.defaultFontPixelWidth * 2; " +
                        "   readonly property real _halfSideLength: _sideLength / 2; " +
                        "" +
                        "   property real _expandMargin: ScreenTools.isMobile ? ScreenTools.defaultFontPixelWidth : 0;" +
                        "" +
                        "   Drag.active:    dragMouseArea.drag.active; " +
                        "" +
                        "   onXChanged: updateCoordinate(); " +
                        "   onYChanged: updateCoordinate(); " +
                        "" +
                        "   function updateCoordinate() { " +
                        "       vertexDrag.coordinate = map.toCoordinate(Qt.point(vertexDrag.x + _expandMargin + _halfSideLength, vertexDrag.y + _expandMargin + _halfSideLength), false); " +
                        "       callbackObject.polygonAdjustVertex(vertexDrag.index, vertexDrag.coordinate); " +
                        "   } " +
                        "" +
                        "   function updatePosition() { " +
                        "       var vertexPoint = map.fromCoordinate(coordinate, false); " +
                        "       vertexDrag.x = vertexPoint.x - _expandMargin - _halfSideLength; " +
                        "       vertexDrag.y = vertexPoint.y - _expandMargin - _halfSideLength; " +
                        "   } " +
                        "" +
                        "   Connections { " +
                        "       target:             map; " +
                        "       onCenterChanged:    updatePosition(); " +
                        "       onZoomLevelChanged: updatePosition(); " +
                        "   } " +
                        "" +
                        "   MouseArea { " +
                        "       id:             dragMouseArea; " +
                        "       anchors.fill:   parent; " +
                        "       drag.target:    parent; " +
                        "       drag.minimumX:  0; " +
                        "       drag.minimumY:  0; " +
                        "       drag.maximumX:  map.width - parent.width; " +
                        "       drag.maximumY:  map.height - parent.height; " +
                        "   } " +
                        "} ",
                        map)
            dragItem.z = QGroundControl.zOrderMapItems + 1
            dragItem.coordinate = vertexCoordinates[i]
            dragItem.index = i
            dragItem.updatePosition()
            _vertexDragList.push(dragItem)
            callbackObject.polygonAdjustStarted()
        }
    }

    function finishAdjustPolygon() {
        _cancelAdjustPolygon()
        callbackObject.polygonAdjustFinished()
    }

    /// Cancels an in progress draw or adjust
    function cancelPolygonEdit() {
        _cancelAdjustPolygon()
        _cancelCapturePolygon()
    }

    function _cancelAdjustPolygon() {
        adjustingPolygon = false
        for (var i=0; i<_vertexDragList.length; i++) {
            _vertexDragList[i].destroy()
        }
        _vertexDragList = []
    }

    function _cancelCapturePolygon() {
        _helpLabel.destroy()
        _newPolygon.destroy()
        _currentPolygon.destroy()
        _nextPointLine.destroy()
        _mouseArea.destroy()
        drawingPolygon = false
    }

    Component {
        id: helpLabelComponent

        QGCMapLabel {
            id:                     polygonHelp
            anchors.topMargin:      parent.height - ScreenTools.availableHeight
            anchors.top:            parent.top
            anchors.left:           parent.left
            anchors.right:          parent.right
            horizontalAlignment:    Text.AlignHCenter
            map:                    _root.map
            text:                   qsTr("Click to add point %1").arg(ScreenTools.isMobile || !polygonReady ? "" : qsTr("- Right Click to end polygon"))

            Connections {
                target: _root

                onDrawingPolygonChanged: {
                    if (drawingPolygon) {
                        polygonHelp.text = qsTr("Click to add point")
                    }
                    polygonHelp.visible = drawingPolygon
                }

                onPolygonReadyChanged: {
                    if (polygonReady && !ScreenTools.isMobile) {
                        polygonHelp.text = qsTr("Click to add point - Right Click to end polygon")
                    }
                }

                onAdjustingPolygonChanged: {
                    if (adjustingPolygon) {
                        polygonHelp.text = qsTr("Adjust polygon by dragging corners")
                    }
                    polygonHelp.visible = adjustingPolygon
                }
            }
        }
    }

    Component {
        id: mouseAreaComponent

        MouseArea {
            anchors.fill:       map
            acceptedButtons:    Qt.LeftButton | Qt.RightButton
            hoverEnabled:       true
            z:                  QGroundControl.zOrderMapItems + 1

            property bool   justClicked: false

            onClicked: {
                if (mouse.button == Qt.LeftButton) {
                    justClicked = true
                    if (_newPolygon.path.length > 2) {
                        // Make sure the new line doesn't intersect the existing polygon
                        var lastSegment = _newPolygon.path.length - 2
                        var newLineA = map.fromCoordinate(_newPolygon.path[lastSegment], false /* clipToViewPort */)
                        var newLineB = map.fromCoordinate(_newPolygon.path[lastSegment+1], false /* clipToViewPort */)
                        for (var i=0; i<lastSegment; i++) {
                            var oldLineA = map.fromCoordinate(_newPolygon.path[i], false /* clipToViewPort */)
                            var oldLineB = map.fromCoordinate(_newPolygon.path[i+1], false /* clipToViewPort */)
                            if (QGroundControl.linesIntersect(newLineA, newLineB, oldLineA, oldLineB)) {
                                return;
                            }
                        }
                    }

                    var clickCoordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */)
                    var polygonPath = _newPolygon.path
                    if (polygonPath.length === 0) {
                        // Add first coordinate
                        polygonPath.push(clickCoordinate)
                    } else {
                        // Add subsequent coordinate
                        if (ScreenTools.isMobile) {
                            // Since mobile has no mouse, the onPositionChangedHandler will not fire. We have to add the coordinate
                            // here instead.
                            justClicked = false
                            polygonPath.push(clickCoordinate)
                        } else {
                            // The onPositionChanged handler for mouse movement will have already added the coordinate to the array.
                            // Just update it to the final position
                            polygonPath[_newPolygon.path.length - 1] = clickCoordinate
                        }
                    }
                    _currentPolygon.path = polygonPath
                    _newPolygon.path = polygonPath

                    if (_mobile && _currentPolygon.path.length === 1) {
                        _mobilePoint.coordinate = _currentPolygon.path[0]
                        _mobilePoint.visible = true
                    } else if (_mobile && _currentPolygon.path.length === 2) {
                        // Show initial line segment on mobile
                        _mobileSegment.path = [ _currentPolygon.path[0], _currentPolygon.path[1] ]
                        _mobileSegment.visible = true
                        _mobilePoint.visible = false
                    } else {
                        _mobileSegment.visible = false
                        _mobilePoint.visible = false
                    }
                } else if (polygonReady) {
                    finishCapturePolygon()
                }
            }

            onPositionChanged: {
                if (ScreenTools.isMobile) {
                    // We don't track mouse drag on mobile
                    return
                }
                if (_newPolygon.path.length) {
                    var dragCoordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */)
                    var polygonPath = _newPolygon.path
                    if (justClicked){
                        // Add new drag coordinate
                        polygonPath.push(dragCoordinate)
                        justClicked = false
                    }

                    // Update drag line
                    _nextPointLine.path = [ _newPolygon.path[_newPolygon.path.length - 2], dragCoordinate ]

                    polygonPath[_newPolygon.path.length - 1] = dragCoordinate
                    _newPolygon.path = polygonPath
                }
            }
        }
    }

    /// Polygon being drawn, including new point
    Component {
        id: newPolygonComponent

        MapPolygon {
            color:      "blue"
            opacity:    0.5
            visible:    path.length > 2
        }
    }

    /// Current complete polygon
    Component {
        id: currentPolygonComponent

        MapPolygon {
            color:      'green'
            opacity:    0.5
            visible:    polygonReady
        }
    }

    /// First line segment to show on mobile
    Component {
        id: mobileSegmentComponent

        MapPolyline {
            line.color: "green"
            line.width: 3
            visible:    false
        }
    }

    /// First line segment to show on mobile
    Component {
        id: mobilePointComponent

        MapQuickItem {
            anchorPoint.x:  rect.width / 2
            anchorPoint.y:  rect.height / 2
            visible:        false

            sourceItem: Rectangle {
                id:     rect
                width:  ScreenTools.defaultFontPixelHeight
                height: width
                color:  "green"
            }
        }
    }

    /// Next line for polygon
    Component {
        id: nextPointComponent

        MapPolyline {
            line.color: "green"
            line.width: 3
        }
    }
}
