All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.androidx.constraintlayout.core.widgets.ConstraintWidget.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.constraintlayout.core.widgets

import androidx.constraintlayout.core.Cache
import androidx.constraintlayout.core.LinearSystem
import androidx.constraintlayout.core.SolverVariable
import androidx.constraintlayout.core.state.WidgetFrame
import androidx.constraintlayout.core.widgets.analyzer.ChainRun
import androidx.constraintlayout.core.widgets.analyzer.HorizontalWidgetRun
import androidx.constraintlayout.core.widgets.analyzer.VerticalWidgetRun
import androidx.constraintlayout.core.widgets.analyzer.WidgetRun
import androidx.constraintlayout.utility.Utils.radians
import kotlin.jvm.JvmField
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

/**
 * Implements a constraint Widget model supporting constraints relations between other widgets.
 *
 *
 * The widget has various anchors (i.e. Left, Top, Right, Bottom, representing their respective
 * sides, as well as Baseline, Center_X and Center_Y). Connecting anchors from one widget to another
 * represents a constraint relation between the two anchors; the [LinearSystem] will then
 * be able to use this model to try to minimize the distances between connected anchors.
 *
 *
 *
 * If opposite anchors are connected (e.g. Left and Right anchors), if they have the same strength,
 * the widget will be equally pulled toward their respective target anchor positions; if the widget
 * has a fixed size, this means that the widget will be centered between the two target anchors. If
 * the widget's size is allowed to adjust, the size of the widget will change to be as large as
 * necessary so that the widget's anchors and the target anchors' distances are zero.
 *
 * Constraints are set by connecting a widget's anchor to another via the
 * [.connect] function.
 */
open class ConstraintWidget {
    ////////////////////////////////////////////////////////////////////////////////////////////////
    // Graph measurements
    ////////////////////////////////////////////////////////////////////////////////////////////////
    var measured = false
    var run = arrayOfNulls(2)
    var horizontalChainRun: ChainRun? = null
    var verticalChainRun: ChainRun? = null
    var horizontalRun: HorizontalWidgetRun? = null
    var verticalRun: VerticalWidgetRun? = null
    var isTerminalWidget = booleanArrayOf(true, true)
    var mResolvedHasRatio = false
    private var mMeasureRequested = true
    private val OPTIMIZE_WRAP = false
    private val OPTIMIZE_WRAP_ON_RESOLVED = true
    private var mWidthOverride = -1
    private var mHeightOverride = -1
    @JvmField
    var frame = WidgetFrame(this)
    @JvmField
    var stringId: String? = null
    fun getRun(orientation: Int): WidgetRun? {
        if (orientation == HORIZONTAL) {
            return horizontalRun
        } else if (orientation == VERTICAL) {
            return verticalRun
        }
        return null
    }

    private var resolvedHorizontal = false
    private var resolvedVertical = false
    var isHorizontalSolvingPassDone = false
        private set
    var isVerticalSolvingPassDone = false
        private set

    fun setFinalFrame(left: Int, top: Int, right: Int, bottom: Int, baseline: Int, orientation: Int) {
        setFrame(left, top, right, bottom)
        baselineDistance = baseline
        if (orientation == HORIZONTAL) {
            resolvedHorizontal = true
            resolvedVertical = false
        } else if (orientation == VERTICAL) {
            resolvedHorizontal = false
            resolvedVertical = true
        } else if (orientation == BOTH) {
            resolvedHorizontal = true
            resolvedVertical = true
        } else {
            resolvedHorizontal = false
            resolvedVertical = false
        }
    }

    fun setFinalLeft(x1: Int) {
        mLeft!!.finalValue = x1
        mX = x1
    }

    fun setFinalTop(y1: Int) {
        mTop.finalValue = y1
        mY = y1
    }

    fun resetSolvingPassFlag() {
        isHorizontalSolvingPassDone = false
        isVerticalSolvingPassDone = false
    }

    fun markHorizontalSolvingPassDone() {
        isHorizontalSolvingPassDone = true
    }

    fun markVerticalSolvingPassDone() {
        isVerticalSolvingPassDone = true
    }

    fun setFinalHorizontal(x1: Int, x2: Int) {
        if (resolvedHorizontal) {
            return
        }
        mLeft!!.finalValue = x1
        mRight!!.finalValue = x2
        mX = x1
        mWidth = x2 - x1
        resolvedHorizontal = true
        if (LinearSystem.FULL_DEBUG) {
            println(
                "*** SET FINAL HORIZONTAL FOR " + debugName
                        + " : " + x1 + " -> " + x2 + " (width: " + mWidth + ")"
            )
        }
    }

    fun setFinalVertical(y1: Int, y2: Int) {
        if (resolvedVertical) {
            return
        }
        mTop.finalValue = y1
        mBottom.finalValue = y2
        mY = y1
        mHeight = y2 - y1
        if (hasBaseline) {
            mBaseline!!.finalValue = y1 + mBaselineDistance
        }
        resolvedVertical = true
        if (LinearSystem.FULL_DEBUG) {
            println(
                "*** SET FINAL VERTICAL FOR " + debugName
                        + " : " + y1 + " -> " + y2 + " (height: " + mHeight + ")"
            )
        }
    }

    fun setFinalBaseline(baselineValue: Int) {
        if (!hasBaseline) {
            return
        }
        val y1 = baselineValue - mBaselineDistance
        val y2 = y1 + mHeight
        mY = y1
        mTop.finalValue = y1
        mBottom.finalValue = y2
        mBaseline!!.finalValue = baselineValue
        resolvedVertical = true
    }

    open val isResolvedHorizontally: Boolean
        get() = resolvedHorizontal || mLeft!!.hasFinalValue() && mRight!!.hasFinalValue()
    open val isResolvedVertically: Boolean
        get() = resolvedVertical || mTop.hasFinalValue() && mBottom.hasFinalValue()

    fun resetFinalResolution() {
        resolvedHorizontal = false
        resolvedVertical = false
        isHorizontalSolvingPassDone = false
        isVerticalSolvingPassDone = false
        var i = 0
        val mAnchorsSize = anchors.size
        while (i < mAnchorsSize) {
            val anchor = anchors[i]
            anchor!!.resetFinalResolution()
            i++
        }
    }

    fun ensureMeasureRequested() {
        mMeasureRequested = true
    }

    fun hasDependencies(): Boolean {
        var i = 0
        val mAnchorsSize = anchors.size
        while (i < mAnchorsSize) {
            val anchor = anchors[i]
            if (anchor!!.hasDependents()) {
                return true
            }
            i++
        }
        return false
    }

    fun hasDanglingDimension(orientation: Int): Boolean {
        return if (orientation == HORIZONTAL) {
            val horizontalTargets = (if (mLeft!!.target != null) 1 else 0) + if (mRight!!.target != null) 1 else 0
            horizontalTargets < 2
        } else {
            val verticalTargets = (if (mTop.target != null) 1 else 0) + (if (mBottom.target != null) 1 else 0) + if (mBaseline!!.target != null) 1 else 0
            verticalTargets < 2
        }
    }

    fun hasResolvedTargets(orientation: Int, size: Int): Boolean {
        if (orientation == HORIZONTAL) {
            if (mLeft!!.target != null && mLeft!!.target!!.hasFinalValue()
                && mRight!!.target != null && mRight!!.target!!.hasFinalValue()
            ) {
                return (mRight!!.target!!.finalValue - mRight!!.margin
                        - (mLeft!!.target!!.finalValue + mLeft!!.margin)) >= size
            }
        } else {
            if (mTop.target != null && mTop.target!!.hasFinalValue()
                && mBottom.target != null && mBottom.target!!.hasFinalValue()
            ) {
                return (mBottom.target!!.finalValue - mBottom.margin
                        - (mTop.target!!.finalValue + mTop.margin)) >= size
            }
        }
        return false
    }

    // Support for direct resolution
    @JvmField
    var mHorizontalResolution = UNKNOWN
    @JvmField
    var mVerticalResolution = UNKNOWN
    private var mWrapBehaviorInParent = WRAP_BEHAVIOR_INCLUDED
    @JvmField
    var mMatchConstraintDefaultWidth = MATCH_CONSTRAINT_SPREAD
    @JvmField
    var mMatchConstraintDefaultHeight = MATCH_CONSTRAINT_SPREAD
    @JvmField
    var mResolvedMatchConstraintDefault = IntArray(2)
    var mMatchConstraintMinWidth = 0
    var mMatchConstraintMaxWidth = 0
    @JvmField
    var mMatchConstraintPercentWidth = 1f
    var mMatchConstraintMinHeight = 0
    var mMatchConstraintMaxHeight = 0
    @JvmField
    var mMatchConstraintPercentHeight = 1f
    /**
     * Returns true if width is set to wrap_content
     *
     * @return
     */
    /**
     * Keep track of wrap_content for width
     *
     * @param widthWrapContent
     */
    var isWidthWrapContent = false
    /**
     * Returns true if height is set to wrap_content
     *
     * @return
     */
    /**
     * Keep track of wrap_content for height
     *
     * @param heightWrapContent
     */
    var isHeightWrapContent = false
    var mResolvedDimensionRatioSide = UNKNOWN
    var mResolvedDimensionRatio = 1.0f
    private var mMaxDimension = intArrayOf(Int.MAX_VALUE, Int.MAX_VALUE)
    private var mCircleConstraintAngle = 0f
    var hasBaseline = false
    var isInPlaceholder = false
    var isInVirtualLayout = false
    var maxHeight: Int
        get() = mMaxDimension[VERTICAL]
        set(maxHeight) {
            mMaxDimension[VERTICAL] = maxHeight
        }
    var maxWidth: Int
        get() = mMaxDimension[HORIZONTAL]
        set(maxWidth) {
            mMaxDimension[HORIZONTAL] = maxWidth
        }
    val isSpreadWidth: Boolean
        get() = mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_SPREAD && dimensionRatio == 0f && mMatchConstraintMinWidth == 0 && mMatchConstraintMaxWidth == 0 && mListDimensionBehaviors[HORIZONTAL] == DimensionBehaviour.MATCH_CONSTRAINT
    val isSpreadHeight: Boolean
        get() = mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_SPREAD && dimensionRatio == 0f && mMatchConstraintMinHeight == 0 && mMatchConstraintMaxHeight == 0 && mListDimensionBehaviors[VERTICAL] == DimensionBehaviour.MATCH_CONSTRAINT

    fun setInBarrier(orientation: Int, value: Boolean) {
        mIsInBarrier[orientation] = value
    }

    fun isInBarrier(orientation: Int): Boolean {
        return mIsInBarrier[orientation]
    }

    var isMeasureRequested: Boolean
        get() = mMeasureRequested && visibility != GONE
        set(measureRequested) {
            mMeasureRequested = measureRequested
        }
    var wrapBehaviorInParent: Int
        get() = mWrapBehaviorInParent
        set(behavior) {
            if (behavior >= 0 && behavior <= WRAP_BEHAVIOR_SKIPPED) {
                mWrapBehaviorInParent = behavior
            }
        }

    /**
     * Keep a cache of the last measure cache as we can bypass remeasures during the onMeasure...
     * the View's measure cache will only be reset in onLayout, so too late for us.
     */
    var lastHorizontalMeasureSpec = 0
        private set
    var lastVerticalMeasureSpec = 0
        private set

    fun setLastMeasureSpec(horizontal: Int, vertical: Int) {
        lastHorizontalMeasureSpec = horizontal
        lastVerticalMeasureSpec = vertical
        isMeasureRequested = false
    }

    /**
     * Define how the widget will resize
     */
    enum class DimensionBehaviour {
        FIXED, WRAP_CONTENT, MATCH_CONSTRAINT, MATCH_PARENT
    }

    // The anchors available on the widget
    // note: all anchors should be added to the mAnchors array (see addAnchors())
    @JvmField
    var mLeft: ConstraintAnchor? = ConstraintAnchor(this, ConstraintAnchor.Type.LEFT)
    @JvmField
    var mTop = ConstraintAnchor(this, ConstraintAnchor.Type.TOP)
    @JvmField
    var mRight: ConstraintAnchor? = ConstraintAnchor(this, ConstraintAnchor.Type.RIGHT)
    @JvmField
    var mBottom = ConstraintAnchor(this, ConstraintAnchor.Type.BOTTOM)
    @JvmField
    var mBaseline: ConstraintAnchor? = ConstraintAnchor(this, ConstraintAnchor.Type.BASELINE)
    var mCenterX = ConstraintAnchor(this, ConstraintAnchor.Type.CENTER_X)
    var mCenterY = ConstraintAnchor(this, ConstraintAnchor.Type.CENTER_Y)
    var mCenter = ConstraintAnchor(this, ConstraintAnchor.Type.CENTER)
    @JvmField
    var mListAnchors = arrayOf(mLeft, mRight, mTop, mBottom, mBaseline, mCenter)

    /**
     * Return the array of anchors of this widget
     *
     * @return array of anchors
     */
    var anchors = ArrayList()
        protected set
    private val mIsInBarrier = BooleanArray(2)
    @JvmField
    var mListDimensionBehaviors = arrayOf(DimensionBehaviour.FIXED, DimensionBehaviour.FIXED)
    /**
     * Returns the parent of this widget if there is one
     *
     * @return parent
     */
    /**
     * Set the parent of this widget
     *
     * @param widget parent
     */
    // Parent of this widget
    var parent: ConstraintWidget? = null

    // Dimensions of the widget
    @JvmField
    var mWidth = 0
    @JvmField
    var mHeight = 0

    /**
     * Return the current ratio of this widget
     *
     * @return the dimension ratio (HORIZONTAL, VERTICAL, or UNKNOWN)
     */
    var dimensionRatio = 0f

    /**
     * Return the current side on which ratio will be applied
     *
     * @return HORIZONTAL, VERTICAL, or UNKNOWN
     */
    var dimensionRatioSide = UNKNOWN
        protected set

    // Origin of the widget
    @JvmField
    var mX = 0
    @JvmField
    var mY = 0
    var mRelX = 0
    var mRelY = 0

    // Root offset
    protected var mOffsetX = 0
    protected var mOffsetY = 0

    // Baseline distance relative to the top of the widget
    @JvmField
    var mBaselineDistance = 0

    // Minimum sizes for the widget
    @JvmField
    protected var mMinWidth = 0
    @JvmField
    protected var mMinHeight = 0
    /**
     * Return the horizontal percentage bias that is used when two opposite connections
     * exist of the same strength.
     *
     * @return horizontal percentage bias
     */
    /**
     * Set the horizontal bias percent to apply when we have two opposite constraints of
     * equal strength
     *
     * @param horizontalBiasPercent the percentage used
     */
    var horizontalBiasPercent = DEFAULT_BIAS
    /**
     * Return the vertical percentage bias that is used when two opposite connections
     * exist of the same strength.
     *
     * @return vertical percentage bias
     */
    /**
     * Set the vertical bias percent to apply when we have two opposite constraints of
     * equal strength
     *
     * @param verticalBiasPercent the percentage used
     */
    var verticalBiasPercent = DEFAULT_BIAS
    /**
     * Return the companion widget. Typically, this would be the real
     * widget we represent with this instance of ConstraintWidget.
     *
     * @return the companion widget, if set.
     */
    /**
     * Set the companion widget. Typically, this would be the real widget we
     * represent with this instance of ConstraintWidget.
     *
     * @param companion
     */
    // The companion widget (typically, the real widget we represent)
    var companionWidget: Any? = null

