content: /*
   * Copyright (C) 2019 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.state;
  
  import static androidx.constraintlayout.core.widgets.ConstraintWidget.HORIZONTAL;
  import static androidx.constraintlayout.core.widgets.ConstraintWidget.UNKNOWN;
  import static androidx.constraintlayout.core.widgets.ConstraintWidget.VERTICAL;
  
  import androidx.annotation.Nullable;
  import androidx.constraintlayout.core.motion.utils.TypedBundle;
  import androidx.constraintlayout.core.motion.utils.TypedValues;
  import androidx.constraintlayout.core.state.helpers.Facade;
  import androidx.constraintlayout.core.widgets.ConstraintAnchor;
  import androidx.constraintlayout.core.widgets.ConstraintWidget;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  
  public class ConstraintReference implements Reference {
  
      private Object mKey;
  
      @Override
      public void setKey(Object key) {
          this.mKey = key;
      }
  
      @Override
      public Object getKey() {
          return mKey;
      }
  
      public void setTag(String tag) {
          mTag = tag;
      }
  
      public String getTag() {
          return mTag;
      }
  
      public interface ConstraintReferenceFactory {
          // @TODO: add description
          ConstraintReference create(State state);
      }
  
      final State mState;
  
      String mTag = null;
  
      Facade mFacade = null;
  
      int mHorizontalChainStyle = ConstraintWidget.CHAIN_SPREAD;
      int mVerticalChainStyle = ConstraintWidget.CHAIN_SPREAD;
  
      float mHorizontalChainWeight = UNKNOWN;
      float mVerticalChainWeight = UNKNOWN;
  
      protected float mHorizontalBias = 0.5f;
      protected float mVerticalBias = 0.5f;
  
      protected int mMarginLeft = 0;
      protected int mMarginRight = 0;
      protected int mMarginStart = 0;
      protected int mMarginEnd = 0;
      protected int mMarginTop = 0;
      protected int mMarginBottom = 0;
  
      protected int mMarginLeftGone = 0;
      protected int mMarginRightGone = 0;
      protected int mMarginStartGone = 0;
      protected int mMarginEndGone = 0;
      protected int mMarginTopGone = 0;
      protected int mMarginBottomGone = 0;
  
      int mMarginBaseline = 0;
      int mMarginBaselineGone = 0;
  
      float mPivotX = Float.NaN;
      float mPivotY = Float.NaN;
  
      float mRotationX = Float.NaN;
      float mRotationY = Float.NaN;
      float mRotationZ = Float.NaN;
  
      float mTranslationX = Float.NaN;
      float mTranslationY = Float.NaN;
      float mTranslationZ = Float.NaN;
  
      float mAlpha = Float.NaN;
  
      float mScaleX = Float.NaN;
      float mScaleY = Float.NaN;
  
      int mVisibility = ConstraintWidget.VISIBLE;
  
      protected Object mLeftToLeft = null;
      protected Object mLeftToRight = null;
      protected Object mRightToLeft = null;
      protected Object mRightToRight = null;
      protected Object mStartToStart = null;
      protected Object mStartToEnd = null;
      protected Object mEndToStart = null;
      protected Object mEndToEnd = null;
      protected Object mTopToTop = null;
      protected Object mTopToBottom = null;
      @Nullable Object mTopToBaseline = null;
      protected Object mBottomToTop = null;
      protected Object mBottomToBottom = null;
      @Nullable Object mBottomToBaseline = null;
      Object mBaselineToBaseline = null;
      Object mBaselineToTop = null;
      Object mBaselineToBottom = null;
      Object mCircularConstraint = null;
      private float mCircularAngle;
      private float mCircularDistance;
  
      State.Constraint mLast = null;
  
      Dimension mHorizontalDimension = Dimension.createFixed(Dimension.WRAP_DIMENSION);
      Dimension mVerticalDimension = Dimension.createFixed(Dimension.WRAP_DIMENSION);
  
      private Object mView;
      private ConstraintWidget mConstraintWidget;
  
      private HashMap<String, Integer> mCustomColors = new HashMap<>();
      private HashMap<String, Float> mCustomFloats = new HashMap<>();
  
      TypedBundle mMotionProperties = null;
  
      // @TODO: add description
      public void setView(Object view) {
          mView = view;
          if (mConstraintWidget != null) {
              mConstraintWidget.setCompanionWidget(mView);
          }
      }
  
      public Object getView() {
          return mView;
      }
  
      // @TODO: add description
      public void setFacade(Facade facade) {
          mFacade = facade;
          if (facade != null) {
              setConstraintWidget(facade.getConstraintWidget());
          }
      }
  
      @Override
      public Facade getFacade() {
          return mFacade;
      }
  
      // @TODO: add description
      @Override
      public void setConstraintWidget(ConstraintWidget widget) {
          if (widget == null) {
              return;
          }
          mConstraintWidget = widget;
          mConstraintWidget.setCompanionWidget(mView);
      }
  
      @Override
      public ConstraintWidget getConstraintWidget() {
          if (mConstraintWidget == null) {
              mConstraintWidget = createConstraintWidget();
              mConstraintWidget.setCompanionWidget(mView);
          }
          return mConstraintWidget;
      }
  
      // @TODO: add description
      public ConstraintWidget createConstraintWidget() {
          return new ConstraintWidget(
                  getWidth().getValue(),
                  getHeight().getValue());
      }
  
      static class IncorrectConstraintException extends Exception {
  
          private final ArrayList<String> mErrors;
  
          IncorrectConstraintException(ArrayList<String> errors) {
              mErrors = errors;
          }
  
          public ArrayList<String> getErrors() {
              return mErrors;
          }
  
          @Override
          public String  getMessage() { return toString(); }
  
          @Override
          public String toString() {
              return "IncorrectConstraintException: " + mErrors.toString();
          }
      }
  
      /**
       * Validate the constraints
       */
      public void validate() throws IncorrectConstraintException {
          ArrayList<String> errors = new ArrayList<>();
          if (mLeftToLeft != null && mLeftToRight != null) {
              errors.add("LeftToLeft and LeftToRight both defined");
          }
          if (mRightToLeft != null && mRightToRight != null) {
              errors.add("RightToLeft and RightToRight both defined");
          }
          if (mStartToStart != null && mStartToEnd != null) {
              errors.add("StartToStart and StartToEnd both defined");
          }
          if (mEndToStart != null && mEndToEnd != null) {
              errors.add("EndToStart and EndToEnd both defined");
          }
          if ((mLeftToLeft != null || mLeftToRight != null
                  || mRightToLeft != null || mRightToRight != null)
                  && (mStartToStart != null || mStartToEnd != null
                  || mEndToStart != null || mEndToEnd != null)) {
              errors.add("Both left/right and start/end constraints defined");
          }
          if (errors.size() > 0) {
              throw new IncorrectConstraintException(errors);
          }
      }
  
      private Object get(Object reference) {
          if (reference == null) {
              return null;
          }
          if (!(reference instanceof ConstraintReference)) {
              return mState.reference(reference);
          }
          return reference;
      }
  
      public ConstraintReference(State state) {
          mState = state;
      }
  
      public void setHorizontalChainStyle(int chainStyle) {
          mHorizontalChainStyle = chainStyle;
      }
  
      public int getHorizontalChainStyle() {
          return mHorizontalChainStyle;
      }
  
      public void setVerticalChainStyle(int chainStyle) {
          mVerticalChainStyle = chainStyle;
      }
  
      // @TODO: add description
      public int getVerticalChainStyle(int chainStyle) {
          return mVerticalChainStyle;
      }
  
      public float getHorizontalChainWeight() {
          return mHorizontalChainWeight;
      }
  
      public void setHorizontalChainWeight(float weight) {
          mHorizontalChainWeight = weight;
      }
  
      public float getVerticalChainWeight() {
          return mVerticalChainWeight;
      }
  
      public void setVerticalChainWeight(float weight) {
          mVerticalChainWeight = weight;
      }
  
      // @TODO: add description
      public ConstraintReference clearVertical() {
          top().clear();
          baseline().clear();
          bottom().clear();
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference clearHorizontal() {
          start().clear();
          end().clear();
          left().clear();
          right().clear();
          return this;
      }
  
      public float getTranslationX() {
          return mTranslationX;
      }
  
      public float getTranslationY() {
          return mTranslationY;
      }
  
      public float getTranslationZ() {
          return mTranslationZ;
      }
  
      public float getScaleX() {
          return mScaleX;
      }
  
      public float getScaleY() {
          return mScaleY;
      }
  
      public float getAlpha() {
          return mAlpha;
      }
  
      public float getPivotX() {
          return mPivotX;
      }
  
      public float getPivotY() {
          return mPivotY;
      }
  
      public float getRotationX() {
          return mRotationX;
      }
  
      public float getRotationY() {
          return mRotationY;
      }
  
      public float getRotationZ() {
          return mRotationZ;
      }
  
      // @TODO: add description
      public ConstraintReference pivotX(float x) {
          mPivotX = x;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference pivotY(float y) {
          mPivotY = y;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference rotationX(float x) {
          mRotationX = x;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference rotationY(float y) {
          mRotationY = y;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference rotationZ(float z) {
          mRotationZ = z;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference translationX(float x) {
          mTranslationX = x;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference translationY(float y) {
          mTranslationY = y;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference translationZ(float z) {
          mTranslationZ = z;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference scaleX(float x) {
          mScaleX = x;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference scaleY(float y) {
          mScaleY = y;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference alpha(float alpha) {
          mAlpha = alpha;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference visibility(int visibility) {
          mVisibility = visibility;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference left() {
          if (mLeftToLeft != null) {
              mLast = State.Constraint.LEFT_TO_LEFT;
          } else {
              mLast = State.Constraint.LEFT_TO_RIGHT;
          }
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference right() {
          if (mRightToLeft != null) {
              mLast = State.Constraint.RIGHT_TO_LEFT;
          } else {
              mLast = State.Constraint.RIGHT_TO_RIGHT;
          }
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference start() {
          if (mStartToStart != null) {
              mLast = State.Constraint.START_TO_START;
          } else {
              mLast = State.Constraint.START_TO_END;
          }
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference end() {
          if (mEndToStart != null) {
              mLast = State.Constraint.END_TO_START;
          } else {
              mLast = State.Constraint.END_TO_END;
          }
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference top() {
          if (mTopToTop != null) {
              mLast = State.Constraint.TOP_TO_TOP;
          } else {
              mLast = State.Constraint.TOP_TO_BOTTOM;
          }
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference bottom() {
          if (mBottomToTop != null) {
              mLast = State.Constraint.BOTTOM_TO_TOP;
          } else {
              mLast = State.Constraint.BOTTOM_TO_BOTTOM;
          }
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference baseline() {
          mLast = State.Constraint.BASELINE_TO_BASELINE;
          return this;
      }
  
      // @TODO: add description
      public void addCustomColor(String name, int color) {
          mCustomColors.put(name, color);
      }
  
      // @TODO: add description
      public void addCustomFloat(String name, float value) {
          if (mCustomFloats == null) {
              mCustomFloats = new HashMap<>();
          }
          mCustomFloats.put(name, value);
      }
  
      private void dereference() {
          mLeftToLeft = get(mLeftToLeft);
          mLeftToRight = get(mLeftToRight);
          mRightToLeft = get(mRightToLeft);
          mRightToRight = get(mRightToRight);
          mStartToStart = get(mStartToStart);
          mStartToEnd = get(mStartToEnd);
          mEndToStart = get(mEndToStart);
          mEndToEnd = get(mEndToEnd);
          mTopToTop = get(mTopToTop);
          mTopToBottom = get(mTopToBottom);
          mBottomToTop = get(mBottomToTop);
          mBottomToBottom = get(mBottomToBottom);
          mBaselineToBaseline = get(mBaselineToBaseline);
          mBaselineToTop = get(mBaselineToTop);
          mBaselineToBottom = get(mBaselineToBottom);
      }
  
      // @TODO: add description
      public ConstraintReference leftToLeft(Object reference) {
          mLast = State.Constraint.LEFT_TO_LEFT;
          mLeftToLeft = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference leftToRight(Object reference) {
          mLast = State.Constraint.LEFT_TO_RIGHT;
          mLeftToRight = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference rightToLeft(Object reference) {
          mLast = State.Constraint.RIGHT_TO_LEFT;
          mRightToLeft = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference rightToRight(Object reference) {
          mLast = State.Constraint.RIGHT_TO_RIGHT;
          mRightToRight = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference startToStart(Object reference) {
          mLast = State.Constraint.START_TO_START;
          mStartToStart = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference startToEnd(Object reference) {
          mLast = State.Constraint.START_TO_END;
          mStartToEnd = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference endToStart(Object reference) {
          mLast = State.Constraint.END_TO_START;
          mEndToStart = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference endToEnd(Object reference) {
          mLast = State.Constraint.END_TO_END;
          mEndToEnd = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference topToTop(Object reference) {
          mLast = State.Constraint.TOP_TO_TOP;
          mTopToTop = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference topToBottom(Object reference) {
          mLast = State.Constraint.TOP_TO_BOTTOM;
          mTopToBottom = reference;
          return this;
      }
  
      ConstraintReference topToBaseline(Object reference) {
          mLast = State.Constraint.TOP_TO_BASELINE;
          mTopToBaseline = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference bottomToTop(Object reference) {
          mLast = State.Constraint.BOTTOM_TO_TOP;
          mBottomToTop = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference bottomToBottom(Object reference) {
          mLast = State.Constraint.BOTTOM_TO_BOTTOM;
          mBottomToBottom = reference;
          return this;
      }
  
      ConstraintReference bottomToBaseline(Object reference) {
          mLast = State.Constraint.BOTTOM_TO_BASELINE;
          mBottomToBaseline = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference baselineToBaseline(Object reference) {
          mLast = State.Constraint.BASELINE_TO_BASELINE;
          mBaselineToBaseline = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference baselineToTop(Object reference) {
          mLast = State.Constraint.BASELINE_TO_TOP;
          mBaselineToTop = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference baselineToBottom(Object reference) {
          mLast = State.Constraint.BASELINE_TO_BOTTOM;
          mBaselineToBottom = reference;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference centerHorizontally(Object reference) {
          Object ref = get(reference);
          mStartToStart = ref;
          mEndToEnd = ref;
          mLast = State.Constraint.CENTER_HORIZONTALLY;
          mHorizontalBias = 0.5f;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference centerVertically(Object reference) {
          Object ref = get(reference);
          mTopToTop = ref;
          mBottomToBottom = ref;
          mLast = State.Constraint.CENTER_VERTICALLY;
          mVerticalBias = 0.5f;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference circularConstraint(Object reference, float angle, float distance) {
          Object ref = get(reference);
          mCircularConstraint = ref;
          mCircularAngle = angle;
          mCircularDistance = distance;
          mLast = State.Constraint.CIRCULAR_CONSTRAINT;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference width(Dimension dimension) {
          return setWidth(dimension);
      }
  
      // @TODO: add description
      public ConstraintReference height(Dimension dimension) {
          return setHeight(dimension);
      }
  
      public Dimension getWidth() {
          return mHorizontalDimension;
      }
  
      // @TODO: add description
      public ConstraintReference setWidth(Dimension dimension) {
          mHorizontalDimension = dimension;
          return this;
      }
  
      public Dimension getHeight() {
          return mVerticalDimension;
      }
  
      // @TODO: add description
      public ConstraintReference setHeight(Dimension dimension) {
          mVerticalDimension = dimension;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference margin(Object marginValue) {
          return margin(mState.convertDimension(marginValue));
      }
  
      // @TODO: add description
      public ConstraintReference marginGone(Object marginGoneValue) {
          return marginGone(mState.convertDimension(marginGoneValue));
      }
  
      // @TODO: add description
      public ConstraintReference margin(int value) {
          if (mLast != null) {
              switch (mLast) {
                  case LEFT_TO_LEFT:
                  case LEFT_TO_RIGHT: {
                      mMarginLeft = value;
                  }
                  break;
                  case RIGHT_TO_LEFT:
                  case RIGHT_TO_RIGHT: {
                      mMarginRight = value;
                  }
                  break;
                  case START_TO_START:
                  case START_TO_END: {
                      mMarginStart = value;
                  }
                  break;
                  case END_TO_START:
                  case END_TO_END: {
                      mMarginEnd = value;
                  }
                  break;
                  case TOP_TO_TOP:
                  case TOP_TO_BOTTOM:
                  case TOP_TO_BASELINE: {
                      mMarginTop = value;
                  }
                  break;
                  case BOTTOM_TO_TOP:
                  case BOTTOM_TO_BOTTOM:
                  case BOTTOM_TO_BASELINE: {
                      mMarginBottom = value;
                  }
                  break;
                  case BASELINE_TO_BOTTOM:
                  case BASELINE_TO_TOP:
                  case BASELINE_TO_BASELINE: {
                      mMarginBaseline = value;
                  }
                  break;
                  case CIRCULAR_CONSTRAINT: {
                      mCircularDistance = value;
                  }
                  break;
                  default:
                      break;
              }
          } else {
              mMarginLeft = value;
              mMarginRight = value;
              mMarginStart = value;
              mMarginEnd = value;
              mMarginTop = value;
              mMarginBottom = value;
          }
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference marginGone(int value) {
          if (mLast != null) {
              switch (mLast) {
                  case LEFT_TO_LEFT:
                  case LEFT_TO_RIGHT: {
                      mMarginLeftGone = value;
                  }
                  break;
                  case RIGHT_TO_LEFT:
                  case RIGHT_TO_RIGHT: {
                      mMarginRightGone = value;
                  }
                  break;
                  case START_TO_START:
                  case START_TO_END: {
                      mMarginStartGone = value;
                  }
                  break;
                  case END_TO_START:
                  case END_TO_END: {
                      mMarginEndGone = value;
                  }
                  break;
                  case TOP_TO_TOP:
                  case TOP_TO_BOTTOM:
                  case TOP_TO_BASELINE: {
                      mMarginTopGone = value;
                  }
                  break;
                  case BOTTOM_TO_TOP:
                  case BOTTOM_TO_BOTTOM:
                  case BOTTOM_TO_BASELINE: {
                      mMarginBottomGone = value;
                  }
                  break;
                  case BASELINE_TO_TOP:
                  case BASELINE_TO_BOTTOM:
                  case BASELINE_TO_BASELINE: {
                      mMarginBaselineGone = value;
                  }
                  break;
                  default:
                      break;
              }
          } else {
              mMarginLeftGone = value;
              mMarginRightGone = value;
              mMarginStartGone = value;
              mMarginEndGone = value;
              mMarginTopGone = value;
              mMarginBottomGone = value;
          }
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference horizontalBias(float value) {
          mHorizontalBias = value;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference verticalBias(float value) {
          mVerticalBias = value;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference bias(float value) {
          if (mLast == null) {
              return this;
          }
          switch (mLast) {
              case CENTER_HORIZONTALLY:
              case LEFT_TO_LEFT:
              case LEFT_TO_RIGHT:
              case RIGHT_TO_LEFT:
              case RIGHT_TO_RIGHT:
              case START_TO_START:
              case START_TO_END:
              case END_TO_START:
              case END_TO_END: {
                  mHorizontalBias = value;
              }
              break;
              case CENTER_VERTICALLY:
              case TOP_TO_TOP:
              case TOP_TO_BOTTOM:
              case TOP_TO_BASELINE:
              case BOTTOM_TO_TOP:
              case BOTTOM_TO_BOTTOM:
              case BOTTOM_TO_BASELINE: {
                  mVerticalBias = value;
              }
              break;
              default:
                  break;
          }
          return this;
      }
  
      /**
       * Clears all constraints.
       */
      public ConstraintReference clearAll() {
          mLeftToLeft = null;
          mLeftToRight = null;
          mMarginLeft = 0;
          mRightToLeft = null;
          mRightToRight = null;
          mMarginRight = 0;
          mStartToStart = null;
          mStartToEnd = null;
          mMarginStart = 0;
          mEndToStart = null;
          mEndToEnd = null;
          mMarginEnd = 0;
          mTopToTop = null;
          mTopToBottom = null;
          mMarginTop = 0;
          mBottomToTop = null;
          mBottomToBottom = null;
          mMarginBottom = 0;
          mBaselineToBaseline = null;
          mCircularConstraint = null;
          mHorizontalBias = 0.5f;
          mVerticalBias = 0.5f;
          mMarginLeftGone = 0;
          mMarginRightGone = 0;
          mMarginStartGone = 0;
          mMarginEndGone = 0;
          mMarginTopGone = 0;
          mMarginBottomGone = 0;
          return this;
      }
  
      // @TODO: add description
      public ConstraintReference clear() {
          if (mLast != null) {
              switch (mLast) {
                  case LEFT_TO_LEFT:
                  case LEFT_TO_RIGHT: {
                      mLeftToLeft = null;
                      mLeftToRight = null;
                      mMarginLeft = 0;
                      mMarginLeftGone = 0;
                  }
                  break;
                  case RIGHT_TO_LEFT:
                  case RIGHT_TO_RIGHT: {
                      mRightToLeft = null;
                      mRightToRight = null;
                      mMarginRight = 0;
                      mMarginRightGone = 0;
                  }
                  break;
                  case START_TO_START:
                  case START_TO_END: {
                      mStartToStart = null;
                      mStartToEnd = null;
                      mMarginStart = 0;
                      mMarginStartGone = 0;
                  }
                  break;
                  case END_TO_START:
                  case END_TO_END: {
                      mEndToStart = null;
                      mEndToEnd = null;
                      mMarginEnd = 0;
                      mMarginEndGone = 0;
                  }
                  break;
                  case TOP_TO_TOP:
                  case TOP_TO_BOTTOM:
                  case TOP_TO_BASELINE: {
                      mTopToTop = null;
                      mTopToBottom = null;
                      mTopToBaseline = null;
                      mMarginTop = 0;
                      mMarginTopGone = 0;
                  }
                  break;
                  case BOTTOM_TO_TOP:
                  case BOTTOM_TO_BOTTOM:
                  case BOTTOM_TO_BASELINE: {
                      mBottomToTop = null;
                      mBottomToBottom = null;
                      mBottomToBaseline = null;
                      mMarginBottom = 0;
                      mMarginBottomGone = 0;
                  }
                  break;
                  case BASELINE_TO_BASELINE: {
                      mBaselineToBaseline = null;
                  }
                  break;
                  case CIRCULAR_CONSTRAINT: {
                      mCircularConstraint = null;
                  }
                  break;
                  default:
                      break;
              }
          } else {
              clearAll();
          }
          return this;
      }
  
      private ConstraintWidget getTarget(Object target) {
          if (target instanceof Reference) {
              Reference referenceTarget = (Reference) target;
              return referenceTarget.getConstraintWidget();
          }
          return null;
      }
  
      private void applyConnection(ConstraintWidget widget,
              Object opaqueTarget,
              State.Constraint type) {
          ConstraintWidget target = getTarget(opaqueTarget);
          if (target == null) {
              return;
          }
          switch (type) {
              // TODO: apply RTL
              default:
                  break;
          }
          switch (type) {
              case START_TO_START: {
                  widget.getAnchor(ConstraintAnchor.Type.LEFT).connect(target.getAnchor(
                          ConstraintAnchor.Type.LEFT), mMarginStart, mMarginStartGone, false);
              }
              break;
              case START_TO_END: {
                  widget.getAnchor(ConstraintAnchor.Type.LEFT).connect(target.getAnchor(
                          ConstraintAnchor.Type.RIGHT), mMarginStart, mMarginStartGone, false);
              }
              break;
              case END_TO_START: {
                  widget.getAnchor(ConstraintAnchor.Type.RIGHT).connect(target.getAnchor(
                          ConstraintAnchor.Type.LEFT), mMarginEnd, mMarginEndGone, false);
              }
              break;
              case END_TO_END: {
                  widget.getAnchor(ConstraintAnchor.Type.RIGHT).connect(target.getAnchor(
                          ConstraintAnchor.Type.RIGHT), mMarginEnd, mMarginEndGone, false);
              }
              break;
              case LEFT_TO_LEFT: {
                  widget.getAnchor(ConstraintAnchor.Type.LEFT).connect(target.getAnchor(
                          ConstraintAnchor.Type.LEFT), mMarginLeft, mMarginLeftGone, false);
              }
              break;
              case LEFT_TO_RIGHT: {
                  widget.getAnchor(ConstraintAnchor.Type.LEFT).connect(target.getAnchor(
                          ConstraintAnchor.Type.RIGHT), mMarginLeft, mMarginLeftGone, false);
              }
              break;
              case RIGHT_TO_LEFT: {
                  widget.getAnchor(ConstraintAnchor.Type.RIGHT).connect(target.getAnchor(
                          ConstraintAnchor.Type.LEFT), mMarginRight, mMarginRightGone, false);
              }
              break;
              case RIGHT_TO_RIGHT: {
                  widget.getAnchor(ConstraintAnchor.Type.RIGHT).connect(target.getAnchor(
                          ConstraintAnchor.Type.RIGHT), mMarginRight, mMarginRightGone, false);
              }
              break;
              case TOP_TO_TOP: {
                  widget.getAnchor(ConstraintAnchor.Type.TOP).connect(target.getAnchor(
                          ConstraintAnchor.Type.TOP), mMarginTop, mMarginTopGone, false);
              }
              break;
              case TOP_TO_BOTTOM: {
                  widget.getAnchor(ConstraintAnchor.Type.TOP).connect(target.getAnchor(
                          ConstraintAnchor.Type.BOTTOM), mMarginTop, mMarginTopGone, false);
              }
              break;
              case TOP_TO_BASELINE: {
                  widget.immediateConnect(ConstraintAnchor.Type.TOP, target,
                          ConstraintAnchor.Type.BASELINE, mMarginTop, mMarginTopGone);
              }
              break;
              case BOTTOM_TO_TOP: {
                  widget.getAnchor(ConstraintAnchor.Type.BOTTOM).connect(target.getAnchor(
                          ConstraintAnchor.Type.TOP), mMarginBottom, mMarginBottomGone, false);
              }
              break;
              case BOTTOM_TO_BOTTOM: {
                  widget.getAnchor(ConstraintAnchor.Type.BOTTOM).connect(target.getAnchor(
                          ConstraintAnchor.Type.BOTTOM), mMarginBottom, mMarginBottomGone, false);
              }
              break;
              case BOTTOM_TO_BASELINE: {
                  widget.immediateConnect(ConstraintAnchor.Type.BOTTOM, target,
                          ConstraintAnchor.Type.BASELINE, mMarginBottom, mMarginBottomGone);
              }
              break;
              case BASELINE_TO_BASELINE: {
                  widget.immediateConnect(ConstraintAnchor.Type.BASELINE, target,
                          ConstraintAnchor.Type.BASELINE, mMarginBaseline, mMarginBaselineGone);
              }
              break;
              case BASELINE_TO_TOP: {
                  widget.immediateConnect(ConstraintAnchor.Type.BASELINE,
                          target, ConstraintAnchor.Type.TOP, mMarginBaseline, mMarginBaselineGone);
              }
              break;
              case BASELINE_TO_BOTTOM: {
                  widget.immediateConnect(ConstraintAnchor.Type.BASELINE, target,
                          ConstraintAnchor.Type.BOTTOM, mMarginBaseline, mMarginBaselineGone);
              }
              break;
              case CIRCULAR_CONSTRAINT: {
                  widget.connectCircularConstraint(target, mCircularAngle, (int) mCircularDistance);
              }
              break;
              default:
                  break;
          }
      }
  
      /**
       * apply all the constraints attributes of the mConstraintWidget
       */
      public void applyWidgetConstraints() {
          applyConnection(mConstraintWidget, mLeftToLeft, State.Constraint.LEFT_TO_LEFT);
          applyConnection(mConstraintWidget, mLeftToRight, State.Constraint.LEFT_TO_RIGHT);
          applyConnection(mConstraintWidget, mRightToLeft, State.Constraint.RIGHT_TO_LEFT);
          applyConnection(mConstraintWidget, mRightToRight, State.Constraint.RIGHT_TO_RIGHT);
          applyConnection(mConstraintWidget, mStartToStart, State.Constraint.START_TO_START);
          applyConnection(mConstraintWidget, mStartToEnd, State.Constraint.START_TO_END);
          applyConnection(mConstraintWidget, mEndToStart, State.Constraint.END_TO_START);
          applyConnection(mConstraintWidget, mEndToEnd, State.Constraint.END_TO_END);
          applyConnection(mConstraintWidget, mTopToTop, State.Constraint.TOP_TO_TOP);
          applyConnection(mConstraintWidget, mTopToBottom, State.Constraint.TOP_TO_BOTTOM);
          applyConnection(mConstraintWidget, mTopToBaseline, State.Constraint.TOP_TO_BASELINE);
          applyConnection(mConstraintWidget, mBottomToTop, State.Constraint.BOTTOM_TO_TOP);
          applyConnection(mConstraintWidget, mBottomToBottom, State.Constraint.BOTTOM_TO_BOTTOM);
          applyConnection(mConstraintWidget, mBottomToBaseline, State.Constraint.BOTTOM_TO_BASELINE);
          applyConnection(mConstraintWidget, mBaselineToBaseline,
                  State.Constraint.BASELINE_TO_BASELINE);
          applyConnection(mConstraintWidget, mBaselineToTop, State.Constraint.BASELINE_TO_TOP);
          applyConnection(mConstraintWidget, mBaselineToBottom, State.Constraint.BASELINE_TO_BOTTOM);
          applyConnection(mConstraintWidget, mCircularConstraint,
                  State.Constraint.CIRCULAR_CONSTRAINT);
      }
  
      // @TODO: add description
      @Override
      public void apply() {
          if (mConstraintWidget == null) {
              return;
          }
          if (mFacade != null) {
              mFacade.apply();
          }
          mHorizontalDimension.apply(mState, mConstraintWidget, HORIZONTAL);
          mVerticalDimension.apply(mState, mConstraintWidget, VERTICAL);
          dereference();
  
          applyWidgetConstraints();
  
          if (mHorizontalChainStyle != ConstraintWidget.CHAIN_SPREAD) {
              mConstraintWidget.setHorizontalChainStyle(mHorizontalChainStyle);
          }
          if (mVerticalChainStyle != ConstraintWidget.CHAIN_SPREAD) {
              mConstraintWidget.setVerticalChainStyle(mVerticalChainStyle);
          }
          if (mHorizontalChainWeight != UNKNOWN) {
              mConstraintWidget.setHorizontalWeight(mHorizontalChainWeight);
          }
          if (mVerticalChainWeight != UNKNOWN) {
              mConstraintWidget.setVerticalWeight(mVerticalChainWeight);
          }
  
          mConstraintWidget.setHorizontalBiasPercent(mHorizontalBias);
          mConstraintWidget.setVerticalBiasPercent(mVerticalBias);
  
          mConstraintWidget.frame.pivotX = mPivotX;
          mConstraintWidget.frame.pivotY = mPivotY;
          mConstraintWidget.frame.rotationX = mRotationX;
          mConstraintWidget.frame.rotationY = mRotationY;
          mConstraintWidget.frame.rotationZ = mRotationZ;
          mConstraintWidget.frame.translationX = mTranslationX;
          mConstraintWidget.frame.translationY = mTranslationY;
          mConstraintWidget.frame.translationZ = mTranslationZ;
          mConstraintWidget.frame.scaleX = mScaleX;
          mConstraintWidget.frame.scaleY = mScaleY;
          mConstraintWidget.frame.alpha = mAlpha;
          mConstraintWidget.frame.visibility = mVisibility;
          mConstraintWidget.setVisibility(mVisibility);
          mConstraintWidget.frame.setMotionAttributes(mMotionProperties);
          if (mCustomColors != null) {
              for (String key : mCustomColors.keySet()) {
                  Integer color = mCustomColors.get(key);
                  mConstraintWidget.frame.setCustomAttribute(key,
                          TypedValues.Custom.TYPE_COLOR, color);
              }
          }
          if (mCustomFloats != null) {
              for (String key : mCustomFloats.keySet()) {
                  float value = mCustomFloats.get(key);
                  mConstraintWidget.frame.setCustomAttribute(key,
                          TypedValues.Custom.TYPE_FLOAT, value);
              }
          }
      }
  }
  