content: /*
   * Copyright (C) 2020 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.motion.utils;
  
  import androidx.constraintlayout.core.motion.CustomAttribute;
  import androidx.constraintlayout.core.motion.CustomVariable;
  import androidx.constraintlayout.core.motion.MotionWidget;
  import androidx.constraintlayout.core.state.WidgetFrame;
  
  import java.text.DecimalFormat;
  import java.util.Arrays;
  
  /**
   * This engine allows manipulation of attributes by Curves
   *
   *
   */
  
  public abstract class SplineSet {
      private static final String TAG = "SplineSet";
      protected CurveFit mCurveFit;
      protected int[] mTimePoints = new int[10];
      protected float[] mValues = new float[10];
      private int mCount;
      private String mType;
  
      // @TODO: add description
      public void setProperty(TypedValues widget, float t) {
          widget.setValue(TypedValues.AttributesType.getId(mType), get(t));
      }
  
      @Override
      public String toString() {
          String str = mType;
          DecimalFormat df = new DecimalFormat("##.##");
          for (int i = 0; i < mCount; i++) {
              str += "[" + mTimePoints[i] + " , " + df.format(mValues[i]) + "] ";
  
          }
          return str;
      }
  
      public void setType(String type) {
          mType = type;
      }
  
      // @TODO: add description
      public float get(float t) {
          return (float) mCurveFit.getPos(t, 0);
      }
  
      // @TODO: add description
      public float getSlope(float t) {
          return (float) mCurveFit.getSlope(t, 0);
      }
  
      public CurveFit getCurveFit() {
          return mCurveFit;
      }
  
      // @TODO: add description
      public void setPoint(int position, float value) {
          if (mTimePoints.length < mCount + 1) {
              mTimePoints = Arrays.copyOf(mTimePoints, mTimePoints.length * 2);
              mValues = Arrays.copyOf(mValues, mValues.length * 2);
          }
          mTimePoints[mCount] = position;
          mValues[mCount] = value;
          mCount++;
      }
  
      // @TODO: add description
      public void setup(int curveType) {
          if (mCount == 0) {
              return;
          }
  
          Sort.doubleQuickSort(mTimePoints, mValues, 0, mCount - 1);
  
          int unique = 1;
  
          for (int i = 1; i < mCount; i++) {
              if (mTimePoints[i - 1] != mTimePoints[i]) {
                  unique++;
              }
          }
  
          double[] time = new double[unique];
          double[][] values = new double[unique][1];
          int k = 0;
          for (int i = 0; i < mCount; i++) {
              if (i > 0 && mTimePoints[i] == mTimePoints[i - 1]) {
                  continue;
              }
  
              time[k] = mTimePoints[i] * 1E-2;
              values[k][0] = mValues[i];
              k++;
          }
          mCurveFit = CurveFit.get(curveType, time, values);
      }
  
      // @TODO: add description
      public static SplineSet makeCustomSpline(String str, KeyFrameArray.CustomArray attrList) {
          return new CustomSet(str, attrList);
      }
  
      // @TODO: add description
      public static SplineSet makeCustomSplineSet(String str, KeyFrameArray.CustomVar attrList) {
          return new CustomSpline(str, attrList);
      }
  
      // @TODO: add description
      public static SplineSet makeSpline(String str, long currentTime) {
  
          return new CoreSpline(str, currentTime);
      }
  
      private static class Sort {
  
          static void doubleQuickSort(int[] key, float[] value, int low, int hi) {
              int[] stack = new int[key.length + 10];
              int count = 0;
              stack[count++] = hi;
              stack[count++] = low;
              while (count > 0) {
                  low = stack[--count];
                  hi = stack[--count];
                  if (low < hi) {
                      int p = partition(key, value, low, hi);
                      stack[count++] = p - 1;
                      stack[count++] = low;
                      stack[count++] = hi;
                      stack[count++] = p + 1;
                  }
              }
          }
  
          private static int partition(int[] array, float[] value, int low, int hi) {
              int pivot = array[hi];
              int i = low;
              for (int j = low; j < hi; j++) {
                  if (array[j] <= pivot) {
                      swap(array, value, i, j);
                      i++;
                  }
              }
              swap(array, value, i, hi);
              return i;
          }
  
          private static void swap(int[] array, float[] value, int a, int b) {
              int tmp = array[a];
              array[a] = array[b];
              array[b] = tmp;
              float tmpv = value[a];
              value[a] = value[b];
              value[b] = tmpv;
          }
      }
  
  
      public static class CustomSet extends SplineSet {
          String mAttributeName;
          KeyFrameArray.CustomArray mConstraintAttributeList;
          float[] mTempValues;
  
          @SuppressWarnings("StringSplitter")
          public CustomSet(String attribute, KeyFrameArray.CustomArray attrList) {
              mAttributeName = attribute.split(",")[1];
              mConstraintAttributeList = attrList;
          }
  
          // @TODO: add description
          @Override
          public void setup(int curveType) {
              int size = mConstraintAttributeList.size();
              int dimensionality = mConstraintAttributeList.valueAt(0).numberOfInterpolatedValues();
              double[] time = new double[size];
              mTempValues = new float[dimensionality];
              double[][] values = new double[size][dimensionality];
              for (int i = 0; i < size; i++) {
  
                  int key = mConstraintAttributeList.keyAt(i);
                  CustomAttribute ca = mConstraintAttributeList.valueAt(i);
  
                  time[i] = key * 1E-2;
                  ca.getValuesToInterpolate(mTempValues);
                  for (int k = 0; k < mTempValues.length; k++) {
                      values[i][k] = mTempValues[k];
                  }
  
              }
              mCurveFit = CurveFit.get(curveType, time, values);
          }
  
          // @TODO: add description
          @Override
          public void setPoint(int position, float value) {
              throw new RuntimeException("don't call for custom "
                      + "attribute call setPoint(pos, ConstraintAttribute)");
          }
  
          // @TODO: add description
          public void setPoint(int position, CustomAttribute value) {
              mConstraintAttributeList.append(position, value);
          }
  
          // @TODO: add description
          public void setProperty(WidgetFrame view, float t) {
              mCurveFit.getPos(t, mTempValues);
              view.setCustomValue(mConstraintAttributeList.valueAt(0), mTempValues);
          }
      }
  
  
      private static class CoreSpline extends SplineSet {
          String mType;
          @SuppressWarnings("unused") long mStart;
  
          CoreSpline(String str, long currentTime) {
              mType = str;
              mStart = currentTime;
          }
  
          @Override
          public void setProperty(TypedValues widget, float t) {
              int id = widget.getId(mType);
              widget.setValue(id, get(t));
          }
      }
  
      public static class CustomSpline extends SplineSet {
          String mAttributeName;
          KeyFrameArray.CustomVar mConstraintAttributeList;
          float[] mTempValues;
  
          @SuppressWarnings("StringSplitter")
          public CustomSpline(String attribute, KeyFrameArray.CustomVar attrList) {
              mAttributeName = attribute.split(",")[1];
              mConstraintAttributeList = attrList;
          }
  
          // @TODO: add description
          @Override
          public void setup(int curveType) {
              int size = mConstraintAttributeList.size();
              int dimensionality = mConstraintAttributeList.valueAt(0).numberOfInterpolatedValues();
              double[] time = new double[size];
              mTempValues = new float[dimensionality];
              double[][] values = new double[size][dimensionality];
              for (int i = 0; i < size; i++) {
  
                  int key = mConstraintAttributeList.keyAt(i);
                  CustomVariable ca = mConstraintAttributeList.valueAt(i);
  
                  time[i] = key * 1E-2;
                  ca.getValuesToInterpolate(mTempValues);
                  for (int k = 0; k < mTempValues.length; k++) {
                      values[i][k] = mTempValues[k];
                  }
  
              }
              mCurveFit = CurveFit.get(curveType, time, values);
          }
  
          // @TODO: add description
          @Override
          public void setPoint(int position, float value) {
              throw new RuntimeException("don't call for custom attribute"
                      + " call setPoint(pos, ConstraintAttribute)");
          }
  
          // @TODO: add description
          @Override
          public void setProperty(TypedValues widget, float t) {
              setProperty((MotionWidget) widget, t);
          }
  
          // @TODO: add description
          public void setPoint(int position, CustomVariable value) {
              mConstraintAttributeList.append(position, value);
          }
  
          // @TODO: add description
          public void setProperty(MotionWidget view, float t) {
              mCurveFit.getPos(t, mTempValues);
              mConstraintAttributeList.valueAt(0).setInterpolatedValue(view, mTempValues);
          }
      }
  
  }
  