    // This is used to possibly "skip" a position while inside a container. For example,
    // a container like Table can use this to implement empty cells
    // (the item positioned after the empty cell will have a skip value of 1)
    private var mContainerItemSkip = 0
    /**
     * Returns the current visibility value for this widget
     *
     * @return the visibility (VISIBLE, INVISIBLE, or GONE)
     */
    /**
     * Set the visibility for this widget
     *
     * @param visibility either VISIBLE, INVISIBLE, or GONE
     */
    // Contains the visibility status of the widget (VISIBLE, INVISIBLE, or GONE)
    var visibility = VISIBLE
    /**
     * Returns the name of this widget (used for debug purposes)
     *
     * @return the debug name
     */
    /**
     * Set the debug name of this widget
     */
    var debugName: String? = null
    /**
     * Returns the type string if set
     *
     * @return type (null if not set)
     */
    /**
     * Set the type of the widget (as a String)
     *
     * @param type type of the widget
     */
    open var type: String? = null
    var mDistToTop = 0
    var mDistToLeft = 0
    var mDistToRight = 0
    var mDistToBottom = 0
    var mLeftHasCentered = false
    var mRightHasCentered = false
    var mTopHasCentered = false
    var mBottomHasCentered = false
    var mHorizontalWrapVisited = false
    var mVerticalWrapVisited = false
    var mGroupsToSolver = false
    /**
     * get the chain starting from this widget to be packed.
     * The horizontal bias will control how elements of the chain are positioned.
     *
     * @return Horizontal Chain Style
     */
    /**
     * Set the chain starting from this widget to be packed.
     * The horizontal bias will control how elements of the chain are positioned.
     *
     * @param horizontalChainStyle (CHAIN_SPREAD, CHAIN_SPREAD_INSIDE, CHAIN_PACKED)
     */
    // Chain support
    var horizontalChainStyle = CHAIN_SPREAD
    /**
     * Set the chain starting from this widget to be packed.
     * The vertical bias will control how elements of the chain are positioned.
     *
     * @return
     */
    /**
     * Set the chain starting from this widget to be packed.
     * The vertical bias will control how elements of the chain are positioned.
     *
     * @param verticalChainStyle (CHAIN_SPREAD, CHAIN_SPREAD_INSIDE, CHAIN_PACKED)
     */
    var verticalChainStyle = CHAIN_SPREAD
    var mHorizontalChainFixedPosition = false
    var mVerticalChainFixedPosition = false
    var mWeight = floatArrayOf(UNKNOWN.toFloat(), UNKNOWN.toFloat())
    var mListNextMatchConstraintsWidget = arrayOf(null, null)
    var mNextChainWidget = arrayOf(null, null)
    var mHorizontalNextWidget: ConstraintWidget? = null
    var mVerticalNextWidget: ConstraintWidget? = null

    // TODO: see if we can make this simpler
    open fun reset() {
        mLeft!!.reset()
        mTop.reset()
        mRight!!.reset()
        mBottom.reset()
        mBaseline!!.reset()
        mCenterX.reset()
        mCenterY.reset()
        mCenter.reset()
        parent = null
        mCircleConstraintAngle = 0f
        mWidth = 0
        mHeight = 0
        dimensionRatio = 0f
        dimensionRatioSide = UNKNOWN
        mX = 0
        mY = 0
        mOffsetX = 0
        mOffsetY = 0
        mBaselineDistance = 0
        mMinWidth = 0
        mMinHeight = 0
        horizontalBiasPercent = DEFAULT_BIAS
        verticalBiasPercent = DEFAULT_BIAS
        mListDimensionBehaviors[DIMENSION_HORIZONTAL] = DimensionBehaviour.FIXED
        mListDimensionBehaviors[DIMENSION_VERTICAL] = DimensionBehaviour.FIXED
        companionWidget = null
        mContainerItemSkip = 0
        visibility = VISIBLE
        type = null
        mHorizontalWrapVisited = false
        mVerticalWrapVisited = false
        horizontalChainStyle = CHAIN_SPREAD
        verticalChainStyle = CHAIN_SPREAD
        mHorizontalChainFixedPosition = false
        mVerticalChainFixedPosition = false
        mWeight[DIMENSION_HORIZONTAL] = UNKNOWN.toFloat()
        mWeight[DIMENSION_VERTICAL] = UNKNOWN.toFloat()
        mHorizontalResolution = UNKNOWN
        mVerticalResolution = UNKNOWN
        mMaxDimension[HORIZONTAL] = Int.MAX_VALUE
        mMaxDimension[VERTICAL] = Int.MAX_VALUE
        mMatchConstraintDefaultWidth = MATCH_CONSTRAINT_SPREAD
        mMatchConstraintDefaultHeight = MATCH_CONSTRAINT_SPREAD
        mMatchConstraintPercentWidth = 1f
        mMatchConstraintPercentHeight = 1f
        mMatchConstraintMaxWidth = Int.MAX_VALUE
        mMatchConstraintMaxHeight = Int.MAX_VALUE
        mMatchConstraintMinWidth = 0
        mMatchConstraintMinHeight = 0
        mResolvedHasRatio = false
        mResolvedDimensionRatioSide = UNKNOWN
        mResolvedDimensionRatio = 1f
        mGroupsToSolver = false
        isTerminalWidget[HORIZONTAL] = true
        isTerminalWidget[VERTICAL] = true
        isInVirtualLayout = false
        mIsInBarrier[HORIZONTAL] = false
        mIsInBarrier[VERTICAL] = false
        mMeasureRequested = true
        mResolvedMatchConstraintDefault[HORIZONTAL] = 0
        mResolvedMatchConstraintDefault[VERTICAL] = 0
        mWidthOverride = -1
        mHeightOverride = -1
    }

    ///////////////////////////////////SERIALIZE///////////////////////////////////////////////
    private fun serializeAnchor(ret: StringBuilder, side: String, a: ConstraintAnchor?) {
        if (a!!.target == null) {
            return
        }
        ret.append(side)
        ret.append(" : [ '")
        ret.append(a.target)
        ret.append("',")
        ret.append(a.mMargin)
        ret.append(",")
        ret.append(a.mGoneMargin)
        ret.append(",")
        ret.append(" ] ,\n")
    }

    private fun serializeCircle(ret: StringBuilder, a: ConstraintAnchor, angle: Float) {
        if (a.target == null) {
            return
        }
        ret.append("circle : [ '")
        ret.append(a.target)
        ret.append("',")
        ret.append(a.mMargin)
        ret.append(",")
        ret.append(angle)
        ret.append(",")
        ret.append(" ] ,\n")
    }

    private fun serializeAttribute(ret: StringBuilder, type: String, value: Float, def: Float) {
        if (value == def) {
            return
        }
        ret.append(type)
        ret.append(" :   ")
        ret.append(value)
        ret.append(",\n")
    }

    private fun serializeAttribute(ret: StringBuilder, type: String, value: Int, def: Int) {
        if (value == def) {
            return
        }
        ret.append(type)
        ret.append(" :   ")
        ret.append(value)
        ret.append(",\n")
    }

    private fun serializeDimensionRatio(ret: StringBuilder, type: String, value: Float, whichSide: Int) {
        if (value == 0f) {
            return
        }
        ret.append(type)
        ret.append(" :  [")
        ret.append(value)
        ret.append(",")
        ret.append(whichSide)
        ret.append("")
        ret.append("],\n")
    }

    private fun serializeSize(
        ret: StringBuilder, type: String, size: Int,
        min: Int, max: Int, override: Int,
        matchConstraintMin: Int, matchConstraintDefault: Int,
        MatchConstraintPercent: Float,
        weight: Float
    ) {
        ret.append(type)
        ret.append(" :  {\n")
        serializeAttribute(ret, "size", size, Int.MIN_VALUE)
        serializeAttribute(ret, "min", min, 0)
        serializeAttribute(ret, "max", max, Int.MAX_VALUE)
        serializeAttribute(ret, "matchMin", matchConstraintMin, 0)
        serializeAttribute(ret, "matchDef", matchConstraintDefault, MATCH_CONSTRAINT_SPREAD)
        serializeAttribute(ret, "matchPercent", matchConstraintDefault, 1)
        ret.append("},\n")
    }

    fun serialize(ret: StringBuilder): StringBuilder {
        ret.append("{\n")
        serializeAnchor(ret, "left", mLeft)
        serializeAnchor(ret, "top", mTop)
        serializeAnchor(ret, "right", mRight)
        serializeAnchor(ret, "bottom", mBottom)
        serializeAnchor(ret, "baseline", mBaseline)
        serializeAnchor(ret, "centerX", mCenterX)
        serializeAnchor(ret, "centerY", mCenterY)
        serializeCircle(ret, mCenter, mCircleConstraintAngle)
        serializeSize(
            ret, "width",
            mWidth,
            mMinWidth,
            mMaxDimension[HORIZONTAL],
            mWidthOverride,
            mMatchConstraintMinWidth,
            mMatchConstraintDefaultWidth,
            mMatchConstraintPercentWidth,
            mWeight[DIMENSION_HORIZONTAL]
        )
        serializeSize(
            ret, "height",
            mHeight,
            mMinHeight,
            mMaxDimension[VERTICAL],
            mHeightOverride,
            mMatchConstraintMinHeight,
            mMatchConstraintDefaultHeight,
            mMatchConstraintPercentHeight,
            mWeight[DIMENSION_VERTICAL]
        )
        serializeDimensionRatio(ret, "dimensionRatio", dimensionRatio, dimensionRatioSide)
        serializeAttribute(ret, "horizontalBias", horizontalBiasPercent, DEFAULT_BIAS)
        serializeAttribute(ret, "verticalBias", verticalBiasPercent, DEFAULT_BIAS)
        ret.append("}\n")
        return ret
    }

    ///////////////////////////////////END SERIALIZE///////////////////////////////////////////
    @JvmField
    var horizontalGroup = -1
    @JvmField
    var verticalGroup = -1
    fun oppositeDimensionDependsOn(orientation: Int): Boolean {
        val oppositeOrientation = if (orientation == HORIZONTAL) VERTICAL else HORIZONTAL
        val dimensionBehaviour = mListDimensionBehaviors[orientation]
        val oppositeDimensionBehaviour = mListDimensionBehaviors[oppositeOrientation]
        return (dimensionBehaviour == DimensionBehaviour.MATCH_CONSTRAINT
                && oppositeDimensionBehaviour == DimensionBehaviour.MATCH_CONSTRAINT)
        //&& mDimensionRatio != 0;
    }

    fun oppositeDimensionsTied(): Boolean {
        return  /* isInHorizontalChain() || isInVerticalChain() || */(mListDimensionBehaviors[HORIZONTAL] == DimensionBehaviour.MATCH_CONSTRAINT
                && mListDimensionBehaviors[VERTICAL] == DimensionBehaviour.MATCH_CONSTRAINT)
    }

    fun hasDimensionOverride(): Boolean {
        return mWidthOverride != -1 || mHeightOverride != -1
    }
    /*-----------------------------------------------------------------------*/ // Creation
    /*-----------------------------------------------------------------------*/
    /**
     * Default constructor
     */
    constructor() {
        addAnchors()
    }

    constructor(debugName: String?) {
        addAnchors()
        this.debugName = debugName
    }

    /**
     * Constructor
     *
     * @param x      x position
     * @param y      y position
     * @param width  width of the layout
     * @param height height of the layout
     */
    constructor(x: Int, y: Int, width: Int, height: Int) {
        mX = x
        mY = y
        mWidth = width
        mHeight = height
        addAnchors()
    }

    constructor(debugName: String?, x: Int, y: Int, width: Int, height: Int) : this(x, y, width, height) {
        this.debugName = debugName
    }

    /**
     * Constructor
     *
     * @param width  width of the layout
     * @param height height of the layout
     */
    constructor(width: Int, height: Int) : this(0, 0, width, height) {}

    fun ensureWidgetRuns() {
        if (horizontalRun == null) {
            horizontalRun = HorizontalWidgetRun(this)
        }
        if (verticalRun == null) {
            verticalRun = VerticalWidgetRun(this)
        }
    }

    constructor(debugName: String?, width: Int, height: Int) : this(width, height) {
        this.debugName = debugName
    }

    /**
     * Reset the solver variables of the anchors
     */
    open fun resetSolverVariables(cache: Cache?) {
        mLeft!!.resetSolverVariable(cache)
        mTop.resetSolverVariable(cache)
        mRight!!.resetSolverVariable(cache)
        mBottom.resetSolverVariable(cache)
        mBaseline!!.resetSolverVariable(cache)
        mCenter.resetSolverVariable(cache)
        mCenterX.resetSolverVariable(cache)
        mCenterY.resetSolverVariable(cache)
    }

    /**
     * Add all the anchors to the mAnchors array
     */
    private fun addAnchors() {
        anchors.add(mLeft)
        anchors.add(mTop)
        anchors.add(mRight)
        anchors.add(mBottom)
        anchors.add(mCenterX)
        anchors.add(mCenterY)
        anchors.add(mCenter)
        anchors.add(mBaseline)
    }

    /**
     * Returns true if the widget is the root widget
     *
     * @return true if root widget, false otherwise
     */
    val isRoot: Boolean
        get() = parent == null

    /**
     * Set a circular constraint
     *
     * @param target the target widget we will use as the center of the circle
     * @param angle  the angle (from 0 to 360)
     * @param radius the radius used
     */
    fun connectCircularConstraint(target: ConstraintWidget, angle: Float, radius: Int) {
        immediateConnect(
            ConstraintAnchor.Type.CENTER, target, ConstraintAnchor.Type.CENTER,
            radius, 0
        )
        mCircleConstraintAngle = angle
    }

    /**
     * Utility debug function. Sets the names of the anchors in the solver given
     * a widget's name. The given name is used as a prefix, resulting in anchors' names
     * of the form:
     *
     *
     *
     *  * {name}.left
     *  * {name}.top
     *  * {name}.right
     *  * {name}.bottom
     *  * {name}.baseline
     *
     *
     * @param system solver used
     * @param name   name of the widget
     */
    fun setDebugSolverName(system: LinearSystem, name: String) {
        debugName = name
        val left = system.createObjectVariable(mLeft)
        val top = system.createObjectVariable(mTop)
        val right = system.createObjectVariable(mRight)
        val bottom = system.createObjectVariable(mBottom)
        left?.name = "$name.left"
        top?.name = "$name.top"
        right?.name = "$name.right"
        bottom?.name = "$name.bottom"
        val baseline = system.createObjectVariable(mBaseline)
        baseline?.name = "$name.baseline"
    }

    /**
     * Create all the system variables for this widget
     *
     * @param system
     * @suppress
     */
    fun createObjectVariables(system: LinearSystem) {
        val left = system.createObjectVariable(mLeft)
        val top = system.createObjectVariable(mTop)
        val right = system.createObjectVariable(mRight)
        val bottom = system.createObjectVariable(mBottom)
        if (mBaselineDistance > 0) {
            val baseline = system.createObjectVariable(mBaseline)
        }
    }

