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.dom.transition;
  
  import android.animation.Animator;
  import android.animation.AnimatorListenerAdapter;
  import android.animation.ArgbEvaluator;
  import android.animation.ObjectAnimator;
  import android.animation.PropertyValuesHolder;
  import android.animation.ValueAnimator;
  import android.graphics.drawable.ColorDrawable;
  import android.os.Handler;
  import android.support.v4.util.ArrayMap;
  import android.support.v4.view.animation.PathInterpolatorCompat;
  import android.text.TextUtils;
  import android.util.Property;
  import android.view.View;
  import android.view.animation.Interpolator;
  
  import org.apache.weex.WXEnvironment;
  import org.apache.weex.WXSDKManager;
  import org.apache.weex.bridge.WXBridgeManager;
  import org.apache.weex.common.Constants;
  import org.apache.weex.dom.CSSShorthand;
  import org.apache.weex.ui.animation.BackgroundColorProperty;
  import org.apache.weex.ui.animation.TransformParser;
  import org.apache.weex.ui.component.WXComponent;
  import org.apache.weex.utils.SingleFunctionParser;
  import org.apache.weex.utils.WXLogUtils;
  import org.apache.weex.utils.WXResourceUtils;
  import org.apache.weex.utils.WXUtils;
  import org.apache.weex.utils.WXViewUtils;
  
  import java.util.ArrayList;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.concurrent.atomic.AtomicInteger;
  import java.util.regex.Pattern;
  import org.apache.weex.common.Constants.TimeFunction;
  
  /**
   *   transition on dom thread
   *   transition-property: height;
   *   transition-duration: .3s;
   *   transition-delay: .05s;
   *   transition-timing-function: ease-in-out;
   *
   *   Created by furture on 2017/10/18.
   */
  public class WXTransition {
  
      public static final  String TRANSITION_PROPERTY = "transitionProperty";
      public static final  String TRANSITION_DURATION = "transitionDuration";
      public static final  String TRANSITION_DELAY = "transitionDelay";
      public static final  String TRANSITION_TIMING_FUNCTION = "transitionTimingFunction";
  
      public static final  Pattern PROPERTY_SPLIT_PATTERN = Pattern.compile("\\||,");
  
  
      /**
       * layout animation property
       * */
      private static final Set<String> LAYOUT_PROPERTIES = new HashSet<>();
      static {
          LAYOUT_PROPERTIES.add(Constants.Name.WIDTH);
          LAYOUT_PROPERTIES.add(Constants.Name.HEIGHT);
          LAYOUT_PROPERTIES.add(Constants.Name.MARGIN_TOP);
          LAYOUT_PROPERTIES.add(Constants.Name.MARGIN_BOTTOM);
          LAYOUT_PROPERTIES.add(Constants.Name.MARGIN_LEFT);
          LAYOUT_PROPERTIES.add(Constants.Name.MARGIN_RIGHT);
          LAYOUT_PROPERTIES.add(Constants.Name.LEFT);
          LAYOUT_PROPERTIES.add(Constants.Name.RIGHT);
          LAYOUT_PROPERTIES.add(Constants.Name.TOP);
          LAYOUT_PROPERTIES.add(Constants.Name.BOTTOM);
          LAYOUT_PROPERTIES.add(Constants.Name.PADDING_LEFT);
          LAYOUT_PROPERTIES.add(Constants.Name.PADDING_RIGHT);
          LAYOUT_PROPERTIES.add(Constants.Name.PADDING_TOP);
          LAYOUT_PROPERTIES.add(Constants.Name.PADDING_BOTTOM);
      }
  
      /**
       * transform animation property, use android system animaton ability
       * */
      private static final Set<String> TRANSFORM_PROPERTIES = new HashSet<>();
      static {
          TRANSFORM_PROPERTIES.add(Constants.Name.OPACITY);
          TRANSFORM_PROPERTIES.add(Constants.Name.BACKGROUND_COLOR);
          TRANSFORM_PROPERTIES.add(Constants.Name.TRANSFORM);
      }
  
      private List<String> properties;
      private Interpolator  interpolator;
      private long  duration;
      private long delay;
      private WXComponent mWXComponent;
      private Handler handler;
      private ValueAnimator layoutValueAnimator;
      private Map<String, Object> layoutPendingUpdates;
      private ObjectAnimator transformAnimator;
      private Map<String, Object> transformPendingUpdates;
      private Runnable transitionEndEvent;
      private Map<String, Object> targetStyles;
      private Runnable animationRunnable;
  
      private Runnable transformAnimationRunnable;
      private volatile AtomicInteger lockToken = new AtomicInteger(0);
  
  
      public WXTransition() {
          this.properties = new ArrayList<>(4);
          this.handler = new Handler();
          this.layoutPendingUpdates = new ArrayMap<>();
          this.transformPendingUpdates = new ArrayMap<>();
          this.targetStyles = new ArrayMap<>();
      }
  
      /**
       * create transition from map styles if style contains transitionProperty
       * */
      public static WXTransition fromMap(Map<String, Object> style, WXComponent component){
          if(style.get(TRANSITION_PROPERTY) == null){
              return  null;
          }
          String propertyString = WXUtils.getString(style.get(TRANSITION_PROPERTY), null);
          if(propertyString == null){
              return null;
          }
          WXTransition transition  = new WXTransition();
          updateTransitionProperties(transition, propertyString);
          if(transition.properties.isEmpty()){
              return  null;
          }
          transition.duration = parseTimeMillis(style, TRANSITION_DURATION, 0);
          transition.delay =  parseTimeMillis(style, TRANSITION_DELAY, 0);
          transition.interpolator = createTimeInterpolator(WXUtils.getString(style.get(TRANSITION_TIMING_FUNCTION), null));
          transition.mWXComponent = component;
          return  transition;
      }
  
      /**
       * check updates has transition property
       * */
      public boolean hasTransitionProperty(Map<String, Object> styles){
          for(String property : properties){
              if(styles.containsKey(property)){
                  return  true;
              }
          }
          return false;
      }
  
      public void updateTranstionParams(Map<String, Object> updates){
          if(updates.containsKey(TRANSITION_DELAY)){
              mWXComponent.getStyles().put(TRANSITION_DELAY, updates.remove(TRANSITION_DELAY));
              this.delay = parseTimeMillis(mWXComponent.getStyles(), TRANSITION_DELAY, 0);
          }
  
          if(updates.containsKey(TRANSITION_TIMING_FUNCTION) && updates.get(TRANSITION_TIMING_FUNCTION) != null){
              mWXComponent.getStyles().put(TRANSITION_TIMING_FUNCTION, updates.remove(TRANSITION_TIMING_FUNCTION));
              this.interpolator = createTimeInterpolator(mWXComponent.getStyles().get(TRANSITION_TIMING_FUNCTION).toString());
          }
  
          if(updates.containsKey(TRANSITION_DURATION)){
              mWXComponent.getStyles().put(TRANSITION_DURATION, updates.remove(TRANSITION_DURATION));
              this.duration = parseTimeMillis(mWXComponent.getStyles(), TRANSITION_DURATION, 0);
          }
  
          if(updates.containsKey(TRANSITION_PROPERTY)){
              mWXComponent.getStyles().put(TRANSITION_PROPERTY, updates.remove(TRANSITION_PROPERTY));
              updateTransitionProperties(this, WXUtils.getString(mWXComponent.getStyles().get(TRANSITION_PROPERTY), null));
          }
      }
  
      /**
       * start transition animation, updates maybe split two different updates,
       * because javascript will send multi update on same transition, we assume that updates in 8ms is one transition
       * */
      public void  startTransition(Map<String, Object> updates){
          synchronized (lockToken){
              final View taregtView = getTargetView();
              if(taregtView == null){
                  return;
              }
              final int token = lockToken.incrementAndGet();
              for(String property : properties){
                  if(updates.containsKey(property)){
                      Object targetValue = updates.remove(property);
                      if(LAYOUT_PROPERTIES.contains(property)) {
                          layoutPendingUpdates.put(property, targetValue);
                      }else if(TRANSFORM_PROPERTIES.contains(property)){
                          transformPendingUpdates.put(property, targetValue);
                      }
                  }
              }
  
              int delay = WXUtils.getNumberInt(mWXComponent.getAttrs().get("actionDelay"), 16);
              if(delay > duration){
                  delay = (int) duration;
              }
              if(animationRunnable != null) {
                  handler.removeCallbacks(animationRunnable);
              }
              animationRunnable = new Runnable() {
                  @Override
                  public void run() {
                      if(token == lockToken.get()) {
                          doTransitionAnimation(token);
                      }
                      animationRunnable = null;
                  }
              };
              if(delay > 0){
                  handler.postDelayed(animationRunnable, delay);
              }else{
                  animationRunnable.run();
              }
          }
      }
  
      /**
       * doTransitionAnimation include transform and layout animation.
       * 1. put pre transition updates from target style to dom style
       * 2. do transform animation and layout animation
       * */
      private void  doTransitionAnimation(final  int token){
          final View taregtView = getTargetView();
          if(taregtView == null){
              return;
          }
          if(targetStyles.size() > 0){
              for(String property : properties){
                  if(!(LAYOUT_PROPERTIES.contains(property) || TRANSFORM_PROPERTIES.contains(property))){
                      continue;
                  }
                  if(layoutPendingUpdates.containsKey(property)){
                      continue;
                  }
                  if(transformPendingUpdates.containsKey(property)){
                      continue;
                  }
                  synchronized (targetStyles){
                      if(targetStyles.containsKey(property)){
                          //reset pre transition style
                          Object targetValue = targetStyles.remove(property);
                          mWXComponent.getStyles().put(property, targetValue);
  //                        domObject.getStyles().put(property, targetValue);
  //                        WXComponent component = getComponent();
  //                        if(component != null && component.getDomObject() != null){
  //                            component.getDomObject().getStyles().put(property, targetValue);
  //                        }
                      }
                  }
              }
          }
  
  
  
          if(transitionEndEvent != null){
              taregtView.removeCallbacks(transitionEndEvent);
          }
          if(transitionEndEvent == null && duration > Float.MIN_NORMAL){
              transitionEndEvent = new Runnable(){
                  @Override
                  public void run() {
                      transitionEndEvent = null;
                      if(duration < Float.MIN_NORMAL){
                          return;
                      }
                      if(mWXComponent != null && mWXComponent.getEvents().contains(Constants.Event.ON_TRANSITION_END)){
                          mWXComponent.fireEvent(Constants.Event.ON_TRANSITION_END);
                      }
                  }
              };
          }
          if(transformAnimationRunnable != null) {
              taregtView.removeCallbacks(transformAnimationRunnable);
          }
          transformAnimationRunnable = new Runnable() {
              @Override
              public void run() {
                  synchronized (lockToken) {
                      if(token == lockToken.get()) {
                          doPendingTransformAnimation(token);
                      }
                  }
              }
          };
          taregtView.post(transformAnimationRunnable);
          doPendingLayoutAnimation();
      }
  
  
      /**
       *  transform, opacity, backgroundcolor which not effect layout use android system animation in main thread.
       * */
      private void doPendingTransformAnimation(int token) {
          if(transformAnimator != null){
              transformAnimator.cancel();
              transformAnimator = null;
          }
          if(transformPendingUpdates.size() == 0){
              return;
          }
          final View taregtView = getTargetView();
          if(taregtView == null){
              return;
          }
          List<PropertyValuesHolder> holders = new ArrayList<>(8);
          String transform  = WXUtils.getString(transformPendingUpdates.remove(Constants.Name.TRANSFORM), null);
          if(!TextUtils.isEmpty(transform)){
              Map<Property<View,Float>, Float>  properties = TransformParser
                  .parseTransForm(mWXComponent.getInstanceId(), transform, (int)mWXComponent.getLayoutWidth(), (int)mWXComponent.getLayoutHeight(), mWXComponent.getViewPortWidth());
              PropertyValuesHolder[]  transformHolders = TransformParser.toHolders(properties);
              for(PropertyValuesHolder holder : transformHolders){
                  holders.add(holder);
              }
              synchronized (targetStyles) {
                  targetStyles.put(Constants.Name.TRANSFORM, transform);
              }
          }
  
          for(String property : properties){
              if(!TRANSFORM_PROPERTIES.contains(property)){
                  continue;
              }
              if(!transformPendingUpdates.containsKey(property)){
                  continue;
              }
              Object value = transformPendingUpdates.remove(property);
              synchronized (targetStyles) {
                  targetStyles.put(property, value);
              }
              switch (property){
                  case Constants.Name.OPACITY:{
                      holders.add(PropertyValuesHolder.ofFloat(View.ALPHA, taregtView.getAlpha(), WXUtils.getFloat(value, 1.0f)));
                      taregtView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); //hardware or none has bug on some platform
                  }
                  break;
                  case Constants.Name.BACKGROUND_COLOR:{
                      int fromColor = WXResourceUtils.getColor(WXUtils.getString(mWXComponent.getStyles().getBackgroundColor(), null), 0);
                      int toColor = WXResourceUtils.getColor(WXUtils.getString(value, null), 0);
                      if(WXViewUtils.getBorderDrawable(taregtView) != null){
                          fromColor = WXViewUtils.getBorderDrawable(taregtView).getColor();
                      }else if (taregtView.getBackground() instanceof ColorDrawable) {
                          fromColor = ((ColorDrawable) taregtView.getBackground()).getColor();
                      }
                      holders.add(PropertyValuesHolder.ofObject(new BackgroundColorProperty(), new ArgbEvaluator(), fromColor,toColor));
                  }
                  break;
                  default:break;
              }
          }
  
          if(token == lockToken.get()) {
              transformPendingUpdates.clear();
          }
          transformAnimator =  ObjectAnimator.ofPropertyValuesHolder(taregtView, holders.toArray(new PropertyValuesHolder[holders.size()]));
          transformAnimator.setDuration((long) duration);
          if((long) delay > 0) {
              transformAnimator.setStartDelay((long) delay);
          }
          if(interpolator != null) {
              transformAnimator.setInterpolator(interpolator);
          }
          transformAnimator.addListener(new AnimatorListenerAdapter() {
              boolean  hasCancel = false;
              @Override
              public void onAnimationCancel(Animator animation) {
                  super.onAnimationCancel(animation);
                  hasCancel = true;
              }
  
              @Override
              public void onAnimationEnd(Animator animation) {
                  if(hasCancel){
                      return;
                  }
                  super.onAnimationEnd(animation);
                  WXTransition.this.onTransitionAnimationEnd();
              }
          });
          transformAnimator.start();
      }
  
  
      public void doPendingLayoutAnimation(){
          if(layoutValueAnimator != null){
              layoutValueAnimator.cancel();
              layoutValueAnimator = null;
          }
          if(layoutPendingUpdates.size() == 0){
              return;
          }
          PropertyValuesHolder[] holders = new PropertyValuesHolder[layoutPendingUpdates.size()];
          int index = 0;
          for(String property : properties){
              if(!LAYOUT_PROPERTIES.contains(property)){
                  continue;
              }
              if(layoutPendingUpdates.containsKey(property)){
                  Object targetValue = layoutPendingUpdates.remove(property);
                  synchronized (targetStyles) {
                      targetStyles.put(property, targetValue);
                  }
                  holders[index] = createLayoutPropertyValueHolder(property, targetValue);
                  index++;
              }
          }
          layoutPendingUpdates.clear();
          doLayoutPropertyValuesHolderAnimation(holders);
      }
  
  
      private PropertyValuesHolder createLayoutPropertyValueHolder(String property, Object value){
          PropertyValuesHolder holder = null;
          switch (property){
              case Constants.Name.WIDTH:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.WIDTH, mWXComponent.getLayoutWidth(),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloat(value, 0.0f), mWXComponent.getViewPortWidth()));
              }
              break;
              case Constants.Name.HEIGHT:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.HEIGHT, mWXComponent.getLayoutHeight(),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloat(value, 0.0f), mWXComponent.getViewPortWidth()));
              }
              break;
              case Constants.Name.MARGIN_TOP:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.MARGIN_TOP,  mWXComponent.getMargin().get(
                      CSSShorthand.EDGE.TOP),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
              }
              break;
              case Constants.Name.MARGIN_LEFT:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.MARGIN_LEFT,  mWXComponent.getMargin().get(CSSShorthand.EDGE.LEFT),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
              }
              break;
              case Constants.Name.MARGIN_RIGHT:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.MARGIN_RIGHT,  mWXComponent.getMargin().get(CSSShorthand.EDGE.RIGHT),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
              }
              break;
              case Constants.Name.MARGIN_BOTTOM:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.MARGIN_BOTTOM,  mWXComponent.getMargin().get(CSSShorthand.EDGE.BOTTOM),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
              }
              break;
              case Constants.Name.LEFT:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.LEFT,  mWXComponent.getLayoutPosition().getLeft(),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
              }
              break;
              case Constants.Name.RIGHT:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.RIGHT,  mWXComponent.getLayoutPosition().getRight(),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
              }
              break;
              case Constants.Name.BOTTOM:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.BOTTOM,  mWXComponent.getLayoutPosition().getBottom(),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
              }
              break;
              case Constants.Name.TOP:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.TOP,  mWXComponent.getLayoutPosition().getTop(),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
              }
              break;
              case Constants.Name.PADDING_TOP:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.PADDING_TOP,  mWXComponent.getPadding().get(CSSShorthand.EDGE.TOP),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
              }
              break;
              case Constants.Name.PADDING_BOTTOM:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.PADDING_BOTTOM,  mWXComponent.getPadding().get(CSSShorthand.EDGE.BOTTOM),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
              }
              break;
              case Constants.Name.PADDING_LEFT:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.PADDING_LEFT,  mWXComponent.getPadding().get(CSSShorthand.EDGE.LEFT),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
              }
              break;
              case Constants.Name.PADDING_RIGHT:{
                  holder = PropertyValuesHolder.ofFloat(Constants.Name.PADDING_RIGHT,  mWXComponent.getPadding().get(CSSShorthand.EDGE.RIGHT),
                          WXViewUtils.getRealPxByWidth(WXUtils.getFloatByViewport(value, mWXComponent.getViewPortWidth()), mWXComponent.getViewPortWidth()));
              }
              break;
              default:
                  break;
          }
          if(holder == null){
              holder  = PropertyValuesHolder.ofFloat(property, 1, 1);
          }
          return  holder;
      }
  
      private void doLayoutPropertyValuesHolderAnimation(PropertyValuesHolder[] holders){
          layoutValueAnimator = ValueAnimator.ofPropertyValuesHolder(holders);
          layoutValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
              @Override
              public void onAnimationUpdate(final ValueAnimator animation) {
                  PropertyValuesHolder holders[] = animation.getValues();
                  for(PropertyValuesHolder holder : holders){
  
                      final String property =  holder.getPropertyName();
                      asynchronouslyUpdateLayout(mWXComponent, property, (Float) animation.getAnimatedValue(property));
                  }
  //                WXBridgeManager.getInstance().calculateLayoutPostToJSThread(mWXComponent.getInstanceId(), mWXComponent.getRef(), false);
              }
          });
          layoutValueAnimator.addListener(new AnimatorListenerAdapter() {
  
              boolean  hasCancel = false;
              @Override
              public void onAnimationCancel(Animator animation) {
                  super.onAnimationCancel(animation);
                  hasCancel = true;
              }
  
              @Override
              public void onAnimationEnd(Animator animation) {
                  if(hasCancel){
                      return;
                  }
                  super.onAnimationEnd(animation);
                  WXTransition.this.onTransitionAnimationEnd();
              }
          });
          if(interpolator != null) {
              layoutValueAnimator.setInterpolator(interpolator);
          }
          layoutValueAnimator.setStartDelay((long) (delay));
          layoutValueAnimator.setDuration((long) (duration));
          layoutValueAnimator.start();
      }
  
      @SuppressWarnings("unused")
      public static void asynchronouslyUpdateLayout(WXComponent component, final String propertyName, final float propertyValue) {
          if(component == null) {
              return;
          }
          final String ref = component.getRef();
          final String instanceId = component.getInstanceId();
          if(TextUtils.isEmpty(ref) || TextUtils.isEmpty(instanceId)) {
              return;
          }
  
          WXSDKManager.getInstance().getWXBridgeManager().post(new Runnable() {
              @Override
              public void run() {
                  switch (propertyName){
                      case Constants.Name.WIDTH:{
                          WXBridgeManager.getInstance().setStyleWidth(instanceId, ref, propertyValue);
                      }
                      break;
                      case Constants.Name.HEIGHT:{
                          WXBridgeManager.getInstance().setStyleHeight(instanceId, ref, propertyValue);
                      }
                      break;
                      case Constants.Name.MARGIN_TOP:{
                          WXBridgeManager.getInstance().setMargin(instanceId, ref, CSSShorthand.EDGE.TOP, propertyValue);
                      }
                      break;
                      case Constants.Name.MARGIN_LEFT:{
                          WXBridgeManager.getInstance().setMargin(instanceId, ref, CSSShorthand.EDGE.LEFT, propertyValue);
                      }
                      break;
                      case Constants.Name.MARGIN_RIGHT:{
                          WXBridgeManager.getInstance().setMargin(instanceId, ref, CSSShorthand.EDGE.RIGHT, propertyValue);
                      }
                      break;
                      case Constants.Name.MARGIN_BOTTOM:{
                          WXBridgeManager.getInstance().setMargin(instanceId, ref, CSSShorthand.EDGE.BOTTOM, propertyValue);
                      }
                      break;
                      case Constants.Name.LEFT:{
                          WXBridgeManager.getInstance().setPosition(instanceId, ref, CSSShorthand.EDGE.LEFT, propertyValue);
                      }
                      break;
                      case Constants.Name.RIGHT:{
                          WXBridgeManager.getInstance().setPosition(instanceId, ref, CSSShorthand.EDGE.RIGHT, (propertyValue));
                      }
                      break;
                      case Constants.Name.BOTTOM:{
                          WXBridgeManager.getInstance().setPosition(instanceId, ref, CSSShorthand.EDGE.BOTTOM, propertyValue);
                      }
                      break;
                      case Constants.Name.TOP:{
                          WXBridgeManager.getInstance().setPosition(instanceId, ref, CSSShorthand.EDGE.TOP, propertyValue);
                      }
                      break;
                      case Constants.Name.PADDING_TOP:{
                          WXBridgeManager.getInstance().setPadding(instanceId, ref, CSSShorthand.EDGE.TOP, propertyValue);
                      }
                      break;
                      case Constants.Name.PADDING_BOTTOM:{
                          WXBridgeManager.getInstance().setPadding(instanceId, ref, CSSShorthand.EDGE.BOTTOM, propertyValue);
                      }
                      break;
                      case Constants.Name.PADDING_LEFT:{
                          WXBridgeManager.getInstance().setPadding(instanceId, ref, CSSShorthand.EDGE.LEFT, propertyValue);
                      }
                      break;
                      case Constants.Name.PADDING_RIGHT:{
                          WXBridgeManager.getInstance().setPadding(instanceId, ref, CSSShorthand.EDGE.RIGHT, propertyValue);
                      }
                      break;
                      default:
                          break;
                  }
              }
          });
      }
  
      private synchronized void onTransitionAnimationEnd(){
          if(duration > 0){
              if(transitionEndEvent != null){
                  View view = getTargetView();
                  if(view != null &&  transitionEndEvent != null){
                      view.post(transitionEndEvent);
                  }
                  transitionEndEvent = null;
              }
          }
          synchronized (targetStyles){
              if(targetStyles.size() > 0){
                  for(String property : properties) {
                      if(targetStyles.containsKey(property)){
                          Object targetValue = targetStyles.remove(property);
                          mWXComponent.getStyles().put(property, targetValue);
                      }
                  }
                  targetStyles.clear();
              }
          }
      }
  
      private View getTargetView(){
          return null != mWXComponent ? mWXComponent.getHostView() : null;
      }
  
  
      /**
       * get time millis
       * */
      private static long parseTimeMillis(Map<String, Object> style, String key, long defaultValue){
          String  duration = WXUtils.getString(style.get(key), null);
          if(duration != null){
              duration = duration.replaceAll("ms", "");
          }
          if(duration != null){
              if(WXEnvironment.isApkDebugable()){
                  if(duration.contains("px")){
                      WXLogUtils.w("Transition Duration Unit Only Support ms, " + duration + " is not ms Unit");
                  }
              }
              duration = duration.replaceAll("px", "");
          }
          if(TextUtils.isEmpty(duration)){
              return  defaultValue;
          }
          try{
              return (long)Float.parseFloat(duration);
          }catch (NumberFormatException e){
              return  defaultValue;
          }
      }
  
      /**
       * create interpolcator same with web
       * http://www.w3school.com.cn/cssref/pr_transition-timing-function.asp
       * */
      private static Interpolator createTimeInterpolator(String interpolator) {
          if (!TextUtils.isEmpty(interpolator)) {
              switch (interpolator) {
                  case TimeFunction.EASE_IN:
                      return PathInterpolatorCompat.create(0.42f,0f, 1f,1f);
                  case TimeFunction.EASE_OUT:
                      return PathInterpolatorCompat.create(0f,0f, 0.58f,1f);
                  case TimeFunction.EASE_IN_OUT:
                      return PathInterpolatorCompat.create(0.42f,0f, 0.58f,1f);
                  case TimeFunction.EASE:
                      return PathInterpolatorCompat.create(0.25f,0.1f, 0.25f,1f);
                  case TimeFunction.LINEAR:
                      return PathInterpolatorCompat.create(0.0f,0f, 1f,1f);
                  default:
                      try {
                          //Parse cubic-bezier
                          SingleFunctionParser<Float> parser = new SingleFunctionParser<>(
                                  interpolator,
                                  new SingleFunctionParser.FlatMapper<Float>() {
                                      @Override
                                      public Float map(String raw) {
                                          return Float.parseFloat(raw);
                                      }
                                  });
                          List<Float> params = parser.parse(TimeFunction.CUBIC_BEZIER);
                          if (params != null && params.size() == 4) {
                              return PathInterpolatorCompat.create(
                                      params.get(0), params.get(1), params.get(2), params.get(3));
                          }
                      } catch (RuntimeException e) {
                          if(WXEnvironment.isApkDebugable()) {
                              WXLogUtils.e("WXTransition", e);
                          }
                      }
              }
          }
          return PathInterpolatorCompat.create(0.25f,0.1f, 0.25f,1f);
      }
  
      private static  void  updateTransitionProperties(WXTransition transition, String transtionProperty){
          if(transtionProperty == null){
              return;
          }
          transition.properties.clear();
          String[] propertiesArray = PROPERTY_SPLIT_PATTERN.split(transtionProperty);
          for(String property : propertiesArray){
              String trim = property.trim();
              if(TextUtils.isEmpty(trim)){
                  continue;
              }
              if(!(LAYOUT_PROPERTIES.contains(trim) || TRANSFORM_PROPERTIES.contains(trim))){
                  if(WXEnvironment.isApkDebugable()){
                      WXLogUtils.e("WXTransition Property Not Supported" + trim + " in " + transtionProperty);
                  }
                  continue;
              }
              transition.properties.add(trim);
          }
      }
  
      public List<String> getProperties() {
          return properties;
      }
  }
  