content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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 org.apache.weex.ui.animation;
  
  import android.animation.PropertyValuesHolder;
  import android.support.annotation.NonNull;
  import android.support.annotation.Nullable;
  import android.support.v4.util.ArrayMap;
  import android.text.TextUtils;
  import android.util.Pair;
  import android.util.Property;
  import android.view.View;
  import org.apache.weex.WXSDKInstance;
  import org.apache.weex.common.Constants;
  import org.apache.weex.common.Constants.Name;
  import org.apache.weex.utils.FunctionParser;
  import org.apache.weex.utils.WXUtils;
  import org.apache.weex.utils.WXViewUtils;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.LinkedHashMap;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  
  public class WXAnimationBean {
  
    public final static String LINEAR = "linear";
    public final static String EASE_IN_OUT = "ease-in-out";
    public final static String EASE_IN = "ease-in";
    public final static String EASE_OUT = "ease-out";
    public final static String CUBIC_BEZIER = "cubic-bezier";
    public final static int NUM_CUBIC_PARAM = 4;
    public long delay;
    public long duration;
    public String timingFunction;
    public @Nullable Style styles;
    public boolean needLayout;
  
    public static class Style {
  
      public final static String WX_TRANSLATE = "translate";
      public final static String WX_TRANSLATE_X = "translateX";
      public final static String WX_TRANSLATE_Y = "translateY";
      public final static String WX_ROTATE = "rotate";
      public final static String WX_SCALE_X = "scaleX";
      public final static String WX_SCALE_Y = "scaleY";
      public final static String WX_SCALE = "scale";
      public final static String WX_ROTATE_X ="rotateX";
      public final static String WX_ROTATE_Y ="rotateY";
      public final static String BACKGROUND_COLOR = Constants.Name.BACKGROUND_COLOR;
      public final static String WIDTH = Constants.Name.WIDTH;
      public final static String HEIGHT = Constants.Name.HEIGHT;
      public final static String TOP = "top";
      public final static String BOTTOM = "bottom";
      public final static String RIGHT = "right";
      public final static String LEFT = "left";
      public final static String CENTER = "center";
      private static final String HALF = "50%";
      private static final String FULL = "100%";
      private static final String ZERO = "0%";
      private static final String PX = "px";
      private static final String DEG = "deg";
      public static Map<String, List<Property<View,Float>>> wxToAndroidMap = new ArrayMap<>();
      private static Map<Property<View, Float>, Float> defaultMap= new ArrayMap<>();
  
  
      static {
        wxToAndroidMap.put(WX_TRANSLATE, Arrays.asList
                (View.TRANSLATION_X, View.TRANSLATION_Y));
        wxToAndroidMap.put(WX_TRANSLATE_X, Collections.singletonList(View.TRANSLATION_X));
        wxToAndroidMap.put(WX_TRANSLATE_Y, Collections.singletonList(View.TRANSLATION_Y));
        wxToAndroidMap.put(WX_ROTATE, Collections.singletonList(View.ROTATION));
        wxToAndroidMap.put(WX_ROTATE_X, Collections.singletonList(View.ROTATION_X));
        wxToAndroidMap.put(WX_ROTATE_Y, Collections.singletonList(View.ROTATION_Y));
        wxToAndroidMap.put(WX_SCALE, Arrays.asList(View.SCALE_X, View.SCALE_Y));
        wxToAndroidMap.put(WX_SCALE_X, Collections.singletonList(View.SCALE_X));
        wxToAndroidMap.put(WX_SCALE_Y, Collections.singletonList(View.SCALE_Y));
        wxToAndroidMap.put(Name.PERSPECTIVE, Collections.singletonList(CameraDistanceProperty.getInstance()));
        wxToAndroidMap = Collections.unmodifiableMap(wxToAndroidMap);
        defaultMap.put(View.TRANSLATION_X, 0f);
        defaultMap.put(View.TRANSLATION_Y, 0f);
        defaultMap.put(View.SCALE_X, 1f);
        defaultMap.put(View.SCALE_Y, 1f);
        defaultMap.put(View.ROTATION, 0f);
        defaultMap.put(View.ROTATION_X, 0f);
        defaultMap.put(View.ROTATION_Y, 0f);
      }
  
      public String opacity;
      public String backgroundColor;
      public String width;
      public String height;
      public String transform;
      public String transformOrigin;
      private Map<Property<View, Float>, Float> transformMap = new LinkedHashMap<>();
      private Pair<Float, Float> pivot;
      private List<PropertyValuesHolder> holders=new LinkedList<>();
      private float cameraDistance = Float.MAX_VALUE;
  
  
      private static Pair<Float, Float> parsePivot(@Nullable String transformOrigin,
                                                   int width, int height,int viewportW) {
        if (!TextUtils.isEmpty(transformOrigin)) {
          int firstSpace = transformOrigin.indexOf(FunctionParser.SPACE);
          if (firstSpace != -1) {
            int i = firstSpace;
            for (; i < transformOrigin.length(); i++) {
              if (transformOrigin.charAt(i) != FunctionParser.SPACE) {
                break;
              }
            }
            if (i < transformOrigin.length() && transformOrigin.charAt(i) != FunctionParser.SPACE) {
              List<String> list = new ArrayList<>(2);
              list.add(transformOrigin.substring(0, firstSpace).trim());
              list.add(transformOrigin.substring(i, transformOrigin.length()).trim());
              return parsePivot(list, width, height,viewportW);
            }
          }
        }
        return null;
      }
  
      private static Pair<Float, Float> parsePivot(@NonNull List<String> list, int width, int height,int viewportW) {
        return new Pair<>(
                parsePivotX(list.get(0), width,viewportW), parsePivotY(list.get(1), height,viewportW));
      }
  
      private static float parsePivotX(String x, int width,int viewportW) {
        String value = x;
        if (WXAnimationBean.Style.LEFT.equals(x)) {
          value = ZERO;
        } else if (WXAnimationBean.Style.RIGHT.equals(x)) {
          value = FULL;
        } else if (WXAnimationBean.Style.CENTER.equals(x)) {
          value = HALF;
        }
        return parsePercentOrPx(value, width,viewportW);
      }
  
      private static float parsePivotY(String y, int height,int viewportW) {
        String value = y;
        if (WXAnimationBean.Style.TOP.equals(y)) {
          value = ZERO;
        } else if (WXAnimationBean.Style.BOTTOM.equals(y)) {
          value = FULL;
        } else if (WXAnimationBean.Style.CENTER.equals(y)) {
          value = HALF;
        }
        return parsePercentOrPx(value, height,viewportW);
      }
  
      private static float parsePercentOrPx(String raw, int unit,int viewportW) {
        final int precision = 1;
        int suffix;
        if ((suffix = raw.lastIndexOf(WXUtils.PERCENT)) != -1) {
          return parsePercent(raw.substring(0, suffix), unit, precision);
        } else if ((suffix = raw.lastIndexOf(PX)) != -1) {
          return WXViewUtils.getRealPxByWidth(WXUtils.fastGetFloat(raw.substring(0, suffix), precision),viewportW);
        }
        return WXViewUtils.getRealPxByWidth(WXUtils.fastGetFloat(raw, precision),viewportW);
      }
  
      private static float parsePercent(String percent, int unit, int precision) {
        return WXUtils.fastGetFloat(percent, precision) / 100 * unit;
      }
  
      private void resetToDefaultIfAbsent() {
        for (Entry<Property<View, Float>, Float> entry : defaultMap.entrySet()) {
          if (!transformMap.containsKey(entry.getKey())) {
            transformMap.put(entry.getKey(), entry.getValue());
          }
        }
      }
  
      public @Nullable Pair<Float, Float> getPivot() {
        return pivot;
      }
  
      public void init(@Nullable String transformOrigin,@Nullable String rawTransform,
                       final int width, final int height,int viewportW, WXSDKInstance instance){
        pivot = parsePivot(transformOrigin,width,height,viewportW);
        transformMap.putAll(TransformParser.parseTransForm(instance.getInstanceId(), rawTransform, width,height,viewportW));
        resetToDefaultIfAbsent();
        if (transformMap.containsKey(CameraDistanceProperty.getInstance())) {
          cameraDistance = transformMap.remove(CameraDistanceProperty.getInstance());
        }
        initHolders();
      }
  
      /**
       * Use this method to init if you already have a list of Property
       * The key is something like {@link View#TRANSLATION_X} and the value is a {@link Pair},
       * of which the first is beginning value and the second is ending value.
       * @param styles a list of Property
       */
      public void init(@NonNull Map<Property<View, Float>, Pair<Float, Float>> styles){
        for(Entry<Property<View, Float>, Pair<Float, Float>> entry:styles.entrySet()){
          holders.add(PropertyValuesHolder.ofFloat(entry.getKey(), entry.getValue().first, entry.getValue().second));
        }
      }
  
      private void initHolders(){
        for (Map.Entry<Property<View, Float>, Float> entry : transformMap.entrySet()) {
          holders.add(PropertyValuesHolder.ofFloat(entry.getKey(), entry.getValue()));
        }
        if (!TextUtils.isEmpty(opacity)) {
          holders.add(PropertyValuesHolder.ofFloat(View.ALPHA, WXUtils.fastGetFloat(opacity, 3)));
        }
      }
  
      public List<PropertyValuesHolder> getHolders(){
        return holders;
      }
  
      public float getCameraDistance(){
        return cameraDistance;
      }
    }
  }
  
  