    /**
     * Returns a string representation of the ConstraintWidget
     *
     * @return string representation of the widget
     */
    override fun toString(): String {
        return ((if (type != null) "type: " + type + " " else "")
                + (if (debugName != null) "id: " + debugName + " " else "")
                + "(" + mX + ", " + mY + ") - (" + mWidth + " x " + mHeight + ")")
    }
    /*-----------------------------------------------------------------------*/ // Position
    /*-----------------------------------------------------------------------*/ // The widget position is expressed in two ways:
    // - relative to its direct parent container (getX(), getY())
    // - relative to the root container (getDrawX(), getDrawY())
    // Additionally, getDrawX()/getDrawY() are used when animating the
    // widget position on screen
    /*-----------------------------------------------------------------------*/
    /**
     * Return the x position of the widget, relative to its container
     *
     * @return x position
     */
    /**
     * Set the x position of the widget, relative to its container
     *
     * @param x x position
     */
    var x: Int
        get() = if (parent != null && parent is ConstraintWidgetContainer) {
            (parent as ConstraintWidgetContainer).mPaddingLeft + mX
        } else mX
        set(x) {
            mX = x
        }
    /**
     * Return the y position of the widget, relative to its container
     *
     * @return y position
     */
    /**
     * Set the y position of the widget, relative to its container
     *
     * @param y y position
     */
    var y: Int
        get() = if (parent != null && parent is ConstraintWidgetContainer) {
            (parent as ConstraintWidgetContainer).mPaddingTop + mY
        } else mY
        set(y) {
            mY = y
        }
    /**
     * Return the width of the widget
     *
     * @return width width
     */
    /**
     * Set the width of the widget
     *
     * @param w width
     */
    var width: Int
        get() = if (visibility == GONE) {
            0
        } else mWidth
        set(w) {
            mWidth = w
            if (mWidth < mMinWidth) {
                mWidth = mMinWidth
            }
        }
    val optimizerWrapWidth: Int
        get() {
            var w = mWidth
            if (mListDimensionBehaviors[DIMENSION_HORIZONTAL] == DimensionBehaviour.MATCH_CONSTRAINT) {
                if (mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_WRAP) {
                    w = max(mMatchConstraintMinWidth, w)
                } else if (mMatchConstraintMinWidth > 0) {
                    w = mMatchConstraintMinWidth
                    mWidth = w
                } else {
                    w = 0
                }
                if (mMatchConstraintMaxWidth > 0 && mMatchConstraintMaxWidth < w) {
                    w = mMatchConstraintMaxWidth
                }
            }
            return w
        }
    val optimizerWrapHeight: Int
        get() {
            var h = mHeight
            if (mListDimensionBehaviors[DIMENSION_VERTICAL] == DimensionBehaviour.MATCH_CONSTRAINT) {
                if (mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_WRAP) {
                    h = max(mMatchConstraintMinHeight, h)
                } else if (mMatchConstraintMinHeight > 0) {
                    h = mMatchConstraintMinHeight
                    mHeight = h
                } else {
                    h = 0
                }
                if (mMatchConstraintMaxHeight > 0 && mMatchConstraintMaxHeight < h) {
                    h = mMatchConstraintMaxHeight
                }
            }
            return h
        }
    /**
     * Return the height of the widget
     *
     * @return height height
     */
    /**
     * Set the height of the widget
     *
     * @param h height
     */
    var height: Int
        get() = if (visibility == GONE) {
            0
        } else mHeight
        set(h) {
            mHeight = h
            if (mHeight < mMinHeight) {
                mHeight = mMinHeight
            }
        }

    /**
     * Get a dimension of the widget in a particular orientation.
     *
     * @param orientation
     * @return The dimension of the specified orientation.
     */
    fun getLength(orientation: Int): Int {
        return if (orientation == HORIZONTAL) {
            width
        } else if (orientation == VERTICAL) {
            height
        } else {
            0
        }
    }

    /**
     * Return the x position of the widget, relative to the root
     * (without animation)
     *
     * @return x position
     */
    protected val rootX: Int
        protected get() = mX + mOffsetX

    /**
     * Return the y position of the widget, relative to the root
     * (without animation)
     *
     * @return
     */
    protected val rootY: Int
        protected get() = mY + mOffsetY
    /**
     * Return the minimum width of the widget
     *
     * @return minimum width
     */
    /**
     * Set the minimum width of the widget
     *
     * @param w minimum width
     */
    var minWidth: Int
        get() = mMinWidth
        set(w) {
            mMinWidth = if (w < 0) {
                0
            } else {
                w
            }
        }
    /**
     * Return the minimum height of the widget
     *
     * @return minimum height
     */
    /**
     * Set the minimum height of the widget
     *
     * @param h minimum height
     */
    var minHeight: Int
        get() = mMinHeight
        set(h) {
            mMinHeight = if (h < 0) {
                0
            } else {
                h
            }
        }

    /**
     * Return the left position of the widget (similar to [.getX])
     *
     * @return left position of the widget
     */
    val left: Int
        get() = x

    /**
     * Return the top position of the widget (similar to [.getY])
     *
     * @return top position of the widget
     */
    val top: Int
        get() = y

    /**
     * Return the right position of the widget
     *
     * @return right position of the widget
     */
    val right: Int
        get() = x + mWidth

    /**
     * Return the bottom position of the widget
     *
     * @return bottom position of the widget
     */
    val bottom: Int
        get() = y + mHeight

    /**
     * Returns all the horizontal margin of the widget.
     */
    val horizontalMargin: Int
        get() {
            var margin = 0
            if (mLeft != null) {
                margin += mLeft!!.mMargin
            }
            if (mRight != null) {
                margin += mRight!!.mMargin
            }
            return margin
        }

    /**
     * Returns all the vertical margin of the widget
     */
    val verticalMargin: Int
        get() {
            var margin = 0
            if (mLeft != null) {
                margin += mTop.mMargin
            }
            if (mRight != null) {
                margin += mBottom.mMargin
            }
            return margin
        }

    /**
     * Return the percentage bias that is used when two opposite connections exist of the same
     * strength in a particular orientation.
     *
     * @param orientation Orientation [.HORIZONTAL]/[.VERTICAL].
     * @return Respective percentage bias.
     */
    fun getBiasPercent(orientation: Int): Float {
        return if (orientation == HORIZONTAL) {
            horizontalBiasPercent
        } else if (orientation == VERTICAL) {
            verticalBiasPercent
        } else {
            UNKNOWN.toFloat()
        }
    }

    /**
     * Return true if this widget has a baseline
     *
     * @return true if the widget has a baseline, false otherwise
     */
    fun hasBaseline(): Boolean {
        return hasBaseline
    }
    /**
     * Return the baseline distance relative to the top of the widget
     *
     * @return baseline
     */
    /**
     * Set the baseline distance relative to the top of the widget
     *
     * @param baseline the distance of the baseline relative to the widget's top
     */
    var baselineDistance: Int
        get() = mBaselineDistance
        set(baseline) {
            mBaselineDistance = baseline
            hasBaseline = baseline > 0
        }

    /**
     * Set both the origin in (x, y) of the widget, relative to its container
     *
     * @param x x position
     * @param y y position
     */
    fun setOrigin(x: Int, y: Int) {
        mX = x
        mY = y
    }

    /**
     * Set the offset of this widget relative to the root widget
     *
     * @param x horizontal offset
     * @param y vertical offset
     */
    open fun setOffset(x: Int, y: Int) {
        mOffsetX = x
        mOffsetY = y
    }

    /**
     * Set the margin to be used when connected to a widget with a visibility of GONE
     *
     * @param type       the anchor to set the margin on
     * @param goneMargin the margin value to use
     */
    fun setGoneMargin(type: ConstraintAnchor.Type?, goneMargin: Int) {
        when (type) {
            ConstraintAnchor.Type.LEFT -> {
                mLeft!!.mGoneMargin = goneMargin
            }
            ConstraintAnchor.Type.TOP -> {
                mTop.mGoneMargin = goneMargin
            }
            ConstraintAnchor.Type.RIGHT -> {
                mRight!!.mGoneMargin = goneMargin
            }
            ConstraintAnchor.Type.BOTTOM -> {
                mBottom.mGoneMargin = goneMargin
            }
            ConstraintAnchor.Type.BASELINE -> {
                mBaseline!!.mGoneMargin = goneMargin
            }
            ConstraintAnchor.Type.CENTER, ConstraintAnchor.Type.CENTER_X, ConstraintAnchor.Type.CENTER_Y, ConstraintAnchor.Type.NONE -> {}
        }
    }

    /**
     * Set the dimension of a widget in a particular orientation.
     *
     * @param length      Size of the dimension.
     * @param orientation HORIZONTAL or VERTICAL
     */
    fun setLength(length: Int, orientation: Int) {
        if (orientation == HORIZONTAL) {
            width = length
        } else if (orientation == VERTICAL) {
            height = length
        }
    }

    /**
     * Set the horizontal style when MATCH_CONSTRAINT is set
     *
     * @param horizontalMatchStyle MATCH_CONSTRAINT_SPREAD or MATCH_CONSTRAINT_WRAP
     * @param min                  minimum value
     * @param max                  maximum value
     * @param percent              Percent width
     */
    fun setHorizontalMatchStyle(horizontalMatchStyle: Int, min: Int, max: Int, percent: Float) {
        mMatchConstraintDefaultWidth = horizontalMatchStyle
        mMatchConstraintMinWidth = min
        mMatchConstraintMaxWidth = if (max == Int.MAX_VALUE) 0 else max
        mMatchConstraintPercentWidth = percent
        if (percent > 0 && percent < 1 && mMatchConstraintDefaultWidth == MATCH_CONSTRAINT_SPREAD) {
            mMatchConstraintDefaultWidth = MATCH_CONSTRAINT_PERCENT
        }
    }

    /**
     * Set the vertical style when MATCH_CONSTRAINT is set
     *
     * @param verticalMatchStyle MATCH_CONSTRAINT_SPREAD or MATCH_CONSTRAINT_WRAP
     * @param min                minimum value
     * @param max                maximum value
     * @param percent            Percent height
     */
    fun setVerticalMatchStyle(verticalMatchStyle: Int, min: Int, max: Int, percent: Float) {
        mMatchConstraintDefaultHeight = verticalMatchStyle
        mMatchConstraintMinHeight = min
        mMatchConstraintMaxHeight = if (max == Int.MAX_VALUE) 0 else max
        mMatchConstraintPercentHeight = percent
        if (percent > 0 && percent < 1 && mMatchConstraintDefaultHeight == MATCH_CONSTRAINT_SPREAD) {
            mMatchConstraintDefaultHeight = MATCH_CONSTRAINT_PERCENT
        }
    }

    /**
     * Set the ratio of the widget
     *
     * @param ratio given string of format [H|V],[float|x:y] or [float|x:y]
     */
    fun setDimensionRatio(ratio: String?) {
        if (ratio == null || ratio.length == 0) {
            dimensionRatio = 0f
            return
        }
        var dimensionRatioSide = UNKNOWN
        var dimensionRatio = 0f
        val len = ratio.length
        var commaIndex = ratio.indexOf(',')
        if (commaIndex > 0 && commaIndex < len - 1) {
            val dimension = ratio.substring(0, commaIndex)
            if (dimension.equals("W", ignoreCase = true)) {
                dimensionRatioSide = HORIZONTAL
            } else if (dimension.equals("H", ignoreCase = true)) {
                dimensionRatioSide = VERTICAL
            }
            commaIndex++
        } else {
            commaIndex = 0
        }
        val colonIndex = ratio.indexOf(':')
        if (colonIndex >= 0 && colonIndex < len - 1) {
            val nominator = ratio.substring(commaIndex, colonIndex)
            val denominator = ratio.substring(colonIndex + 1)
            if (nominator.length > 0 && denominator.length > 0) {
                try {
                    val nominatorValue = nominator.toFloat()
                    val denominatorValue = denominator.toFloat()
                    if (nominatorValue > 0 && denominatorValue > 0) {
                        dimensionRatio = if (dimensionRatioSide == VERTICAL) {
                            abs(denominatorValue / nominatorValue)
                        } else {
                            abs(nominatorValue / denominatorValue)
                        }
                    }
                } catch (e: NumberFormatException) {
                    // Ignore
                }
            }
        } else {
            val r = ratio.substring(commaIndex)
            if (r.length > 0) {
                try {
                    dimensionRatio = r.toFloat()
                } catch (e: NumberFormatException) {
                    // Ignore
                }
            }
        }
        if (dimensionRatio > 0) {
            this.dimensionRatio = dimensionRatio
            this.dimensionRatioSide = dimensionRatioSide
        }
    }

    /**
     * Set the ratio of the widget
     * The ratio will be applied if at least one of the dimension (width or height) is set to a behaviour
     * of DimensionBehaviour.MATCH_CONSTRAINT -- the dimension's value will be set to the other dimension * ratio.
     *
     * @param ratio A float value that describes W/H or H/W depending on the provided dimensionRatioSide
     * @param dimensionRatioSide The side the ratio should be calculated on, HORIZONTAL, VERTICAL, or UNKNOWN
     */
    fun setDimensionRatio(ratio: Float, dimensionRatioSide: Int) {
        dimensionRatio = ratio
        this.dimensionRatioSide = dimensionRatioSide
    }

    /**
     * Set both width and height of the widget
     *
     * @param w width
     * @param h height
     */
    fun setDimension(w: Int, h: Int) {
        mWidth = w
        if (mWidth < mMinWidth) {
            mWidth = mMinWidth
        }
        mHeight = h
        if (mHeight < mMinHeight) {
            mHeight = mMinHeight
        }
    }

    /**
     * Set the position+dimension of the widget given left/top/right/bottom
     *
     * @param left   left side position of the widget
     * @param top    top side position of the widget
     * @param right  right side position of the widget
     * @param bottom bottom side position of the widget
     */
    fun setFrame(left: Int, top: Int, right: Int, bottom: Int) {
        var w = right - left
        var h = bottom - top
        mX = left
        mY = top
        if (visibility == GONE) {
            mWidth = 0
            mHeight = 0
            return
        }

        // correct dimensional instability caused by rounding errors
        if (mListDimensionBehaviors[DIMENSION_HORIZONTAL] == DimensionBehaviour.FIXED && w < mWidth) {
            w = mWidth
        }
        if (mListDimensionBehaviors[DIMENSION_VERTICAL] == DimensionBehaviour.FIXED && h < mHeight) {
            h = mHeight
        }
        mWidth = w
        mHeight = h
        if (mHeight < mMinHeight) {
            mHeight = mMinHeight
        }
        if (mWidth < mMinWidth) {
            mWidth = mMinWidth
        }
        if (mMatchConstraintMaxWidth > 0 && mListDimensionBehaviors[HORIZONTAL] == DimensionBehaviour.MATCH_CONSTRAINT) {
            mWidth = min(mWidth, mMatchConstraintMaxWidth)
        }
        if (mMatchConstraintMaxHeight > 0 && mListDimensionBehaviors[VERTICAL] == DimensionBehaviour.MATCH_CONSTRAINT) {
            mHeight = min(mHeight, mMatchConstraintMaxHeight)
        }
        if (w != mWidth) {
            mWidthOverride = mWidth
        }
        if (h != mHeight) {
            mHeightOverride = mHeight
        }
        if (LinearSystem.FULL_DEBUG) {
            println("update from solver " + debugName + " " + mX + ":" + mY + " - " + mWidth + " x " + mHeight)
        }
    }

    /**
     * Set the position+dimension of the widget based on starting/ending positions on one dimension.
     *
     * @param start       Left/Top side position of the widget.
     * @param end         Right/Bottom side position of the widget.
     * @param orientation Orientation being set (HORIZONTAL/VERTICAL).
     */
    fun setFrame(start: Int, end: Int, orientation: Int) {
        if (orientation == HORIZONTAL) {
            setHorizontalDimension(start, end)
        } else if (orientation == VERTICAL) {
            setVerticalDimension(start, end)
        }
    }

    /**
     * Set the positions for the horizontal dimension only
     *
     * @param left   left side position of the widget
     * @param right  right side position of the widget
     */
    fun setHorizontalDimension(left: Int, right: Int) {
        mX = left
        mWidth = right - left
        if (mWidth < mMinWidth) {
            mWidth = mMinWidth
        }
    }

    /**
     * Set the positions for the vertical dimension only
     *
     * @param top    top side position of the widget
     * @param bottom bottom side position of the widget
     */
    fun setVerticalDimension(top: Int, bottom: Int) {
        mY = top
        mHeight = bottom - top
        if (mHeight < mMinHeight) {
            mHeight = mMinHeight
        }
    }

    /**
     * Get the left/top position of the widget relative to the outer side of the container (right/bottom).
     *
     * @param orientation Orientation by which to find the relative positioning of the widget.
     * @return The relative position of the widget.
     */
    fun getRelativePositioning(orientation: Int): Int {
        return if (orientation == HORIZONTAL) {
            mRelX
        } else if (orientation == VERTICAL) {
            mRelY
        } else {
            0
        }
    }

