import QtQuick 2.7

import Common.Styles 1.0
import Utils 1.0

// ===================================================================
//
// Paned is one container divided in two areas.
// The division between the areas can be modified with a handle.
//
// Each area can have a fixed or dynamic minimum width.
// See `minimumLeftLimit` and `minimumRightLimit` attributes.
//
// Note: Paned supports too `maximumLeftLimit` and
// `maximumRightLimit`.
//
// To create a dynamic minimum width, it's necessary to give
// a percentage to `minmimumLeftLimit` or `minimumRightLimit` like:
// `minimumLeftLimit: '66%'`.
// The percentage is relative to the container width.
//
// ===================================================================

Item {
  id: container

  property alias childA: contentA.data
  property alias childB: contentB.data
  property bool defaultClosed: false
  property bool resizeAInPriority: false
  property int closingEdge: Qt.LeftEdge // `LeftEdge` or `RightEdge`.
  property int defaultChildAWidth

  // User limits: string or int values.
  // By default: no limits.
  property var maximumLeftLimit
  property var maximumRightLimit
  property var minimumLeftLimit: 0
  property var minimumRightLimit: 0

  property bool _isClosed
  property int _savedContentAWidth

  // Internal limits.
  property var _maximumLeftLimit
  property var _maximumRightLimit
  property var _minimumLeftLimit
  property var _minimumRightLimit

  // -----------------------------------------------------------------

  function _getLimitValue (limit) {
    if (limit == null) {
      return
    }

    return limit.isDynamic
      ? width * limit.value
      : limit.value
  }

  function _parseLimit (limit) {
    if (limit == null) {
      return
    }

    if (Utils.isString(limit)) {
      var arr = limit.split('%')

      if (arr[1] === '') {
        return {
          isDynamic: true,
          value: +arr[0] / 100
        }
      }
    }

    return {
      value: limit
    }
  }

  function _applyLimits () {
    var maximumLeftLimit = _getLimitValue(_maximumLeftLimit)
    var maximumRightLimit = _getLimitValue(_maximumRightLimit)
    var minimumLeftLimit = _getLimitValue(_minimumLeftLimit)
    var minimumRightLimit = _getLimitValue(_minimumRightLimit)

    var theoreticalBWidth = container.width - contentA.width - handle.width

    // If closed, set correctly the handle position to left or right.
    if (_isClosed) {
      contentA.width = (closingEdge !== Qt.LeftEdge)
        ? container.width - handle.width
        : 0
    }
    // width(A) < minimum width(A).
    else if (contentA.width < minimumLeftLimit) {
      contentA.width = minimumLeftLimit
    }
    // width(A) > maximum width(A).
    else if (maximumLeftLimit != null && contentA.width > maximumLeftLimit) {
      contentA.width = maximumLeftLimit
    }
    // width(B) < minimum width(B).
    else if (theoreticalBWidth < minimumRightLimit) {
      contentA.width = container.width - handle.width - minimumRightLimit
    }
    // width(B) > maximum width(B).
    else if (maximumRightLimit != null && theoreticalBWidth > maximumRightLimit) {
      contentA.width = container.width - handle.width - maximumRightLimit
    } else if (resizeAInPriority) {
      contentA.width = container.width - handle.width - contentB.width
    }
  }

  // -----------------------------------------------------------------

  function _applyLimitsOnUserMove (offset) {
    var minimumRightLimit = _getLimitValue(_minimumRightLimit)
    var minimumLeftLimit = _getLimitValue(_minimumLeftLimit)

    // One area is closed.
    if (_isClosed) {
      if (closingEdge === Qt.LeftEdge) {
        if (offset > minimumLeftLimit / 2) {
          _open()
        }
      } else {
        if (-offset > minimumRightLimit / 2) {
          _open()
        }
      }

      return
    }

    // Check limits.
    var maximumLeftLimit = _getLimitValue(_maximumLeftLimit)
    var maximumRightLimit = _getLimitValue(_maximumRightLimit)

    var theoreticalBWidth = container.width - offset - contentA.width - handle.width

    // width(A) < minimum width(A).
    if (contentA.width + offset < minimumLeftLimit) {
      contentA.width = minimumLeftLimit

      if (closingEdge === Qt.LeftEdge && -offset > minimumLeftLimit / 2) {
        if (_isClosed) {
          _open()
        } else {
          _close()
        }
      }
    }
    // width(A) > maximum width(A).
    else if (maximumLeftLimit != null && contentA.width + offset > maximumLeftLimit) {
      contentA.width = maximumLeftLimit
    }
    // width(B) < minimum width(B).
    else if (theoreticalBWidth < minimumRightLimit) {
      contentA.width = container.width - handle.width - minimumRightLimit

      if (closingEdge !== Qt.LeftEdge && offset > minimumRightLimit / 2) {
        if (_isClosed) {
          _open()
        } else {
          _close()
        }
      }
    }
    // width(B) > maximum width(B).
    else if (maximumRightLimit != null && theoreticalBWidth > maximumRightLimit) {
      contentA.width = container.width - handle.width - maximumRightLimit
    }
    // Resize A/B.
    else {
      contentA.width = contentA.width + offset
    }
  }

  // -----------------------------------------------------------------

  function _open () {
    _isClosed = false
    _applyLimits()
  }

  function _close () {
    _isClosed = true
    _savedContentAWidth = contentA.width
    closingTransition.running = true
  }

  function _inverseClosingState () {
    if (!_isClosed) {
      // Save state and close.
      _close()
    } else {
      // Restore old state.
      openingTransition.running = true
    }
  }

  function _isVisible (edge) {
    return (
      !_isClosed ||
      openingTransition.running ||
      closingTransition.running
    ) || closingEdge !== edge
  }

  // -----------------------------------------------------------------

  onWidthChanged: _applyLimits()

  Component.onCompleted: {
    // Unable to modify this properties after creation.
    // It's a desired choice.
    _maximumLeftLimit = _parseLimit(maximumLeftLimit)
    _maximumRightLimit = _parseLimit(maximumRightLimit)
    _minimumLeftLimit = _parseLimit(minimumLeftLimit)
    _minimumRightLimit = _parseLimit(minimumRightLimit)

    contentA.width = (defaultChildAWidth == null)
      ? _getLimitValue(_minimumLeftLimit)
      : defaultChildAWidth

    _isClosed = defaultClosed
    _savedContentAWidth = contentA.width
  }

  Item {
    id: contentA

    height: parent.height
    visible: _isVisible(Qt.LeftEdge)
  }

  MouseArea {
    id: handle

    property int _mouseStart

    anchors.left: contentA.right
    cursorShape: Qt.SplitHCursor
    height: parent.height
    hoverEnabled: true
    width: PanedStyle.handle.width

    onDoubleClicked: _inverseClosingState()
    onMouseXChanged: pressed &&
      _applyLimitsOnUserMove(mouseX - _mouseStart)
    onPressed: _mouseStart = mouseX

    Rectangle {
      anchors.fill: parent
      color: parent.pressed
        ? PanedStyle.handle.color.pressed
        : (parent.containsMouse
           ? PanedStyle.handle.color.hovered
           : PanedStyle.handle.color.normal
          )
    }
  }

  Item {
    id: contentB

    anchors.left: handle.right
    height: parent.height
    visible: _isVisible(Qt.RightEdge)
    width: container.width - contentA.width - handle.width
  }

  PropertyAnimation {
    id: openingTransition

    duration: PanedStyle.transitionDuration
    property: 'width'
    target: contentA
    to: _savedContentAWidth

    onRunningChanged: !running && _open()
  }

  PropertyAnimation {
    id: closingTransition

    duration: PanedStyle.transitionDuration
    property: 'width'
    target: contentA
    to: closingEdge === Qt.LeftEdge ? 0 : container.width - handle.width
  }
}