    /**
     * Set the left/top position of the widget relative to the outer side of the container (right/bottom).
     *
     * @param offset      Offset of the relative position.
     * @param orientation Orientation of the offset being set.
     */
    fun setRelativePositioning(offset: Int, orientation: Int) {
        if (orientation == HORIZONTAL) {
            mRelX = offset
        } else if (orientation == VERTICAL) {
            mRelY = offset
        }
    }
    /**
     * Accessor for the skip value
     *
     * @return skip value
     */
    /**
     * Set the skip value for this widget. This can be used when a widget is in a container,
     * so that container can position the widget as if it was positioned further in the list
     * of widgets. For example, with Table, this is used to skip empty cells
     * (the widget after an empty cell will have a skip value of one)
     *
     * @param skip
     */
    var containerItemSkip: Int
        get() = mContainerItemSkip
        set(skip) {
            mContainerItemSkip = if (skip >= 0) {
                skip
            } else {
                0
            }
        }

    /**
     * Set the horizontal weight (only used in chains)
     *
     * @param horizontalWeight Floating point value weight
     */
    fun setHorizontalWeight(horizontalWeight: Float) {
        mWeight[DIMENSION_HORIZONTAL] = horizontalWeight
    }

    /**
     * Set the vertical weight (only used in chains)
     *
     * @param verticalWeight Floating point value weight
     */
    fun setVerticalWeight(verticalWeight: Float) {
        mWeight[DIMENSION_VERTICAL] = verticalWeight
    }

    /**
     * Returns true if this widget should be used in a barrier
     */
    open fun allowedInBarrier(): Boolean {
        return visibility != GONE
    }
    /*-----------------------------------------------------------------------*/ // Connections
    /*-----------------------------------------------------------------------*/
    /**
     * Immediate connection to an anchor without any checks.
     *
     * @param startType  The type of anchor on this widget
     * @param target     The target widget
     * @param endType    The type of anchor on the target widget
     * @param margin     How much margin we want to keep as a minimum distance between the two anchors
     * @param goneMargin How much margin we want to keep if the target is set to `View.GONE`
     */
    fun immediateConnect(
        startType: ConstraintAnchor.Type, target: ConstraintWidget,
        endType: ConstraintAnchor.Type, margin: Int, goneMargin: Int
    ) {
        val startAnchor = getAnchor(startType)
        val endAnchor = target.getAnchor(endType)
        startAnchor!!.connect(endAnchor, margin, goneMargin, true)
    }

    /**
     * Connect the given anchors together (the from anchor should be owned by this widget)
     *
     * @param from    the anchor we are connecting from (of this widget)
     * @param to      the anchor we are connecting to
     * @param margin  how much margin we want to have
     */
    fun connect(from: ConstraintAnchor, to: ConstraintAnchor, margin: Int) {
        if (from.owner === this) {
            connect(from.type, to.owner, to.type, margin)
        }
    }

    /**
     * Connect a given anchor of this widget to another anchor of a target widget
     *
     * @param constraintFrom which anchor of this widget to connect from
     * @param target         the target widget
     * @param constraintTo   the target anchor on the target widget
     */
    fun connect(
        constraintFrom: ConstraintAnchor.Type,
        target: ConstraintWidget,
        constraintTo: ConstraintAnchor.Type
    ) {
        if (LinearSystem.DEBUG) {
            println(debugName + " connect " + constraintFrom + " to " + target + " " + constraintTo)
        }
        connect(constraintFrom, target, constraintTo, 0)
    }

    /**
     * Connect a given anchor of this widget to another anchor of a target widget
     *
     * @param constraintFrom which anchor of this widget to connect from
     * @param target         the target widget
     * @param constraintTo   the target anchor on the target widget
     * @param margin         how much margin we want to keep as a minimum distance between the two anchors
     */
    fun connect(
        constraintFrom: ConstraintAnchor.Type,
        target: ConstraintWidget,
        constraintTo: ConstraintAnchor.Type, margin: Int
    ) {
        if (constraintFrom === ConstraintAnchor.Type.CENTER) {
            // If we have center, we connect instead to the corresponding
            // left/right or top/bottom pairs
            if (constraintTo === ConstraintAnchor.Type.CENTER) {
                val left = getAnchor(ConstraintAnchor.Type.LEFT)
                val right = getAnchor(ConstraintAnchor.Type.RIGHT)
                val top = getAnchor(ConstraintAnchor.Type.TOP)
                val bottom = getAnchor(ConstraintAnchor.Type.BOTTOM)
                var centerX = false
                var centerY = false
                if (left != null && left.isConnected
                    || right != null && right.isConnected
                ) {
                    // don't apply center here
                } else {
                    connect(
                        ConstraintAnchor.Type.LEFT, target,
                        ConstraintAnchor.Type.LEFT, 0
                    )
                    connect(
                        ConstraintAnchor.Type.RIGHT, target,
                        ConstraintAnchor.Type.RIGHT, 0
                    )
                    centerX = true
                }
                if (top != null && top.isConnected
                    || bottom != null && bottom.isConnected
                ) {
                    // don't apply center here
                } else {
                    connect(
                        ConstraintAnchor.Type.TOP, target,
                        ConstraintAnchor.Type.TOP, 0
                    )
                    connect(
                        ConstraintAnchor.Type.BOTTOM, target,
                        ConstraintAnchor.Type.BOTTOM, 0
                    )
                    centerY = true
                }
                if (centerX && centerY) {
                    val center = getAnchor(ConstraintAnchor.Type.CENTER)
                    center!!.connect(target.getAnchor(ConstraintAnchor.Type.CENTER), 0)
                } else if (centerX) {
                    val center = getAnchor(ConstraintAnchor.Type.CENTER_X)
                    center!!.connect(target.getAnchor(ConstraintAnchor.Type.CENTER_X), 0)
                } else if (centerY) {
                    val center = getAnchor(ConstraintAnchor.Type.CENTER_Y)
                    center!!.connect(target.getAnchor(ConstraintAnchor.Type.CENTER_Y), 0)
                }
            } else if (constraintTo === ConstraintAnchor.Type.LEFT
                || constraintTo === ConstraintAnchor.Type.RIGHT
            ) {
                connect(
                    ConstraintAnchor.Type.LEFT, target,
                    constraintTo, 0
                )
                connect(
                    ConstraintAnchor.Type.RIGHT, target,
                    constraintTo, 0
                )
                val center = getAnchor(ConstraintAnchor.Type.CENTER)
                center!!.connect(target.getAnchor(constraintTo), 0)
            } else if (constraintTo === ConstraintAnchor.Type.TOP
                || constraintTo === ConstraintAnchor.Type.BOTTOM
            ) {
                connect(
                    ConstraintAnchor.Type.TOP, target,
                    constraintTo, 0
                )
                connect(
                    ConstraintAnchor.Type.BOTTOM, target,
                    constraintTo, 0
                )
                val center = getAnchor(ConstraintAnchor.Type.CENTER)
                center!!.connect(target.getAnchor(constraintTo), 0)
            }
        } else if (constraintFrom === ConstraintAnchor.Type.CENTER_X
            && (constraintTo === ConstraintAnchor.Type.LEFT
                    || constraintTo === ConstraintAnchor.Type.RIGHT)
        ) {
            val left = getAnchor(ConstraintAnchor.Type.LEFT)
            val targetAnchor = target.getAnchor(constraintTo)
            val right = getAnchor(ConstraintAnchor.Type.RIGHT)
            left!!.connect(targetAnchor, 0)
            right!!.connect(targetAnchor, 0)
            val centerX = getAnchor(ConstraintAnchor.Type.CENTER_X)
            centerX!!.connect(targetAnchor, 0)
        } else if (constraintFrom === ConstraintAnchor.Type.CENTER_Y
            && (constraintTo === ConstraintAnchor.Type.TOP
                    || constraintTo === ConstraintAnchor.Type.BOTTOM)
        ) {
            val targetAnchor = target.getAnchor(constraintTo)
            val top = getAnchor(ConstraintAnchor.Type.TOP)
            top!!.connect(targetAnchor, 0)
            val bottom = getAnchor(ConstraintAnchor.Type.BOTTOM)
            bottom!!.connect(targetAnchor, 0)
            val centerY = getAnchor(ConstraintAnchor.Type.CENTER_Y)
            centerY!!.connect(targetAnchor, 0)
        } else if (constraintFrom === ConstraintAnchor.Type.CENTER_X
            && constraintTo === ConstraintAnchor.Type.CENTER_X
        ) {
            // Center X connection will connect left & right
            val left = getAnchor(ConstraintAnchor.Type.LEFT)
            val leftTarget = target.getAnchor(ConstraintAnchor.Type.LEFT)
            left!!.connect(leftTarget, 0)
            val right = getAnchor(ConstraintAnchor.Type.RIGHT)
            val rightTarget = target.getAnchor(ConstraintAnchor.Type.RIGHT)
            right!!.connect(rightTarget, 0)
            val centerX = getAnchor(ConstraintAnchor.Type.CENTER_X)
            centerX!!.connect(target.getAnchor(constraintTo), 0)
        } else if (constraintFrom === ConstraintAnchor.Type.CENTER_Y
            && constraintTo === ConstraintAnchor.Type.CENTER_Y
        ) {
            // Center Y connection will connect top & bottom.
            val top = getAnchor(ConstraintAnchor.Type.TOP)
            val topTarget = target.getAnchor(ConstraintAnchor.Type.TOP)
            top!!.connect(topTarget, 0)
            val bottom = getAnchor(ConstraintAnchor.Type.BOTTOM)
            val bottomTarget = target.getAnchor(ConstraintAnchor.Type.BOTTOM)
            bottom!!.connect(bottomTarget, 0)
            val centerY = getAnchor(ConstraintAnchor.Type.CENTER_Y)
            centerY!!.connect(target.getAnchor(constraintTo), 0)
        } else {
            val fromAnchor = getAnchor(constraintFrom)
            val toAnchor = target.getAnchor(constraintTo)
            if (fromAnchor!!.isValidConnection(toAnchor)) {
                // make sure that the baseline takes precedence over top/bottom
                // and reversely, reset the baseline if we are connecting top/bottom
                if (constraintFrom === ConstraintAnchor.Type.BASELINE) {
                    val top = getAnchor(ConstraintAnchor.Type.TOP)
                    val bottom = getAnchor(ConstraintAnchor.Type.BOTTOM)
                    top?.reset()
                    bottom?.reset()
                } else if (constraintFrom === ConstraintAnchor.Type.TOP
                    || constraintFrom === ConstraintAnchor.Type.BOTTOM
                ) {
                    val baseline = getAnchor(ConstraintAnchor.Type.BASELINE)
                    baseline?.reset()
                    val center = getAnchor(ConstraintAnchor.Type.CENTER)
                    if (center!!.target != toAnchor) {
                        center.reset()
                    }
                    val opposite = getAnchor(constraintFrom)!!.opposite
                    val centerY = getAnchor(ConstraintAnchor.Type.CENTER_Y)
                    if (centerY!!.isConnected) {
                        opposite!!.reset()
                        centerY.reset()
                    } else {
                        if (AUTOTAG_CENTER) {
                            // let's see if we need to mark center_y as connected
                            if (opposite!!.isConnected && opposite.target!!.owner
                                === toAnchor!!.owner
                            ) {
                                val targetCenterY = toAnchor!!.owner.getAnchor(
                                    ConstraintAnchor.Type.CENTER_Y
                                )
                                centerY.connect(targetCenterY, 0)
                            }
                        }
                    }
                } else if (constraintFrom === ConstraintAnchor.Type.LEFT
                    || constraintFrom === ConstraintAnchor.Type.RIGHT
                ) {
                    val center = getAnchor(ConstraintAnchor.Type.CENTER)
                    if (center!!.target != toAnchor) {
                        center.reset()
                    }
                    val opposite = getAnchor(constraintFrom)!!.opposite
                    val centerX = getAnchor(ConstraintAnchor.Type.CENTER_X)
                    if (centerX!!.isConnected) {
                        opposite!!.reset()
                        centerX.reset()
                    } else {
                        if (AUTOTAG_CENTER) {
                            // let's see if we need to mark center_x as connected
                            if (opposite!!.isConnected && opposite.target!!.owner
                                === toAnchor!!.owner
                            ) {
                                val targetCenterX = toAnchor!!.owner.getAnchor(
                                    ConstraintAnchor.Type.CENTER_X
                                )
                                centerX.connect(targetCenterX, 0)
                            }
                        }
                    }
                }
                fromAnchor.connect(toAnchor, margin)
            }
        }
    }

    /**
     * Reset all the constraints set on this widget
     */
    fun resetAllConstraints() {
        resetAnchors()
        verticalBiasPercent = DEFAULT_BIAS
        horizontalBiasPercent = DEFAULT_BIAS
    }

    /**
     * Reset the given anchor
     *
     * @param anchor the anchor we want to reset
     */
    fun resetAnchor(anchor: ConstraintAnchor) {
        if (parent != null) {
            if (parent is ConstraintWidgetContainer) {
                val parent = parent as ConstraintWidgetContainer?
                if (parent!!.handlesInternalConstraints()) {
                    return
                }
            }
        }
        val left = getAnchor(ConstraintAnchor.Type.LEFT)
        val right = getAnchor(ConstraintAnchor.Type.RIGHT)
        val top = getAnchor(ConstraintAnchor.Type.TOP)
        val bottom = getAnchor(ConstraintAnchor.Type.BOTTOM)
        val center = getAnchor(ConstraintAnchor.Type.CENTER)
        val centerX = getAnchor(ConstraintAnchor.Type.CENTER_X)
        val centerY = getAnchor(ConstraintAnchor.Type.CENTER_Y)
        if (anchor == center) {
            if (left!!.isConnected && right!!.isConnected
                && left.target == right.target
            ) {
                left.reset()
                right.reset()
            }
            if (top!!.isConnected && bottom!!.isConnected
                && top.target == bottom.target
            ) {
                top.reset()
                bottom.reset()
            }
            horizontalBiasPercent = 0.5f
            verticalBiasPercent = 0.5f
        } else if (anchor == centerX) {
            if (left!!.isConnected && right!!.isConnected
                && left.target!!.owner === right.target!!.owner
            ) {
                left.reset()
                right.reset()
            }
            horizontalBiasPercent = 0.5f
        } else if (anchor == centerY) {
            if (top!!.isConnected && bottom!!.isConnected
                && top.target!!.owner === bottom.target!!.owner
            ) {
                top.reset()
                bottom.reset()
            }
            verticalBiasPercent = 0.5f
        } else if (anchor == left || anchor == right) {
            if (left!!.isConnected && left.target == right!!.target) {
                center!!.reset()
            }
        } else if (anchor == top || anchor == bottom) {
            if (top!!.isConnected && top.target == bottom!!.target) {
                center!!.reset()
            }
        }
        anchor.reset()
    }

    /**
     * Reset all connections
     */
    fun resetAnchors() {
        val parent = parent
        if (parent != null && parent is ConstraintWidgetContainer) {
            val parentContainer = parent as ConstraintWidgetContainer?
            if (parentContainer!!.handlesInternalConstraints()) {
                return
            }
        }
        var i = 0
        val mAnchorsSize = anchors.size
        while (i < mAnchorsSize) {
            val anchor = anchors[i]
            anchor!!.reset()
            i++
        }
    }

    /**
     * Given a type of anchor, returns the corresponding anchor.
     *
     * @param anchorType type of the anchor (LEFT, TOP, RIGHT, BOTTOM, BASELINE, CENTER_X, CENTER_Y)
     * @return the matching anchor
     */
    open fun getAnchor(anchorType: ConstraintAnchor.Type): ConstraintAnchor? {
        return when (anchorType) {
            ConstraintAnchor.Type.LEFT -> {
                mLeft
            }
            ConstraintAnchor.Type.TOP -> {
                mTop
            }
            ConstraintAnchor.Type.RIGHT -> {
                mRight
            }
            ConstraintAnchor.Type.BOTTOM -> {
                mBottom
            }
            ConstraintAnchor.Type.BASELINE -> {
                mBaseline
            }
            ConstraintAnchor.Type.CENTER_X -> {
                mCenterX
            }
            ConstraintAnchor.Type.CENTER_Y -> {
                mCenterY
            }
            ConstraintAnchor.Type.CENTER -> {
                mCenter
            }
            ConstraintAnchor.Type.NONE -> null
        }
        throw AssertionError(anchorType.name)
    }
    /**
     * Accessor for the horizontal dimension behaviour
     *
     * @return dimension behaviour
     */
    /**
     * Set the widget's behaviour for the horizontal dimension
     *
     * @param behaviour the horizontal dimension's behaviour
     */
    var horizontalDimensionBehaviour: DimensionBehaviour
        get() = mListDimensionBehaviors[DIMENSION_HORIZONTAL]
        set(behaviour) {
            mListDimensionBehaviors[DIMENSION_HORIZONTAL] = behaviour
        }
    /**
     * Accessor for the vertical dimension behaviour
     *
     * @return dimension behaviour
     */
    /**
     * Set the widget's behaviour for the vertical dimension
     *
     * @param behaviour the vertical dimension's behaviour
     */
    var verticalDimensionBehaviour: DimensionBehaviour
        get() = mListDimensionBehaviors[DIMENSION_VERTICAL]
        set(behaviour) {
            mListDimensionBehaviors[DIMENSION_VERTICAL] = behaviour
        }

    /**
     * Get the widget's [DimensionBehaviour] in an specific orientation.
     *
     * @param orientation
     * @return The [DimensionBehaviour] of the widget.
     */
    fun getDimensionBehaviour(orientation: Int): DimensionBehaviour? {
        return if (orientation == HORIZONTAL) {
            horizontalDimensionBehaviour
        } else if (orientation == VERTICAL) {
            verticalDimensionBehaviour
        } else {
            null
        }
    }

    /**
     * Test if you are in a Horizontal chain
     *
     * @return true if in a horizontal chain
     */
    val isInHorizontalChain: Boolean
        get() = if (mLeft!!.target != null && mLeft!!.target!!.target === mLeft
            || mRight!!.target != null && mRight!!.target!!.target === mRight
        ) {
            true
        } else false

    /**
     * Return the previous chain member if one exists
     *
     * @param orientation HORIZONTAL or VERTICAL
     * @return the previous chain member or null if we are the first chain element
     */
    fun getPreviousChainMember(orientation: Int): ConstraintWidget? {
        if (orientation == HORIZONTAL) {
            if (mLeft!!.target != null && mLeft!!.target!!.target === mLeft) {
                return mLeft!!.target!!.owner
            }
        } else if (orientation == VERTICAL) {
            if (mTop.target != null && mTop.target!!.target === mTop) {
                return mTop.target!!.owner
            }
        }
        return null
    }

    /**
     * Return the next chain member if one exists
     *
     * @param orientation HORIZONTAL or VERTICAL
     * @return the next chain member or null if we are the last chain element
     */
    fun getNextChainMember(orientation: Int): ConstraintWidget? {
        if (orientation == HORIZONTAL) {
            if (mRight!!.target != null && mRight!!.target!!.target === mRight) {
                return mRight!!.target!!.owner
            }
        } else if (orientation == VERTICAL) {
            if (mBottom.target != null && mBottom.target!!.target === mBottom) {
                return mBottom.target!!.owner
            }
        }
        return null
    }

    /**
     * if in a horizontal chain return the left most widget in the chain.
     *
     * @return left most widget in chain or null
     */
    val horizontalChainControlWidget: ConstraintWidget?
        get() {
            var found: ConstraintWidget? = null
            if (isInHorizontalChain) {
                var tmp: ConstraintWidget? = this
                while (found == null && tmp != null) {
                    val anchor = tmp.getAnchor(ConstraintAnchor.Type.LEFT)
                    val targetOwner = anchor?.target
                    val target = targetOwner?.owner
                    if (target === parent) {
                        found = tmp
                        break
                    }
                    val targetAnchor = if (target == null) null else target.getAnchor(ConstraintAnchor.Type.RIGHT)!!.target
                    if (targetAnchor != null && targetAnchor.owner !== tmp) {
                        found = tmp
                    } else {
                        tmp = target
                    }
                }
            }
            return found
        }

    /**
     * Test if you are in a vertical chain
     *
     * @return true if in a vertical chain
     */
    val isInVerticalChain: Boolean
        get() = if (mTop.target != null && mTop.target!!.target === mTop
            || mBottom.target != null && mBottom.target!!.target === mBottom
        ) {
            true
        } else false

    /**
     * if in a vertical chain return the top most widget in the chain.
     *
     * @return top most widget in chain or null
     */
    val verticalChainControlWidget: ConstraintWidget?
        get() {
            var found: ConstraintWidget? = null
            if (isInVerticalChain) {
                var tmp: ConstraintWidget? = this
                while (found == null && tmp != null) {
                    val anchor = tmp.getAnchor(ConstraintAnchor.Type.TOP)
                    val targetOwner = anchor?.target
                    val target = targetOwner?.owner
                    if (target === parent) {
                        found = tmp
                        break
                    }
                    val targetAnchor = if (target == null) null else target.getAnchor(ConstraintAnchor.Type.BOTTOM)!!.target
                    if (targetAnchor != null && targetAnchor.owner !== tmp) {
                        found = tmp
                    } else {
                        tmp = target
                    }
                }
            }
            return found
        }

    /**
     * Determine if the widget is the first element of a chain in a given orientation.
     *
     * @param orientation Either [.HORIZONTAL] or [.VERTICAL]
     * @return if the widget is the head of a chain
     */
    private fun isChainHead(orientation: Int): Boolean {
        val offset = orientation * 2
        return ((mListAnchors[offset]!!.target != null
                && mListAnchors[offset]!!.target!!.target !== mListAnchors[offset])
                && (mListAnchors[offset + 1]!!.target != null
                && mListAnchors[offset + 1]!!.target!!.target === mListAnchors[offset + 1]))
    }
    /*-----------------------------------------------------------------------*/ // Constraints
    /*-----------------------------------------------------------------------*/
    /**
     * Add this widget to the solver
     *
     * @param system the solver we want to add the widget to
     * @param optimize true if [Optimizer.OPTIMIZATION_GRAPH] is on
     */
    open fun addToSolver(system: LinearSystem, optimize: Boolean) {
        if (LinearSystem.FULL_DEBUG) {
            println("\n----------------------------------------------")
            println("-- adding $debugName to the solver")
            if (isInVirtualLayout) {
                println("-- note: is in virtual layout")
            }
            println("----------------------------------------------\n")
        }
        val left = system.createObjectVariable(mLeft)
        val right = system.createObjectVariable(mRight)
        val top = system.createObjectVariable(mTop)
        val bottom = system.createObjectVariable(mBottom)
        val baseline = system.createObjectVariable(mBaseline)
        var horizontalParentWrapContent = false
        var verticalParentWrapContent = false
        if (parent != null) {
            horizontalParentWrapContent = if (parent != null) parent!!.mListDimensionBehaviors[DIMENSION_HORIZONTAL] == DimensionBehaviour.WRAP_CONTENT else false
            verticalParentWrapContent = if (parent != null) parent!!.mListDimensionBehaviors[DIMENSION_VERTICAL] == DimensionBehaviour.WRAP_CONTENT else false
            when (mWrapBehaviorInParent) {
                WRAP_BEHAVIOR_SKIPPED -> {
                    horizontalParentWrapContent = false
                    verticalParentWrapContent = false
                }
                WRAP_BEHAVIOR_HORIZONTAL_ONLY -> {
                    verticalParentWrapContent = false
                }
                WRAP_BEHAVIOR_VERTICAL_ONLY -> {
                    horizontalParentWrapContent = false
                }
            }
        }
        if (visibility == GONE && !hasDependencies()
            && !mIsInBarrier[HORIZONTAL] && !mIsInBarrier[VERTICAL]
        ) {
            return
        }
        if (resolvedHorizontal || resolvedVertical) {
            if (LinearSystem.FULL_DEBUG) {
                println("\n----------------------------------------------")
                println(
                    "-- setting " + debugName
                            + " to " + mX + ", " + mY + " " + mWidth + " x " + mHeight
                )
                println("----------------------------------------------\n")
            }
            // For now apply all, but that won't work for wrap/wrap layouts.
            if (resolvedHorizontal) {
                system.addEquality(left, mX)
                system.addEquality(right, mX + mWidth)
                if (horizontalParentWrapContent && parent != null) {
                    if (OPTIMIZE_WRAP_ON_RESOLVED) {
                        val container = parent as ConstraintWidgetContainer
                        mLeft?.let { container.addHorizontalWrapMinVariable(it) }
                        mRight?.let { container.addHorizontalWrapMaxVariable(it) }
                    } else {
                        val wrapStrength = SolverVariable.STRENGTH_EQUALITY
                        system.addGreaterThan(system.createObjectVariable(parent!!.mRight), right, 0, wrapStrength)
                    }
                }
            }
            if (resolvedVertical) {
                system.addEquality(top, mY)
                system.addEquality(bottom, mY + mHeight)
                if (mBaseline!!.hasDependents()) {
                    system.addEquality(baseline, mY + mBaselineDistance)
                }
                if (verticalParentWrapContent && parent != null) {
                    if (OPTIMIZE_WRAP_ON_RESOLVED) {
                        val container = parent as ConstraintWidgetContainer
                        container.addVerticalWrapMinVariable(mTop)
                        container.addVerticalWrapMaxVariable(mBottom)
                    } else {
                        val wrapStrength = SolverVariable.STRENGTH_EQUALITY
                        system.addGreaterThan(system.createObjectVariable(parent!!.mBottom), bottom, 0, wrapStrength)
                    }
                }
            }
            if (resolvedHorizontal && resolvedVertical) {
                resolvedHorizontal = false
                resolvedVertical = false
                if (LinearSystem.FULL_DEBUG) {
                    println("\n----------------------------------------------")
                    println("-- setting COMPLETED for $debugName")
                    println("----------------------------------------------\n")
                }
                return
            }
        }
        LinearSystem.metrics?.let {
            it.widgets++
        }
        if (LinearSystem.FULL_DEBUG) {
            if (optimize && horizontalRun != null && verticalRun != null) {
                println("-- horizontal run : " + horizontalRun!!.start + " : " + horizontalRun!!.end)
                println("-- vertical run : " + verticalRun!!.start + " : " + verticalRun!!.end)
            }
        }
        if (optimize && horizontalRun != null && verticalRun != null && horizontalRun!!.start.resolved && horizontalRun!!.end.resolved
            && verticalRun!!.start.resolved && verticalRun!!.end.resolved
        ) {
            LinearSystem.metrics?.let {
                it.graphSolved++
            }
            system.addEquality(left, horizontalRun!!.start.value)
            system.addEquality(right, horizontalRun!!.end.value)
            system.addEquality(top, verticalRun!!.start.value)
            system.addEquality(bottom, verticalRun!!.end.value)
            system.addEquality(baseline, verticalRun!!.baseline.value)
            if (parent != null) {
                if (horizontalParentWrapContent && isTerminalWidget[HORIZONTAL] && !isInHorizontalChain) {
                    val parentMax = system.createObjectVariable(parent!!.mRight)
                    system.addGreaterThan(parentMax, right, 0, SolverVariable.STRENGTH_FIXED)
                }
                if (verticalParentWrapContent && isTerminalWidget[VERTICAL] && !isInVerticalChain) {
                    val parentMax = system.createObjectVariable(parent!!.mBottom)
                    system.addGreaterThan(parentMax, bottom, 0, SolverVariable.STRENGTH_FIXED)
                }
            }
            resolvedHorizontal = false
            resolvedVertical = false
            return  // we are done here
        }
        LinearSystem.metrics?.let {
            it.linearSolved++
        }
        var inHorizontalChain = false
        var inVerticalChain = false
        if (parent != null) {
            // Add this widget to a horizontal chain if it is the Head of it.
            inHorizontalChain = if (isChainHead(HORIZONTAL)) {
                (parent as ConstraintWidgetContainer).addChain(this, HORIZONTAL)
                true
            } else {
                isInHorizontalChain
            }

            // Add this widget to a vertical chain if it is the Head of it.
            inVerticalChain = if (isChainHead(VERTICAL)) {
                (parent as ConstraintWidgetContainer).addChain(this, VERTICAL)
                true
            } else {
                isInVerticalChain
            }
            if (!inHorizontalChain && horizontalParentWrapContent && visibility != GONE && mLeft!!.target == null && mRight!!.target == null) {
                if (LinearSystem.FULL_DEBUG) {
                    println("<>1 ADDING H WRAP GREATER FOR $debugName")
                }
                val parentRight = system.createObjectVariable(parent!!.mRight)
                system.addGreaterThan(parentRight, right, 0, SolverVariable.STRENGTH_LOW)
            }
            if (!inVerticalChain && verticalParentWrapContent && visibility != GONE && mTop.target == null && mBottom.target == null && mBaseline == null) {
                if (LinearSystem.FULL_DEBUG) {
                    println("<>1 ADDING V WRAP GREATER FOR $debugName")
                }
                val parentBottom = system.createObjectVariable(parent!!.mBottom)
                system.addGreaterThan(parentBottom, bottom, 0, SolverVariable.STRENGTH_LOW)
            }
        }
        var width = mWidth
        if (width < mMinWidth) {
            width = mMinWidth
        }
        var height = mHeight
        if (height < mMinHeight) {
            height = mMinHeight
        }

        // Dimensions can be either fixed (a given value) or dependent on the solver if set to MATCH_CONSTRAINT
        val horizontalDimensionFixed = mListDimensionBehaviors[DIMENSION_HORIZONTAL] != DimensionBehaviour.MATCH_CONSTRAINT
        val verticalDimensionFixed = mListDimensionBehaviors[DIMENSION_VERTICAL] != DimensionBehaviour.MATCH_CONSTRAINT

        // We evaluate the dimension ratio here as the connections can change.
        // TODO: have a validation pass after connection instead
        var useRatio = false
        mResolvedDimensionRatioSide = dimensionRatioSide
        mResolvedDimensionRatio = dimensionRatio
        var matchConstraintDefaultWidth = mMatchConstraintDefaultWidth
        var matchConstraintDefaultHeight = mMatchConstraintDefaultHeight
        if (dimensionRatio > 0 && visibility != GONE) {
            useRatio = true
            if (mListDimensionBehaviors[DIMENSION_HORIZONTAL] == DimensionBehaviour.MATCH_CONSTRAINT
                && matchConstraintDefaultWidth == MATCH_CONSTRAINT_SPREAD
            ) {
                matchConstraintDefaultWidth = MATCH_CONSTRAINT_RATIO
            }
            if (mListDimensionBehaviors[DIMENSION_VERTICAL] == DimensionBehaviour.MATCH_CONSTRAINT
                && matchConstraintDefaultHeight == MATCH_CONSTRAINT_SPREAD
            ) {
                matchConstraintDefaultHeight = MATCH_CONSTRAINT_RATIO
            }
            if (mListDimensionBehaviors[DIMENSION_HORIZONTAL] == DimensionBehaviour.MATCH_CONSTRAINT && mListDimensionBehaviors[DIMENSION_VERTICAL] == DimensionBehaviour.MATCH_CONSTRAINT && matchConstraintDefaultWidth == MATCH_CONSTRAINT_RATIO && matchConstraintDefaultHeight == MATCH_CONSTRAINT_RATIO) {
                setupDimensionRatio(horizontalParentWrapContent, verticalParentWrapContent, horizontalDimensionFixed, verticalDimensionFixed)
            } else if (mListDimensionBehaviors[DIMENSION_HORIZONTAL] == DimensionBehaviour.MATCH_CONSTRAINT
                && matchConstraintDefaultWidth == MATCH_CONSTRAINT_RATIO
            ) {
                mResolvedDimensionRatioSide = HORIZONTAL
                width = (mResolvedDimensionRatio * mHeight).toInt()
                if (mListDimensionBehaviors[DIMENSION_VERTICAL] != DimensionBehaviour.MATCH_CONSTRAINT) {
                    matchConstraintDefaultWidth = MATCH_CONSTRAINT_RATIO_RESOLVED
                    useRatio = false
                }
            } else if (mListDimensionBehaviors[DIMENSION_VERTICAL] == DimensionBehaviour.MATCH_CONSTRAINT
                && matchConstraintDefaultHeight == MATCH_CONSTRAINT_RATIO
            ) {
                mResolvedDimensionRatioSide = VERTICAL
                if (dimensionRatioSide == UNKNOWN) {
                    // need to reverse the ratio as the parsing is done in horizontal mode
                    mResolvedDimensionRatio = 1 / mResolvedDimensionRatio
                }
                height = (mResolvedDimensionRatio * mWidth).toInt()
                if (mListDimensionBehaviors[DIMENSION_HORIZONTAL] != DimensionBehaviour.MATCH_CONSTRAINT) {
                    matchConstraintDefaultHeight = MATCH_CONSTRAINT_RATIO_RESOLVED
                    useRatio = false
                }
            }
        }
        mResolvedMatchConstraintDefault[HORIZONTAL] = matchConstraintDefaultWidth
        mResolvedMatchConstraintDefault[VERTICAL] = matchConstraintDefaultHeight
        mResolvedHasRatio = useRatio
        val useHorizontalRatio = useRatio && (mResolvedDimensionRatioSide == HORIZONTAL
                || mResolvedDimensionRatioSide == UNKNOWN)
        val useVerticalRatio = useRatio && (mResolvedDimensionRatioSide == VERTICAL
                || mResolvedDimensionRatioSide == UNKNOWN)

        // Horizontal resolution
        var wrapContent = (mListDimensionBehaviors[DIMENSION_HORIZONTAL] == DimensionBehaviour.WRAP_CONTENT
                && this is ConstraintWidgetContainer)
        if (wrapContent) {
            width = 0
        }
        var applyPosition = true
        if (mCenter.isConnected) {
            applyPosition = false
        }
        val isInHorizontalBarrier = mIsInBarrier[HORIZONTAL]
        val isInVerticalBarrier = mIsInBarrier[VERTICAL]
        if (mHorizontalResolution != DIRECT && !resolvedHorizontal) {
            if (!optimize || !(horizontalRun != null && horizontalRun!!.start.resolved && horizontalRun!!.end.resolved)) {
                val parentMax = if (parent != null) system.createObjectVariable(parent!!.mRight) else null
                val parentMin = if (parent != null) system.createObjectVariable(parent!!.mLeft) else null
                applyConstraints(
                    system,
                    true,
                    horizontalParentWrapContent,
                    verticalParentWrapContent,
                    isTerminalWidget[HORIZONTAL],
                    parentMin,
                    parentMax,
                    mListDimensionBehaviors[DIMENSION_HORIZONTAL],
                    wrapContent,
                    mLeft,
                    mRight,
                    mX,
                    width,
                    mMinWidth,
                    mMaxDimension[HORIZONTAL],
                    horizontalBiasPercent,
                    useHorizontalRatio,
                    mListDimensionBehaviors[VERTICAL] == DimensionBehaviour.MATCH_CONSTRAINT,
                    inHorizontalChain,
                    inVerticalChain,
                    isInHorizontalBarrier,
                    matchConstraintDefaultWidth,
                    matchConstraintDefaultHeight,
                    mMatchConstraintMinWidth,
                    mMatchConstraintMaxWidth,
                    mMatchConstraintPercentWidth,
                    applyPosition
                )
            } else if (optimize) {
                system.addEquality(left, horizontalRun!!.start.value)
                system.addEquality(right, horizontalRun!!.end.value)
                if (parent != null) {
                    if (horizontalParentWrapContent && isTerminalWidget[HORIZONTAL] && !isInHorizontalChain) {
                        if (LinearSystem.FULL_DEBUG) {
                            println("<>2 ADDING H WRAP GREATER FOR $debugName")
                        }
                        val parentMax = system.createObjectVariable(parent!!.mRight)
                        system.addGreaterThan(parentMax, right, 0, SolverVariable.STRENGTH_FIXED)
                    }
                }
            }
        }
        var applyVerticalConstraints = true
        if (optimize && verticalRun != null && verticalRun!!.start.resolved && verticalRun!!.end.resolved) {
            system.addEquality(top, verticalRun!!.start.value)
            system.addEquality(bottom, verticalRun!!.end.value)
            system.addEquality(baseline, verticalRun!!.baseline.value)
            if (parent != null) {
                if (!inVerticalChain && verticalParentWrapContent && isTerminalWidget[VERTICAL]) {
                    if (LinearSystem.FULL_DEBUG) {
                        println("<>2 ADDING V WRAP GREATER FOR $debugName")
                    }
                    val parentMax = system.createObjectVariable(parent!!.mBottom)
                    system.addGreaterThan(parentMax, bottom, 0, SolverVariable.STRENGTH_FIXED)
                }
            }
            applyVerticalConstraints = false
        }
        if (mVerticalResolution == DIRECT) {
            if (LinearSystem.FULL_DEBUG) {
                println("\n----------------------------------------------")
                println("-- DONE adding $debugName to the solver")
                println("-- SKIP VERTICAL RESOLUTION")
                println("----------------------------------------------\n")
            }
            applyVerticalConstraints = false
        }
        if (applyVerticalConstraints && !resolvedVertical) {
            // Vertical Resolution
            wrapContent = (mListDimensionBehaviors[DIMENSION_VERTICAL] == DimensionBehaviour.WRAP_CONTENT
                    && this is ConstraintWidgetContainer)
            if (wrapContent) {
                height = 0
            }
            val parentMax = if (parent != null) system.createObjectVariable(parent!!.mBottom) else null
            val parentMin = if (parent != null) system.createObjectVariable(parent!!.mTop) else null
            if (mBaselineDistance > 0 || visibility == GONE) {
                // if we are GONE we might still have to deal with baseline, even if our baseline distance would be zero
                if (mBaseline?.target != null) {
                    system.addEquality(baseline, top, baselineDistance, SolverVariable.STRENGTH_FIXED)
                    val baselineTarget = system.createObjectVariable(mBaseline!!.target)
                    val baselineMargin = mBaseline!!.margin
                    system.addEquality(baseline, baselineTarget, baselineMargin, SolverVariable.STRENGTH_FIXED)
                    applyPosition = false
                    if (verticalParentWrapContent) {
                        if (LinearSystem.FULL_DEBUG) {
                            println("<>3 ADDING V WRAP GREATER FOR $debugName")
                        }
                        val end = system.createObjectVariable(mBottom)
                        val wrapStrength = SolverVariable.STRENGTH_EQUALITY
                        system.addGreaterThan(parentMax, end, 0, wrapStrength)
                    }
                } else if (visibility == GONE) {
                    // TODO: use the constraints graph here to help
                    system.addEquality(baseline, top, mBaseline!!.margin, SolverVariable.STRENGTH_FIXED)
                } else {
                    system.addEquality(baseline, top, baselineDistance, SolverVariable.STRENGTH_FIXED)
                }
            }
            applyConstraints(
                system,
                false,
                verticalParentWrapContent,
                horizontalParentWrapContent,
                isTerminalWidget[VERTICAL],
                parentMin,
                parentMax,
                mListDimensionBehaviors[DIMENSION_VERTICAL],
                wrapContent,
                mTop,
                mBottom,
                mY,
                height,
                mMinHeight,
                mMaxDimension[VERTICAL],
                verticalBiasPercent,
                useVerticalRatio,
                mListDimensionBehaviors[HORIZONTAL] == DimensionBehaviour.MATCH_CONSTRAINT,
                inVerticalChain,
                inHorizontalChain,
                isInVerticalBarrier,
                matchConstraintDefaultHeight,
                matchConstraintDefaultWidth,
                mMatchConstraintMinHeight,
                mMatchConstraintMaxHeight,
                mMatchConstraintPercentHeight,
                applyPosition
            )
        }
        if (useRatio) {
            val strength = SolverVariable.STRENGTH_FIXED
            if (mResolvedDimensionRatioSide == VERTICAL) {
                system.addRatio(bottom, top, right, left, mResolvedDimensionRatio, strength)
            } else {
                system.addRatio(right, left, bottom, top, mResolvedDimensionRatio, strength)
            }
        }
        if (mCenter.isConnected) {
            system.addCenterPoint(this, mCenter.target!!.owner, radians((mCircleConstraintAngle + 90).toDouble()).toFloat(), mCenter.margin)
        }
        if (LinearSystem.FULL_DEBUG) {
            println("\n----------------------------------------------")
            println("-- DONE adding $debugName to the solver")
            println("----------------------------------------------\n")
        }
        resolvedHorizontal = false
        resolvedVertical = false
    }

    /**
     * Used to select which widgets should be added to the solver first
     * @return
     */
    fun addFirst(): Boolean {
        return this is VirtualLayout || this is Guideline
    }

    /**
     * Resolves the dimension ratio parameters
     * (mResolvedDimensionRatioSide & mDimensionRatio)
     *
     * @param hParentWrapContent       true if parent is in wrap content horizontally
     * @param vParentWrapContent       true if parent is in wrap content vertically
     * @param horizontalDimensionFixed true if this widget horizontal dimension is fixed
     * @param verticalDimensionFixed   true if this widget vertical dimension is fixed
     */
    fun setupDimensionRatio(hParentWrapContent: Boolean, vParentWrapContent: Boolean, horizontalDimensionFixed: Boolean, verticalDimensionFixed: Boolean) {
        if (mResolvedDimensionRatioSide == UNKNOWN) {
            if (horizontalDimensionFixed && !verticalDimensionFixed) {
                mResolvedDimensionRatioSide = HORIZONTAL
            } else if (!horizontalDimensionFixed && verticalDimensionFixed) {
                mResolvedDimensionRatioSide = VERTICAL
                if (dimensionRatioSide == UNKNOWN) {
                    // need to reverse the ratio as the parsing is done in horizontal mode
                    mResolvedDimensionRatio = 1 / mResolvedDimensionRatio
                }
            }
        }
        if (mResolvedDimensionRatioSide == HORIZONTAL && !(mTop.isConnected && mBottom.isConnected)) {
            mResolvedDimensionRatioSide = VERTICAL
        } else if (mResolvedDimensionRatioSide == VERTICAL && !(mLeft!!.isConnected && mRight!!.isConnected)) {
            mResolvedDimensionRatioSide = HORIZONTAL
        }

        // if dimension is still unknown... check parentWrap
        if (mResolvedDimensionRatioSide == UNKNOWN) {
            if (!(mTop.isConnected && mBottom.isConnected
                        && mLeft!!.isConnected && mRight!!.isConnected)
            ) {
                // only do that if not all connections are set
                if (mTop.isConnected && mBottom.isConnected) {
                    mResolvedDimensionRatioSide = HORIZONTAL
                } else if (mLeft!!.isConnected && mRight!!.isConnected) {
                    mResolvedDimensionRatio = 1 / mResolvedDimensionRatio
                    mResolvedDimensionRatioSide = VERTICAL
                }
            }
        }
        if (false && mResolvedDimensionRatioSide == UNKNOWN) {
            if (hParentWrapContent && !vParentWrapContent) {
                mResolvedDimensionRatioSide = HORIZONTAL
            } else if (!hParentWrapContent && vParentWrapContent) {
                mResolvedDimensionRatio = 1 / mResolvedDimensionRatio
                mResolvedDimensionRatioSide = VERTICAL
            }
        }
        if (mResolvedDimensionRatioSide == UNKNOWN) {
            if (mMatchConstraintMinWidth > 0 && mMatchConstraintMinHeight == 0) {
                mResolvedDimensionRatioSide = HORIZONTAL
            } else if (mMatchConstraintMinWidth == 0 && mMatchConstraintMinHeight > 0) {
                mResolvedDimensionRatio = 1 / mResolvedDimensionRatio
                mResolvedDimensionRatioSide = VERTICAL
            }
        }
        if (false && mResolvedDimensionRatioSide == UNKNOWN && hParentWrapContent && vParentWrapContent) {
            mResolvedDimensionRatio = 1 / mResolvedDimensionRatio
            mResolvedDimensionRatioSide = VERTICAL
        }
    }

    /**
     * Apply the constraints in the system depending on the existing anchors, in one dimension
     * @param system                the linear system we are adding constraints to
     * @param parentWrapContent
     * @param isTerminal
     * @param parentMax
     * @param dimensionBehaviour
     * @param wrapContent           is the widget trying to wrap its content (i.e. its size will depends on its content)
     * @param beginAnchor           the first anchor
     * @param endAnchor             the second anchor
     * @param beginPosition         the original position of the anchor
     * @param dimension             the dimension
     * @param maxDimension
     * @param oppositeVariable
     * @param matchPercentDimension the percentage relative to the parent, applied if in match constraint and percent mode
     * @param applyPosition
     */
    private fun applyConstraints(
        system: LinearSystem, isHorizontal: Boolean,
        parentWrapContent: Boolean, oppositeParentWrapContent: Boolean,
        isTerminal: Boolean, parentMin: SolverVariable?, parentMax: SolverVariable?,
        dimensionBehaviour: DimensionBehaviour, wrapContent: Boolean,
        beginAnchor: ConstraintAnchor?, endAnchor: ConstraintAnchor?,
        beginPosition: Int, dimension: Int, minDimension: Int,
        maxDimension: Int, bias: Float, useRatio: Boolean, oppositeVariable: Boolean, inChain: Boolean,
        oppositeInChain: Boolean, inBarrier: Boolean, matchConstraintDefault: Int,
        oppositeMatchConstraintDefault: Int,
        matchMinDimension: Int, matchMaxDimension: Int, matchPercentDimension: Float, applyPosition: Boolean
    ) {
        var parentWrapContent = parentWrapContent
        var isTerminal = isTerminal
        var dimension = dimension
        var matchConstraintDefault = matchConstraintDefault
        var matchMinDimension = matchMinDimension
        var matchMaxDimension = matchMaxDimension
        val begin = system.createObjectVariable(beginAnchor)
        val end = system.createObjectVariable(endAnchor)
        val beginTarget = system.createObjectVariable(beginAnchor!!.target)
        val endTarget = system.createObjectVariable(endAnchor!!.target)
        LinearSystem.metrics?.let {
            it.nonresolvedWidgets++
        }
        val isBeginConnected = beginAnchor.isConnected
        val isEndConnected = endAnchor.isConnected
        val isCenterConnected = mCenter.isConnected
        var variableSize = false
        var numConnections = 0
        if (isBeginConnected) {
            numConnections++
        }
        if (isEndConnected) {
            numConnections++
        }
        if (isCenterConnected) {
            numConnections++
        }
        if (useRatio) {
            matchConstraintDefault = MATCH_CONSTRAINT_RATIO
        }
        variableSize = when (dimensionBehaviour) {
            DimensionBehaviour.FIXED -> {
                false
            }
            DimensionBehaviour.WRAP_CONTENT -> {
                false
            }
            DimensionBehaviour.MATCH_PARENT -> {
                false
            }
            DimensionBehaviour.MATCH_CONSTRAINT -> {
                matchConstraintDefault != MATCH_CONSTRAINT_RATIO_RESOLVED
            }
        }
        if (mWidthOverride != -1 && isHorizontal) {
            if (LinearSystem.FULL_DEBUG) {
                println("OVERRIDE WIDTH to $mWidthOverride")
            }
            variableSize = false
            dimension = mWidthOverride
            mWidthOverride = -1
        }
        if (mHeightOverride != -1 && !isHorizontal) {
            if (LinearSystem.FULL_DEBUG) {
                println("OVERRIDE HEIGHT to $mHeightOverride")
            }
            variableSize = false
            dimension = mHeightOverride
            mHeightOverride = -1
        }
        if (visibility == GONE) {
            dimension = 0
            variableSize = false
        }

        // First apply starting direct connections (more solver-friendly)
        if (applyPosition) {
            if (!isBeginConnected && !isEndConnected && !isCenterConnected) {
                system.addEquality(begin, beginPosition)
            } else if (isBeginConnected && !isEndConnected) {
                system.addEquality(begin, beginTarget, beginAnchor.margin, SolverVariable.STRENGTH_FIXED)
            }
        }

        // Then apply the dimension
        if (!variableSize) {
            if (wrapContent) {
                system.addEquality(end, begin, 0, SolverVariable.STRENGTH_HIGH)
                if (minDimension > 0) {
                    system.addGreaterThan(end, begin, minDimension, SolverVariable.STRENGTH_FIXED)
                }
                if (maxDimension < Int.MAX_VALUE) {
                    system.addLowerThan(end, begin, maxDimension, SolverVariable.STRENGTH_FIXED)
                }
            } else {
                system.addEquality(end, begin, dimension, SolverVariable.STRENGTH_FIXED)
            }
        } else {
            if (numConnections != 2 && !useRatio
                && (matchConstraintDefault == MATCH_CONSTRAINT_WRAP
                        || matchConstraintDefault == MATCH_CONSTRAINT_SPREAD)
            ) {
                variableSize = false
                var d = max(matchMinDimension, dimension)
                if (matchMaxDimension > 0) {
                    d = min(matchMaxDimension, d)
                }
                system.addEquality(end, begin, d, SolverVariable.STRENGTH_FIXED)
            } else {
                if (matchMinDimension == WRAP) {
                    matchMinDimension = dimension
                }
                if (matchMaxDimension == WRAP) {
                    matchMaxDimension = dimension
                }
                if (dimension > 0
                    && matchConstraintDefault != MATCH_CONSTRAINT_WRAP
                ) {
                    if (USE_WRAP_DIMENSION_FOR_SPREAD && matchConstraintDefault == MATCH_CONSTRAINT_SPREAD) {
                        system.addGreaterThan(end, begin, dimension, SolverVariable.STRENGTH_HIGHEST)
                    }
                    dimension = 0
                }
                if (matchMinDimension > 0) {
                    system.addGreaterThan(end, begin, matchMinDimension, SolverVariable.STRENGTH_FIXED)
                    dimension = max(dimension, matchMinDimension)
                }
                if (matchMaxDimension > 0) {
                    var applyLimit = true
                    if (parentWrapContent && matchConstraintDefault == MATCH_CONSTRAINT_WRAP) {
                        applyLimit = false
                    }
                    if (applyLimit) {
                        system.addLowerThan(end, begin, matchMaxDimension, SolverVariable.STRENGTH_FIXED)
                    }
                    dimension = min(dimension, matchMaxDimension)
                }
                if (matchConstraintDefault == MATCH_CONSTRAINT_WRAP) {
                    if (parentWrapContent) {
                        system.addEquality(end, begin, dimension, SolverVariable.STRENGTH_FIXED)
                    } else if (inChain) {
                        system.addEquality(end, begin, dimension, SolverVariable.STRENGTH_EQUALITY)
                        system.addLowerThan(end, begin, dimension, SolverVariable.STRENGTH_FIXED)
                    } else {
                        system.addEquality(end, begin, dimension, SolverVariable.STRENGTH_EQUALITY)
                        system.addLowerThan(end, begin, dimension, SolverVariable.STRENGTH_FIXED)
                    }
                } else if (matchConstraintDefault == MATCH_CONSTRAINT_PERCENT) {
                    var percentBegin: SolverVariable? = null
                    var percentEnd: SolverVariable? = null
                    if (beginAnchor.type === ConstraintAnchor.Type.TOP || beginAnchor.type === ConstraintAnchor.Type.BOTTOM) {
                        // vertical
                        percentBegin = system.createObjectVariable(parent!!.getAnchor(ConstraintAnchor.Type.TOP))
                        percentEnd = system.createObjectVariable(parent!!.getAnchor(ConstraintAnchor.Type.BOTTOM))
                    } else {
                        percentBegin = system.createObjectVariable(parent!!.getAnchor(ConstraintAnchor.Type.LEFT))
                        percentEnd = system.createObjectVariable(parent!!.getAnchor(ConstraintAnchor.Type.RIGHT))
                    }
                    system.addConstraint(system.createRow().createRowDimensionRatio(end, begin, percentEnd, percentBegin, matchPercentDimension))
                    if (parentWrapContent) {
                        variableSize = false
                    }
                } else {
                    isTerminal = true
                }
            }
        }
        if (!applyPosition || inChain) {
            // If we don't need to apply the position, let's finish now.
            if (LinearSystem.FULL_DEBUG) {
                println(
                    "only deal with dimension for " + debugName
                            + ", not positioning (applyPosition: " + applyPosition + " inChain: " + inChain + ")"
                )
            }
            if (numConnections < 2 && parentWrapContent && isTerminal) {
                system.addGreaterThan(begin, parentMin, 0, SolverVariable.STRENGTH_FIXED)
                var applyEnd = isHorizontal || mBaseline!!.target == null
                if (!isHorizontal && mBaseline!!.target != null) {
                    // generally we wouldn't take the current widget in the wrap content, but if the connected element is a ratio widget,
                    // then we can contribute (as the ratio widget may not be enough by itself) to it.
                    val target = mBaseline!!.target!!.owner
                    applyEnd =
                        if (target.dimensionRatio != 0f && target.mListDimensionBehaviors[0] == DimensionBehaviour.MATCH_CONSTRAINT && target.mListDimensionBehaviors[1] == DimensionBehaviour.MATCH_CONSTRAINT) {
                            true
                        } else {
                            false
                        }
                }
                if (applyEnd) {
                    if (LinearSystem.FULL_DEBUG) {
                        println("<>4 ADDING WRAP GREATER FOR $debugName")
                    }
                    system.addGreaterThan(parentMax, end, 0, SolverVariable.STRENGTH_FIXED)
                }
            }
            return
        }

        // Ok, we are dealing with single or centered constraints, let's apply them
        var wrapStrength = SolverVariable.STRENGTH_EQUALITY
        if (!isBeginConnected && !isEndConnected && !isCenterConnected) {
            // note we already applied the start position before, no need to redo it...
        } else if (isBeginConnected && !isEndConnected) {
            // note we already applied the start position before, no need to redo it...

            // If we are constrained to a barrier, make sure that we are not bypassed in the wrap
            val beginWidget = beginAnchor.target!!.owner
            if (parentWrapContent && beginWidget is Barrier) {
                wrapStrength = SolverVariable.STRENGTH_FIXED
            }
        } else if (!isBeginConnected && isEndConnected) {
            system.addEquality(end, endTarget, -endAnchor.margin, SolverVariable.STRENGTH_FIXED)
            if (parentWrapContent) {
                if (OPTIMIZE_WRAP && begin?.isFinalValue == true && parent != null) {
                    val container = parent as ConstraintWidgetContainer
                    if (isHorizontal) {
                        container.addHorizontalWrapMinVariable(beginAnchor)
                    } else {
                        container.addVerticalWrapMinVariable(beginAnchor)
                    }
                } else {
                    if (LinearSystem.FULL_DEBUG) {
                        println("<>5 ADDING WRAP GREATER FOR $debugName")
                    }
                    system.addGreaterThan(begin, parentMin, 0, SolverVariable.STRENGTH_EQUALITY)
                }
            }
        } else if (isBeginConnected && isEndConnected) {
            var applyBoundsCheck = true
            var applyCentering = false
            var applyStrongChecks = false
            var applyRangeCheck = false
            var rangeCheckStrength = SolverVariable.STRENGTH_EQUALITY
            var boundsCheckStrength = SolverVariable.STRENGTH_HIGHEST // TODO: might not need it here (it's overridden)
            var centeringStrength = SolverVariable.STRENGTH_BARRIER
            if (parentWrapContent) {
                rangeCheckStrength = SolverVariable.STRENGTH_EQUALITY
            }
            val beginWidget = beginAnchor.target!!.owner
            val endWidget = endAnchor.target!!.owner
            val parent = parent
            if (variableSize) {
                if (matchConstraintDefault == MATCH_CONSTRAINT_SPREAD) {
                    if (matchMaxDimension == 0 && matchMinDimension == 0) {
                        applyStrongChecks = true
                        rangeCheckStrength = SolverVariable.STRENGTH_FIXED
                        boundsCheckStrength = SolverVariable.STRENGTH_FIXED
                        // Optimization in case of centering in parent
                        if (beginTarget?.isFinalValue == true && endTarget?.isFinalValue == true) {
                            system.addEquality(begin, beginTarget, beginAnchor.margin, SolverVariable.STRENGTH_FIXED)
                            system.addEquality(end, endTarget, -endAnchor.margin, SolverVariable.STRENGTH_FIXED)
                            return
                        }
                    } else {
                        applyCentering = true
                        rangeCheckStrength = SolverVariable.STRENGTH_EQUALITY
                        boundsCheckStrength = SolverVariable.STRENGTH_EQUALITY
                        applyBoundsCheck = true
                        applyRangeCheck = true
                    }
                    if (beginWidget is Barrier || endWidget is Barrier) {
                        boundsCheckStrength = SolverVariable.STRENGTH_HIGHEST
                    }
                } else if (matchConstraintDefault == MATCH_CONSTRAINT_PERCENT) {
                    applyCentering = true
                    rangeCheckStrength = SolverVariable.STRENGTH_EQUALITY
                    boundsCheckStrength = SolverVariable.STRENGTH_EQUALITY
                    applyBoundsCheck = true
                    applyRangeCheck = true
                    if (beginWidget is Barrier || endWidget is Barrier) {
                        boundsCheckStrength = SolverVariable.STRENGTH_HIGHEST
                    }
                } else if (matchConstraintDefault == MATCH_CONSTRAINT_WRAP) {
                    applyCentering = true
                    applyRangeCheck = true
                    rangeCheckStrength = SolverVariable.STRENGTH_FIXED
                } else if (matchConstraintDefault == MATCH_CONSTRAINT_RATIO) {
                    if (mResolvedDimensionRatioSide == UNKNOWN) {
                        applyCentering = true
                        applyRangeCheck = true
                        applyStrongChecks = true
                        rangeCheckStrength = SolverVariable.STRENGTH_FIXED
                        boundsCheckStrength = SolverVariable.STRENGTH_EQUALITY
                        if (oppositeInChain) {
                            boundsCheckStrength = SolverVariable.STRENGTH_EQUALITY
                            centeringStrength = SolverVariable.STRENGTH_HIGHEST
                            if (parentWrapContent) {
                                centeringStrength = SolverVariable.STRENGTH_EQUALITY
                            }
                        } else {
                            centeringStrength = SolverVariable.STRENGTH_FIXED
                        }
                    } else {
                        applyCentering = true
                        applyRangeCheck = true
                        applyStrongChecks = true
                        if (useRatio) {
                            // useRatio is true if the side we base ourselves on for the ratio is this one
                            // if that's not the case, we need to have a stronger constraint.
                            val otherSideInvariable = (oppositeMatchConstraintDefault == MATCH_CONSTRAINT_PERCENT
                                    || oppositeMatchConstraintDefault == MATCH_CONSTRAINT_WRAP)
                            if (!otherSideInvariable) {
                                rangeCheckStrength = SolverVariable.STRENGTH_FIXED
                                boundsCheckStrength = SolverVariable.STRENGTH_EQUALITY
                            }
                        } else {
                            rangeCheckStrength = SolverVariable.STRENGTH_EQUALITY
                            if (matchMaxDimension > 0) {
                                boundsCheckStrength = SolverVariable.STRENGTH_EQUALITY
                            } else if (matchMaxDimension == 0 && matchMinDimension == 0) {
                                if (!oppositeInChain) {
                                    boundsCheckStrength = SolverVariable.STRENGTH_FIXED
                                } else {
                                    rangeCheckStrength = if (beginWidget !== parent && endWidget !== parent) {
                                        SolverVariable.STRENGTH_HIGHEST
                                    } else {
                                        SolverVariable.STRENGTH_EQUALITY
                                    }
                                    boundsCheckStrength = SolverVariable.STRENGTH_HIGHEST
                                }
                            }
                        }
                    }
                }
            } else {
                applyCentering = true
                applyRangeCheck = true

                // Let's optimize away if we can...
                if (beginTarget != null) {
                    if (endTarget != null) {
                        if (beginTarget.isFinalValue && endTarget.isFinalValue) {
                            system.addCentering(
                                begin, beginTarget, beginAnchor.margin,
                                bias, endTarget, end, endAnchor.margin, SolverVariable.STRENGTH_FIXED
                            )
                            if (parentWrapContent && isTerminal) {
                                var margin = 0
                                if (endAnchor.target != null) {
                                    margin = endAnchor.margin
                                }
                                if (endTarget !== parentMax) { // if not already applied
                                    if (LinearSystem.FULL_DEBUG) {
                                        println("<>6 ADDING WRAP GREATER FOR $debugName")
                                    }
                                    system.addGreaterThan(parentMax, end, margin, wrapStrength)
                                }
                            }
                            return
                        }
                    }
                }
            }
            if (applyRangeCheck && beginTarget === endTarget && beginWidget !== parent) {
                // no need to apply range / bounds check if we are centered on the same anchor
                applyRangeCheck = false
                applyBoundsCheck = false
            }
            if (applyCentering) {
                if (!variableSize && !oppositeVariable && !oppositeInChain
                    && beginTarget === parentMin && endTarget === parentMax
                ) {
                    // for fixed size widgets, we can simplify the constraints
                    centeringStrength = SolverVariable.STRENGTH_FIXED
                    rangeCheckStrength = SolverVariable.STRENGTH_FIXED
                    applyBoundsCheck = false
                    parentWrapContent = false
                }
                system.addCentering(
                    begin, beginTarget, beginAnchor.margin,
                    bias, endTarget, end, endAnchor.margin, centeringStrength
                )
            }
            if (visibility == GONE && !endAnchor.hasDependents()) {
                return
            }
            if (applyRangeCheck) {
                if (parentWrapContent && beginTarget !== endTarget && !variableSize) {
                    if (beginWidget is Barrier || endWidget is Barrier) {
                        rangeCheckStrength = SolverVariable.STRENGTH_BARRIER
                    }
                }
                system.addGreaterThan(begin, beginTarget, beginAnchor.margin, rangeCheckStrength)
                system.addLowerThan(end, endTarget, -endAnchor.margin, rangeCheckStrength)
            }
            if (parentWrapContent && inBarrier // if we are referenced by a barrier
                && !(beginWidget is Barrier || endWidget is Barrier)
                && !(endWidget === parent)
            ) {
                // ... but not directly constrained by it
                // ... then make sure we can hold our own
                boundsCheckStrength = SolverVariable.STRENGTH_BARRIER
                rangeCheckStrength = SolverVariable.STRENGTH_BARRIER
                applyBoundsCheck = true
            }
            if (applyBoundsCheck) {
                if (applyStrongChecks && (!oppositeInChain || oppositeParentWrapContent)) {
                    var strength = boundsCheckStrength
                    if (beginWidget === parent || endWidget === parent) {
                        strength = SolverVariable.STRENGTH_BARRIER
                    }
                    if (beginWidget is Guideline || endWidget is Guideline) {
                        strength = SolverVariable.STRENGTH_EQUALITY
                    }
                    if (beginWidget is Barrier || endWidget is Barrier) {
                        strength = SolverVariable.STRENGTH_EQUALITY
                    }
                    if (oppositeInChain) {
                        strength = SolverVariable.STRENGTH_EQUALITY
                    }
                    boundsCheckStrength = max(strength, boundsCheckStrength)
                }
                if (parentWrapContent) {
                    boundsCheckStrength = min(rangeCheckStrength, boundsCheckStrength)
                    if (useRatio && !oppositeInChain && (beginWidget === parent || endWidget === parent)) {
                        // When using ratio, relax some strength to allow other parts of the system
                        // to take precedence rather than driving it
                        boundsCheckStrength = SolverVariable.STRENGTH_HIGHEST
                    }
                }
                system.addEquality(begin, beginTarget, beginAnchor.margin, boundsCheckStrength)
                system.addEquality(end, endTarget, -endAnchor.margin, boundsCheckStrength)
            }
            if (parentWrapContent) {
                var margin = 0
                if (parentMin === beginTarget) {
                    margin = beginAnchor.margin
                }
                if (beginTarget !== parentMin) { // already done otherwise
                    if (LinearSystem.FULL_DEBUG) {
                        println("<>7 ADDING WRAP GREATER FOR $debugName")
                    }
                    system.addGreaterThan(begin, parentMin, margin, wrapStrength)
                }
            }
            if (parentWrapContent && variableSize && minDimension == 0 && matchMinDimension == 0) {
                if (LinearSystem.FULL_DEBUG) {
                    println("<>8 ADDING WRAP GREATER FOR $debugName")
                }
                if (variableSize && matchConstraintDefault == MATCH_CONSTRAINT_RATIO) {
                    system.addGreaterThan(end, begin, 0, SolverVariable.STRENGTH_FIXED)
                } else {
                    system.addGreaterThan(end, begin, 0, wrapStrength)
                }
            }
        }
        if (parentWrapContent && isTerminal) {
            var margin = 0
            if (endAnchor.target != null) {
                margin = endAnchor.margin
            }
            if (endTarget !== parentMax) { // if not already applied
                if (OPTIMIZE_WRAP && end?.isFinalValue == true && parent != null) {
                    val container = parent as ConstraintWidgetContainer
                    if (isHorizontal) {
                        container.addHorizontalWrapMaxVariable(endAnchor)
                    } else {
                        container.addVerticalWrapMaxVariable(endAnchor)
                    }
                    return
                }
                if (LinearSystem.FULL_DEBUG) {
                    println("<>9 ADDING WRAP GREATER FOR $debugName")
                }
                system.addGreaterThan(parentMax, end, margin, wrapStrength)
            }
        }
    }

    /**
     * Update the widget from the values generated by the solver
     *
     * @param system the solver we get the values from.
     * @param optimize true if [Optimizer.OPTIMIZATION_GRAPH] is on
     */
    open fun updateFromSolver(system: LinearSystem, optimize: Boolean) {
        var left = mLeft?.let { system.getObjectVariableValue(it) } ?: 0
        var top = system.getObjectVariableValue(mTop)
        var right = mRight?.let { system.getObjectVariableValue(it) } ?: 0
        var bottom = system.getObjectVariableValue(mBottom)
        if (optimize && horizontalRun != null && horizontalRun!!.start.resolved && horizontalRun!!.end.resolved) {
            left = horizontalRun!!.start.value
            right = horizontalRun!!.end.value
        }
        if (optimize && verticalRun != null && verticalRun!!.start.resolved && verticalRun!!.end.resolved) {
            top = verticalRun!!.start.value
            bottom = verticalRun!!.end.value
        }
        val w = right - left
        val h = bottom - top
        if (w < 0 || h < 0 || left == Int.MIN_VALUE || left == Int.MAX_VALUE || top == Int.MIN_VALUE || top == Int.MAX_VALUE || right == Int.MIN_VALUE || right == Int.MAX_VALUE || bottom == Int.MIN_VALUE || bottom == Int.MAX_VALUE) {
            left = 0
            top = 0
            right = 0
            bottom = 0
        }
        setFrame(left, top, right, bottom)
        if (LinearSystem.DEBUG) {
            println(" *** UPDATE FROM SOLVER $this")
        }
    }

    open fun copy(src: ConstraintWidget, map: HashMap) {
        // Support for direct resolution
        mHorizontalResolution = src.mHorizontalResolution
        mVerticalResolution = src.mVerticalResolution
        mMatchConstraintDefaultWidth = src.mMatchConstraintDefaultWidth
        mMatchConstraintDefaultHeight = src.mMatchConstraintDefaultHeight
        mResolvedMatchConstraintDefault[0] = src.mResolvedMatchConstraintDefault[0]
        mResolvedMatchConstraintDefault[1] = src.mResolvedMatchConstraintDefault[1]
        mMatchConstraintMinWidth = src.mMatchConstraintMinWidth
        mMatchConstraintMaxWidth = src.mMatchConstraintMaxWidth
        mMatchConstraintMinHeight = src.mMatchConstraintMinHeight
        mMatchConstraintMaxHeight = src.mMatchConstraintMaxHeight
        mMatchConstraintPercentHeight = src.mMatchConstraintPercentHeight
        isWidthWrapContent = src.isWidthWrapContent
        isHeightWrapContent = src.isHeightWrapContent
        mResolvedDimensionRatioSide = src.mResolvedDimensionRatioSide
        mResolvedDimensionRatio = src.mResolvedDimensionRatio
        mMaxDimension = src.mMaxDimension.copyOf(src.mMaxDimension.size)
        mCircleConstraintAngle = src.mCircleConstraintAngle
        hasBaseline = src.hasBaseline
        isInPlaceholder = src.isInPlaceholder

        // The anchors available on the widget
        // note: all anchors should be added to the mAnchors array (see addAnchors())
        mLeft!!.reset()
        mTop.reset()
        mRight!!.reset()
        mBottom.reset()
        mBaseline!!.reset()
        mCenterX.reset()
        mCenterY.reset()
        mCenter.reset()
        mListDimensionBehaviors = mListDimensionBehaviors.copyOf(2).filterNotNull().toTypedArray()
        parent = if (parent == null) null else map[src.parent]
        mWidth = src.mWidth
        mHeight = src.mHeight
        dimensionRatio = src.dimensionRatio
        dimensionRatioSide = src.dimensionRatioSide
        mX = src.mX
        mY = src.mY
        mRelX = src.mRelX
        mRelY = src.mRelY
        mOffsetX = src.mOffsetX
        mOffsetY = src.mOffsetY
        mBaselineDistance = src.mBaselineDistance
        mMinWidth = src.mMinWidth
        mMinHeight = src.mMinHeight
        horizontalBiasPercent = src.horizontalBiasPercent
        verticalBiasPercent = src.verticalBiasPercent
        companionWidget = src.companionWidget
        mContainerItemSkip = src.mContainerItemSkip
        visibility = src.visibility
        debugName = src.debugName
        type = src.type
        mDistToTop = src.mDistToTop
        mDistToLeft = src.mDistToLeft
        mDistToRight = src.mDistToRight
        mDistToBottom = src.mDistToBottom
        mLeftHasCentered = src.mLeftHasCentered
        mRightHasCentered = src.mRightHasCentered
        mTopHasCentered = src.mTopHasCentered
        mBottomHasCentered = src.mBottomHasCentered
        mHorizontalWrapVisited = src.mHorizontalWrapVisited
        mVerticalWrapVisited = src.mVerticalWrapVisited
        horizontalChainStyle = src.horizontalChainStyle
        verticalChainStyle = src.verticalChainStyle
        mHorizontalChainFixedPosition = src.mHorizontalChainFixedPosition
        mVerticalChainFixedPosition = src.mVerticalChainFixedPosition
        mWeight[0] = src.mWeight[0]
        mWeight[1] = src.mWeight[1]
        mListNextMatchConstraintsWidget[0] = src.mListNextMatchConstraintsWidget[0]
        mListNextMatchConstraintsWidget[1] = src.mListNextMatchConstraintsWidget[1]
        mNextChainWidget[0] = src.mNextChainWidget[0]
        mNextChainWidget[1] = src.mNextChainWidget[1]
        mHorizontalNextWidget = if (src.mHorizontalNextWidget == null) null else map[src.mHorizontalNextWidget]
        mVerticalNextWidget = if (src.mVerticalNextWidget == null) null else map[src.mVerticalNextWidget]
    }

    open fun updateFromRuns(updateHorizontal: Boolean, updateVertical: Boolean) {
        var updateHorizontal = updateHorizontal
        var updateVertical = updateVertical
        updateHorizontal = updateHorizontal and horizontalRun!!.isResolved
        updateVertical = updateVertical and verticalRun!!.isResolved
        var left = horizontalRun!!.start.value
        var top = verticalRun!!.start.value
        var right = horizontalRun!!.end.value
        var bottom = verticalRun!!.end.value
        var w = right - left
        var h = bottom - top
        if (w < 0 || h < 0 || left == Int.MIN_VALUE || left == Int.MAX_VALUE || top == Int.MIN_VALUE || top == Int.MAX_VALUE || right == Int.MIN_VALUE || right == Int.MAX_VALUE || bottom == Int.MIN_VALUE || bottom == Int.MAX_VALUE) {
            left = 0
            top = 0
            right = 0
            bottom = 0
        }
        w = right - left
        h = bottom - top
        if (updateHorizontal) {
            mX = left
        }
        if (updateVertical) {
            mY = top
        }
        if (visibility == GONE) {
            mWidth = 0
            mHeight = 0
            return
        }

        // correct dimensional instability caused by rounding errors
        if (updateHorizontal) {
            if (mListDimensionBehaviors[DIMENSION_HORIZONTAL] == DimensionBehaviour.FIXED && w < mWidth) {
                w = mWidth
            }
            mWidth = w
            if (mWidth < mMinWidth) {
                mWidth = mMinWidth
            }
        }
        if (updateVertical) {
            if (mListDimensionBehaviors[DIMENSION_VERTICAL] == DimensionBehaviour.FIXED && h < mHeight) {
                h = mHeight
            }
            mHeight = h
            if (mHeight < mMinHeight) {
                mHeight = mMinHeight
            }
        }
    }

    fun addChildrenToSolverByDependency(container: ConstraintWidgetContainer, system: LinearSystem, widgets: HashSet, orientation: Int, addSelf: Boolean) {
        if (addSelf) {
            if (!widgets.contains(this)) {
                return
            }
            Optimizer.checkMatchParent(container, system, this)
            widgets.remove(this)
            addToSolver(system, container.optimizeFor(Optimizer.OPTIMIZATION_GRAPH))
        }
        if (orientation == HORIZONTAL) {
            var dependents = mLeft!!.dependents
            if (dependents != null) {
                for (anchor in dependents) {
                    anchor.owner.addChildrenToSolverByDependency(container, system, widgets, orientation, true)
                }
            }
            dependents = mRight!!.dependents
            if (dependents != null) {
                for (anchor in dependents) {
                    anchor.owner.addChildrenToSolverByDependency(container, system, widgets, orientation, true)
                }
            }
        } else {
            var dependents = mTop.dependents
            if (dependents != null) {
                for (anchor in dependents) {
                    anchor.owner.addChildrenToSolverByDependency(container, system, widgets, orientation, true)
                }
            }
            dependents = mBottom.dependents
            if (dependents != null) {
                for (anchor in dependents) {
                    anchor.owner.addChildrenToSolverByDependency(container, system, widgets, orientation, true)
                }
            }
            dependents = mBaseline!!.dependents
            if (dependents != null) {
                for (anchor in dependents) {
                    anchor.owner.addChildrenToSolverByDependency(container, system, widgets, orientation, true)
                }
            }
        }
        // horizontal
    }

    open fun getSceneString(ret: StringBuilder) {
        ret.append("  $stringId:{\n")
        ret.append("    actualWidth:$mWidth")
        ret.append("\n")
        ret.append("    actualHeight:$mHeight")
        ret.append("\n")
        ret.append("    actualLeft:$mX")
        ret.append("\n")
        ret.append("    actualTop:$mY")
        ret.append("\n")
        getSceneString(ret, "left", mLeft)
        getSceneString(ret, "top", mTop)
        getSceneString(ret, "right", mRight)
        getSceneString(ret, "bottom", mBottom)
        getSceneString(ret, "baseline", mBaseline)
        getSceneString(ret, "centerX", mCenterX)
        getSceneString(ret, "centerY", mCenterY)
        getSceneString(
            ret, "    width",
            mWidth,
            mMinWidth,
            mMaxDimension[HORIZONTAL],
            mWidthOverride,
            mMatchConstraintMinWidth,
            mMatchConstraintDefaultWidth,
            mMatchConstraintPercentWidth,
            mWeight[DIMENSION_HORIZONTAL]
        )
        getSceneString(
            ret, "    height",
            mHeight,
            mMinHeight,
            mMaxDimension[VERTICAL],
            mHeightOverride,
            mMatchConstraintMinHeight,
            mMatchConstraintDefaultHeight,
            mMatchConstraintPercentHeight,
            mWeight[DIMENSION_VERTICAL]
        )
        serializeDimensionRatio(ret, "    dimensionRatio", dimensionRatio, dimensionRatioSide)
        serializeAttribute(ret, "    horizontalBias", horizontalBiasPercent, DEFAULT_BIAS)
        serializeAttribute(ret, "    verticalBias", verticalBiasPercent, DEFAULT_BIAS)
        serializeAttribute(ret, "    horizontalChainStyle", horizontalChainStyle, CHAIN_SPREAD)
        serializeAttribute(ret, "    verticalChainStyle", verticalChainStyle, CHAIN_SPREAD)
        ret.append("  }")
    }

    private fun getSceneString(
        ret: StringBuilder, type: String, size: Int,
        min: Int, max: Int, override: Int,
        matchConstraintMin: Int, matchConstraintDefault: Int,
        MatchConstraintPercent: Float,
        weight: Float
    ) {
        ret.append(type)
        ret.append(" :  {\n")
        serializeAttribute(ret, "      size", size, 0)
        serializeAttribute(ret, "      min", min, 0)
        serializeAttribute(ret, "      max", max, Int.MAX_VALUE)
        serializeAttribute(ret, "      matchMin", matchConstraintMin, 0)
        serializeAttribute(ret, "      matchDef", matchConstraintDefault, MATCH_CONSTRAINT_SPREAD)
        serializeAttribute(ret, "      matchPercent", MatchConstraintPercent, 1f)
        ret.append("    },\n")
    }

    private fun getSceneString(ret: StringBuilder, side: String, a: ConstraintAnchor?) {
        if (a!!.target == null) {
            return
        }
        ret.append("    ")
        ret.append(side)
        ret.append(" : [ '")
        ret.append(a.target)
        ret.append("'")
        if (a.mGoneMargin != Int.MIN_VALUE || a.mMargin != 0) {
            ret.append(",")
            ret.append(a.mMargin)
            if (a.mGoneMargin != Int.MIN_VALUE) {
                ret.append(",")
                ret.append(a.mGoneMargin)
                ret.append(",")
            }
        }
        ret.append(" ] ,\n")
    }

    companion object {
        private const val AUTOTAG_CENTER = false
        protected const val SOLVER = 1
        const val DIRECT = 2

        // apply an intrinsic size when wrap content for spread dimensions
        private const val USE_WRAP_DIMENSION_FOR_SPREAD = false

        ////////////////////////////////////////////////////////////////////////////////////////////////
        const val MATCH_CONSTRAINT_SPREAD = 0
        const val MATCH_CONSTRAINT_WRAP = 1
        const val MATCH_CONSTRAINT_PERCENT = 2
        const val MATCH_CONSTRAINT_RATIO = 3
        const val MATCH_CONSTRAINT_RATIO_RESOLVED = 4
        const val UNKNOWN = -1
        const val HORIZONTAL = 0
        const val VERTICAL = 1
        const val BOTH = 2
        const val VISIBLE = 0
        const val INVISIBLE = 4
        const val GONE = 8

        // Values of the chain styles
        const val CHAIN_SPREAD = 0
        const val CHAIN_SPREAD_INSIDE = 1
        const val CHAIN_PACKED = 2

        // Values of the wrap behavior in parent
        const val WRAP_BEHAVIOR_INCLUDED = 0 // default
        const val WRAP_BEHAVIOR_HORIZONTAL_ONLY = 1
        const val WRAP_BEHAVIOR_VERTICAL_ONLY = 2
        const val WRAP_BEHAVIOR_SKIPPED = 3
        private const val WRAP = -2
        const val ANCHOR_LEFT = 0
        const val ANCHOR_RIGHT = 1
        const val ANCHOR_TOP = 2
        const val ANCHOR_BOTTOM = 3
        const val ANCHOR_BASELINE = 4

        // The horizontal and vertical behaviour for the widgets' dimensions
        const val DIMENSION_HORIZONTAL = 0
        const val DIMENSION_VERTICAL = 1

        // Percentages used for biasing one connection over another when dual connections
        // of the same strength exist
        var DEFAULT_BIAS = 0.5f
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy