/**
 * Apache License Version 2.0, January 2004 http://www.apache.org/licenses/
 * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 * 1. Definitions.
 * "License" shall mean the terms and conditions for use, reproduction, and distribution as defined
 * by Sections 1 through 9 of this document.
 * "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is
 * granting the License.
 * "Legal Entity" shall mean the union of the acting entity and all other entities that control, are
 * controlled by, or are under common control with that entity. For the purposes of this definition,
 * "control" means (i) the power, direct or indirect, to cause the direction or management of such
 * entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
 * outstanding shares, or (iii) beneficial ownership of such entity.
 * "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this
 * License.
 * "Source" form shall mean the preferred form for making modifications, including but not limited
 * to software source code, documentation source, and configuration files.
 * "Object" form shall mean any form resulting from mechanical transformation or translation of a
 * Source form, including but not limited to compiled object code, generated documentation, and
 * conversions to other media types.
 * "Work" shall mean the work of authorship, whether in Source or Object form, made available under
 * the License, as indicated by a copyright notice that is included in or attached to the work (an
 * example is provided in the Appendix below).
 * "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or
 * derived from) the Work and for which the editorial revisions, annotations, elaborations, or other
 * modifications represent, as a whole, an original work of authorship. For the purposes of this
 * License, Derivative Works shall not include works that remain separable from, or merely link (or
 * bind by name) to the interfaces of, the Work and Derivative Works thereof.
 * "Contribution" shall mean any work of authorship, including the original version of the Work and
 * any modifications or additions to that Work or Derivative Works thereof, that is intentionally
 * submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or
 * Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this
 * definition, "submitted" means any form of electronic, verbal, or written communication sent to
 * the Licensor or its representatives, including but not limited to communication on electronic
 * mailing lists, source code control systems, and issue tracking systems that are managed by, or on
 * behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding
 * communication that is conspicuously marked or otherwise designated in writing by the copyright
 * owner as "Not a Contribution."
 * "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a
 * Contribution has been received by Licensor and subsequently incorporated within the Work.
 * 2. Grant of Copyright License. Subject to the terms and conditions of this License, each
 * Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
 * irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display,
 * publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or
 * Object form.
 * 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor
 * hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 * (except as stated in this section) patent license to make, have made, use, offer to sell, sell,
 * import, and otherwise transfer the Work, where such license applies only to those patent claims
 * licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or
 * by combination of their Contribution(s) with the Work to which such Contribution(s) was
 * submitted. If You institute patent litigation against any entity (including a cross-claim or
 * counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work
 * constitutes direct or contributory patent infringement, then any patent licenses granted to You
 * under this License for that Work shall terminate as of the date such litigation is filed.
 * 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works
 * thereof in any medium, with or without modifications, and in Source or Object form, provided that
 * You meet the following conditions:
 * (a) You must give any other recipients of the Work or Derivative Works a copy of this License;
 * and
 * (b) You must cause any modified files to carry prominent notices stating that You changed the
 * files; and
 * (c) You must retain, in the Source form of any Derivative Works that You distribute, all
 * copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding
 * those notices that do not pertain to any part of the Derivative Works; and
 * (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative
 * Works that You distribute must include a readable copy of the attribution notices contained
 * within such NOTICE file, excluding those notices that do not pertain to any part of the
 * Derivative Works, in at least one of the following places: within a NOTICE text file distributed
 * as part of the Derivative Works; within the Source form or documentation, if provided along with
 * the Derivative Works; or, within a display generated by the Derivative Works, if and wherever
 * such third-party notices normally appear. The contents of the NOTICE file are for informational
 * purposes only and do not modify the License. You may add Your own attribution notices within
 * Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the
 * Work, provided that such additional attribution notices cannot be construed as modifying the
 * License.
 * You may add Your own copyright statement to Your modifications and may provide additional or
 * different license terms and conditions for use, reproduction, or distribution of Your
 * modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and
 * distribution of the Work otherwise complies with the conditions stated in this License.
 * 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution
 * intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms
 * and conditions of this License, without any additional terms or conditions. Notwithstanding the
 * above, nothing herein shall supersede or modify the terms of any separate license agreement you
 * may have executed with Licensor regarding such Contributions.
 * 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service
 * marks, or product names of the Licensor, except as required for reasonable and customary use in
 * describing the origin of the Work and reproducing the content of the NOTICE file.
 * 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor
 * provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation,
 * any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 * PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or
 * redistributing the Work and assume any risks associated with Your exercise of permissions under
 * this License.
 * 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including
 * negligence), contract, or otherwise, unless required by applicable law (such as deliberate and
 * grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for
 * damages, including any direct, indirect, special, incidental, or consequential damages of any
 * character arising as a result of this License or out of the use or inability to use the Work
 * (including but not limited to damages for loss of goodwill, work stoppage, computer failure or
 * malfunction, or any and all other commercial damages or losses), even if such Contributor has
 * been advised of the possibility of such damages.
 * 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works
 * thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty,
 * indemnity, or other liability obligations and/or rights consistent with this License. However, in
 * accepting such obligations, You may act only on Your own behalf and on Your sole responsibility,
 * not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each
 * Contributor harmless for any liability incurred by, or claims asserted against, such Contributor
 * by reason of your accepting any such warranty or additional liability.
 * END OF TERMS AND CONDITIONS
 * APPENDIX: How to apply the Apache License to your work.
 * To apply the Apache License to your work, attach the following boilerplate notice, with the
 * fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include
 * the brackets!)  The text should be enclosed in the appropriate comment syntax for the file
 * format. We also recommend that a file or class name and description of purpose be included on the
 * same "printed page" as the copyright notice for easier identification within third-party
 * archives.
 * Copyright 2016 Alibaba Group
 * 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 com.taobao.weex.ui.component;

import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Message;
import android.support.annotation.CallSuper;
import android.support.annotation.CheckResult;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.text.TextUtils;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.taobao.weex.IWXActivityStateListener;
import com.taobao.weex.WXEnvironment;
import com.taobao.weex.WXSDKInstance;
import com.taobao.weex.WXSDKManager;
import com.taobao.weex.bridge.Invoker;
import com.taobao.weex.common.Constants;
import com.taobao.weex.common.IWXObject;
import com.taobao.weex.common.WXRuntimeException;
import com.taobao.weex.dom.ImmutableDomObject;
import com.taobao.weex.dom.WXDomHandler;
import com.taobao.weex.dom.WXDomObject;
import com.taobao.weex.dom.WXDomTask;
import com.taobao.weex.dom.WXStyle;
import com.taobao.weex.dom.flex.Spacing;
import com.taobao.weex.ui.IFComponentHolder;
import com.taobao.weex.ui.animation.WXAnimationModule;
import com.taobao.weex.ui.component.pesudo.OnActivePseudoListner;
import com.taobao.weex.ui.component.pesudo.PesudoStatus;
import com.taobao.weex.ui.component.pesudo.TouchActivePseudoListener;
import com.taobao.weex.ui.view.border.BorderDrawable;
import com.taobao.weex.ui.view.gesture.WXGesture;
import com.taobao.weex.ui.view.gesture.WXGestureObservable;
import com.taobao.weex.ui.view.gesture.WXGestureType;
import com.taobao.weex.utils.WXDataStructureUtil;
import com.taobao.weex.utils.WXLogUtils;
import com.taobao.weex.utils.WXReflectionUtils;
import com.taobao.weex.utils.WXResourceUtils;
import com.taobao.weex.utils.WXUtils;
import com.taobao.weex.utils.WXViewUtils;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * abstract component
 *
 */
public abstract class  WXComponent<T extends View> implements IWXObject, IWXActivityStateListener,OnActivePseudoListner {

  public static final String PROP_FIXED_SIZE = "fixedSize";
  public static final String PROP_FS_MATCH_PARENT = "m";
  public static final String PROP_FS_WRAP_CONTENT = "w";

  private int mFixedProp = 0;
  public static int mComponentNum = 0;
  /** package **/ T mHost;

  private volatile WXVContainer mParent;
  private volatile ImmutableDomObject mDomObj;
  private WXSDKInstance mInstance;
  private Context mContext;

  private int mAbsoluteY = 0;
  private int mAbsoluteX = 0;
  private Set<String> mGestureType;

  private BorderDrawable mBackgroundDrawable;
  private int mPreRealWidth = 0;
  private int mPreRealHeight = 0;
  private int mPreRealLeft = 0;
  private int mPreRealTop = 0;
  private WXGesture wxGesture;
  private IFComponentHolder mHolder;
  private boolean isUsing = false;
  private List<OnClickListener> mHostClickListeners;
  private List<OnFocusChangeListener> mFocusChangeListeners;
  private String mCurrentRef;
  private Set<String> mAppendEvents = new HashSet<>();
  private WXAnimationModule.AnimationHolder mAnimationHolder;
  private PesudoStatus mPesudoStatus = new PesudoStatus();

  //Holding the animation bean when component is uninitialized
  public void postAnimation(WXAnimationModule.AnimationHolder holder) {
    this.mAnimationHolder = holder;
  }

  private OnClickListener mClickEventListener = new OnClickListener() {
    @Override
    public void onHostViewClick() {
      Map<String, Object> param= WXDataStructureUtil.newHashMapWithExpectedSize(1);
      Map<String, Object> position = WXDataStructureUtil.newHashMapWithExpectedSize(4);
      int[] location = new int[2];
      mHost.getLocationOnScreen(location);
      position.put("x", WXViewUtils.getWebPxByWidth(location[0],mInstance.getViewPortWidth()));
      position.put("y", WXViewUtils.getWebPxByWidth(location[1],mInstance.getViewPortWidth()));
      position.put("width", WXViewUtils.getWebPxByWidth(mDomObj.getLayoutWidth(),mInstance.getViewPortWidth()));
      position.put("height", WXViewUtils.getWebPxByWidth(mDomObj.getLayoutHeight(),mInstance.getViewPortWidth()));
      param.put(Constants.Name.POSITION, position);
      fireEvent(Constants.Event.CLICK,param);
    }
  };

  public String getInstanceId() {
    return mInstance.getInstanceId();
  }

  public Rect getComponentSize() {
    Rect size = new Rect();
    if (mHost != null) {
      int[] location = new int[2];
      int[] anchor = new int[2];
      mHost.getLocationOnScreen(location);
      mInstance.getContainerView().getLocationOnScreen(anchor);
      int left = location[0] - anchor[0];
      int top = location[1] - anchor[1];
      int width = (int) mDomObj.getLayoutWidth();
      int height = (int) mDomObj.getLayoutHeight();
      size.set(left, top, left + width, top + height);
    }
    return size;
  }

  public final void invoke(String method, JSONArray args) {
    final Invoker invoker = mHolder.getMethodInvoker(method);
    if (invoker != null) {
      try {
        getInstance()
            .getNativeInvokeHelper()
            .invoke(this,invoker,args);

      } catch (Exception e) {
        WXLogUtils.e("[WXComponent] updateProperties :" + "class:" + getClass() + "method:" + invoker.toString() + " function " + WXLogUtils.getStackTrace(e));
      }
    }else{
      onInvokeUnknownMethod(method,args);
    }
  }

  /**
   * Will be invoked when request method not found.
   * Subclass should override this method, If you return hard-code method list in {@link IFComponentHolder#getMethods()}
   * @param method
   * @param args
   */
  protected void onInvokeUnknownMethod(String method, JSONArray args){

  }

  public interface OnClickListener{
    void onHostViewClick();
  }

  interface OnFocusChangeListener{
    void onFocusChange(boolean hasFocus);
  }

  @Deprecated
  public WXComponent(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, String instanceId, boolean isLazy) {
    this(instance,dom,parent,isLazy);
  }

  @Deprecated
  public WXComponent(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, boolean isLazy) {
    this(instance,dom,parent);
  }

  public WXComponent(WXSDKInstance instance, WXDomObject dom, WXVContainer parent) {
    mInstance = instance;
    mContext = mInstance.getContext();
    mParent = parent;
    mDomObj = dom.clone();
    mCurrentRef = mDomObj.getRef();
    mGestureType = new HashSet<>();
    ++mComponentNum;
    onCreate();
  }

  protected void onCreate(){

  }

  public void bindHolder(IFComponentHolder holder){
    mHolder = holder;
  }


  public WXSDKInstance getInstance(){
    return mInstance;
  }

  public Context getContext(){
    return mContext;
  }

  /**
   * Find component by component reference.
   * @param ref
   * @return
   */
  protected final WXComponent findComponent(String ref){
    if(mInstance != null && ref != null){
      return WXSDKManager.getInstance()
          .getWXRenderManager()
          .getWXComponent(mInstance.getInstanceId(), ref);
    }
    return null;
  }

  public final void fireEvent(String type){
    fireEvent(type,null);
  }

  public final void fireEvent(String type, Map<String, Object> params){
    fireEvent(type,params,null);
  }

  protected final void fireEvent(String type, Map<String, Object> params,Map<String, Object> domChanges){
    if(mInstance != null && mDomObj != null) {
      mInstance.fireEvent(mCurrentRef, type, params,domChanges);
    }
  }

  /**
   * The view is created as needed
   * @return true for lazy
   */
  public boolean isLazy() {
    return mParent != null && mParent.isLazy();
  }

  public void applyLayoutAndEvent(WXComponent component) {
    if(!isLazy()) {
      if (component == null) {
        component = this;
      }
      setLayout(component.getDomObject());
      setPadding(component.getDomObject().getPadding(), component.getDomObject().getBorder());
      addEvents();

    }
  }

  protected final void addFocusChangeListener(OnFocusChangeListener l){
    View view;
    if(l != null && (view = getRealView()) != null) {
      if( mFocusChangeListeners == null){
        mFocusChangeListeners = new ArrayList<>();
        view.setFocusable(true);
        view.setOnFocusChangeListener(new View.OnFocusChangeListener() {
          @Override
          public void onFocusChange(View v, boolean hasFocus) {
            for (OnFocusChangeListener listener : mFocusChangeListeners){
              if(listener != null){
                listener.onFocusChange(hasFocus);
              }
            }
          }
        });
      }
      mFocusChangeListeners.add(l);
    }
  }

  protected final void addClickListener(OnClickListener l){
    View view;
    if(l != null && (view = getRealView()) != null) {
      if(mHostClickListeners == null){
        mHostClickListeners = new ArrayList<>();
        view.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
            for (OnClickListener listener : mHostClickListeners){
              if(listener != null) {
                listener.onHostViewClick();
              }
            }
          }
        });
      }
      mHostClickListeners.add(l);

    }
  }

  protected final void removeClickListener(OnClickListener l){
    mHostClickListeners.remove(l);
  }

  public void bindData(WXComponent component){
    if(!isLazy()) {
      if (component == null) {
        component = this;
      }
      mCurrentRef = component.getDomObject().getRef();
      updateProperties(component.getDomObject().getStyles());
      updateProperties(component.getDomObject().getAttrs());
      updateExtra(component.getDomObject().getExtra());
    }
  }

  public void refreshData(WXComponent component){

  }

  protected BorderDrawable getOrCreateBorder() {
    if (mBackgroundDrawable == null) {
      Drawable backgroundDrawable = mHost.getBackground();
      WXViewUtils.setBackGround(mHost,null);
      mBackgroundDrawable = new BorderDrawable();
      if (backgroundDrawable == null) {
        WXViewUtils.setBackGround(mHost,mBackgroundDrawable);
      } else {
        //TODO Not strictly clip according to background-clip:border-box
        WXViewUtils.setBackGround(mHost,new LayerDrawable(new Drawable[]{
            mBackgroundDrawable,backgroundDrawable}));
      }
    }
    return mBackgroundDrawable;
  }

  /**
   * layout view
   */
  public final void setLayout(ImmutableDomObject domObject) {
    if ( domObject == null || TextUtils.isEmpty(mCurrentRef)) {
      return;
    }

    boolean nullParent = mParent == null;//parent is nullable
    mDomObj = domObject;

    //offset by sibling
    int siblingOffset = nullParent?0:mParent.getChildrenLayoutTopOffset();

    Spacing parentPadding = (nullParent?new Spacing():mParent.getDomObject().getPadding());
    Spacing parentBorder = (nullParent?new Spacing():mParent.getDomObject().getBorder());
    Spacing margin = mDomObj.getMargin();
    int realWidth = (int) mDomObj.getLayoutWidth();
    int realHeight = (int) mDomObj.getLayoutHeight();
    int realLeft = (int) (mDomObj.getLayoutX() - parentPadding.get(Spacing.LEFT) -
                          parentBorder.get(Spacing.LEFT));
    int realTop = (int) (mDomObj.getLayoutY() - parentPadding.get(Spacing.TOP) -
                         parentBorder.get(Spacing.TOP)) + siblingOffset;
    int realRight = (int) margin.get(Spacing.RIGHT);
    int realBottom = (int) margin.get(Spacing.BOTTOM);

    if (mPreRealWidth == realWidth && mPreRealHeight == realHeight && mPreRealLeft == realLeft && mPreRealTop == realTop) {
      return;
    }

    mAbsoluteY = (int) (nullParent?0:mParent.getAbsoluteY() + mDomObj.getLayoutY());
    mAbsoluteX = (int) (nullParent?0:mParent.getAbsoluteX() + mDomObj.getLayoutX());

    //calculate first screen time
    if (!mInstance.mEnd &&!(mHost instanceof ViewGroup) && mAbsoluteY+realHeight > mInstance.getWeexHeight()+1) {
      mInstance.firstScreenRenderFinished();
    }

    if (mHost == null) {
      return;
    }

    MeasureOutput measureOutput = measure(realWidth, realHeight);
    realWidth = measureOutput.width;
    realHeight = measureOutput.height;

    //fixed style
    if (mDomObj.isFixed()) {
      setFixedHostLayoutParams(mHost,realWidth,realHeight,realLeft,realRight,realTop,realBottom);
    }else {
      setHostLayoutParams(mHost, realWidth, realHeight, realLeft, realRight, realTop, realBottom);
    }

    mPreRealWidth = realWidth;
    mPreRealHeight = realHeight;
    mPreRealLeft = realLeft;
    mPreRealTop = realTop;

    onFinishLayout();
  }


  public int getLayoutTopOffsetForSibling(){
    return 0;
  }

  protected void setHostLayoutParams(T host, int width, int height, int left, int right, int top, int bottom){
    ViewGroup.LayoutParams lp;
    if(mParent == null){
      FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width,height);
      params.setMargins(left, top, right, bottom);
      lp = params;
    }else{
      lp = mParent.getChildLayoutParams(this,host,width, height, left,right,top,bottom);
    }
    if(lp != null) {
      mHost.setLayoutParams(lp);
    }
  }

  private void setFixedHostLayoutParams(T host, int width, int height, int left, int right, int top, int bottom){
    if (host.getParent() instanceof ViewGroup) {
      ViewGroup viewGroup = (ViewGroup) host.getParent();
      viewGroup.removeView(host);
    }
    FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);

    params.width = width;
    params.height = height;
    params.setMargins(left, top, right, bottom);
    host.setLayoutParams(params);
    mInstance.addFixedView(host);

    if (WXEnvironment.isApkDebugable()) {
      WXLogUtils.d("Weex_Fixed_Style", "WXComponent:setLayout :" + left + " " + top + " " + width + " " + height);
      WXLogUtils.d("Weex_Fixed_Style", "WXComponent:setLayout Left:" + mDomObj.getStyles().getLeft() + " " + (int) mDomObj.getStyles().getTop());
    }
  }

  /**
   * After component's layout result is apply to view. May be invoke multiple times since
   * DOM can be changed in js runtime.
   */
  protected void onFinishLayout(){

  }

  public void setPadding(Spacing padding, Spacing border) {
    int left = (int) (padding.get(Spacing.LEFT) + border.get(Spacing.LEFT));
    int top = (int) (padding.get(Spacing.TOP) + border.get(Spacing.TOP));
    int right = (int) (padding.get(Spacing.RIGHT) + border.get(Spacing.RIGHT));
    int bottom = (int) (padding.get(Spacing.BOTTOM) + border.get(Spacing.BOTTOM));

    if (mHost == null) {
      return;
    }
    mHost.setPadding(left, top, right, bottom);
  }

  private void addEvents() {
    int count = mDomObj.getEvents().size();
    for (int i = 0; i < count; ++i) {
      addEvent(mDomObj.getEvents().get(i));
    }
    setActiveTouchListener();
  }

  public void updateExtra(Object extra) {

  }

  public ImmutableDomObject getDomObject() {
    return mDomObj;
  }

  /**
   * measure
   */
  protected MeasureOutput measure(int width, int height) {
    MeasureOutput measureOutput = new MeasureOutput();

    if(mFixedProp != 0){
      measureOutput.width = mFixedProp;
      measureOutput.height = mFixedProp;
    }else {
      measureOutput.width = width;
      measureOutput.height = height;
    }
    return measureOutput;
  }


  public void updateProperties(Map<String, Object> props) {
    if (props == null || mHost == null) {
      return;
    }

    for (Map.Entry<String, Object> entry : props.entrySet()) {
      String key = entry.getKey();
      Object param = entry.getValue();
      String value = WXUtils.getString(param, null);
      if (TextUtils.isEmpty(value)) {
        param = convertEmptyProperty(key, value);
      }
      if (!setProperty(key, param)) {
        if (mHolder == null) {
          return;
        }
        Invoker invoker = mHolder.getPropertyInvoker(key);
        if (invoker != null) {
          try {
            Type[] paramClazzs = invoker.getParameterTypes();
            if (paramClazzs.length != 1) {
              WXLogUtils.e("[WXComponent] setX method only one parameter´╝Ü" + invoker);
              return;
            }
            param = WXReflectionUtils.parseArgument(paramClazzs[0], param);
            invoker.invoke(this, param);
          } catch (Exception e) {
            WXLogUtils.e("[WXComponent] updateProperties :" + "class:" + getClass() + "method:" + invoker.toString() + " function " + WXLogUtils.getStackTrace(e));
          }
        }
      }
    }
    readyToRender();
  }

  /**
   * Apply styles and attributes.
   * @param key name of argument
   * @param param value of argument
   * @return true means that the property is consumed
     */
  protected boolean setProperty(String key, Object param) {
    switch (key) {
      case Constants.Name.DISABLED:
        Boolean disabled = WXUtils.getBoolean(param,null);
        if (disabled != null) {
          setDisabled(disabled);
          setPseudoClassStatus(Constants.PESUDO.DISABLED, disabled);
        }
        return true;
      case Constants.Name.POSITION:
        String position = WXUtils.getString(param,null);
        if (position != null)
          setSticky(position);
        return true;
      case Constants.Name.BACKGROUND_COLOR:
        String bgColor = WXUtils.getString(param,null);
        if (bgColor != null)
          setBackgroundColor(bgColor);
        return true;
      case Constants.Name.BACKGROUND_IMAGE:
        String bgImage = WXUtils.getString(param,null);
        if(bgImage!=null){
          setBackgroundImage(bgImage);
        }
        return true;
      case Constants.Name.OPACITY:
        Float opacity = WXUtils.getFloat(param,null);
        if (opacity != null)
          setOpacity(opacity);
        return true;
      case Constants.Name.BORDER_RADIUS:
      case Constants.Name.BORDER_TOP_LEFT_RADIUS:
      case Constants.Name.BORDER_TOP_RIGHT_RADIUS:
      case Constants.Name.BORDER_BOTTOM_RIGHT_RADIUS:
      case Constants.Name.BORDER_BOTTOM_LEFT_RADIUS:
        Float radius = WXUtils.getFloat(param,null);
        if (radius != null)
          setBorderRadius(key,radius);
        return true;
      case Constants.Name.BORDER_WIDTH:
      case Constants.Name.BORDER_TOP_WIDTH:
      case Constants.Name.BORDER_RIGHT_WIDTH:
      case Constants.Name.BORDER_BOTTOM_WIDTH:
      case Constants.Name.BORDER_LEFT_WIDTH:
        Float width = WXUtils.getFloat(param,null);
        if (width != null)
          setBorderWidth(key,width);
        return true;
      case Constants.Name.BORDER_STYLE:
      case Constants.Name.BORDER_RIGHT_STYLE:
      case Constants.Name.BORDER_BOTTOM_STYLE:
      case Constants.Name.BORDER_LEFT_STYLE:
      case Constants.Name.BORDER_TOP_STYLE:
        String border_style = WXUtils.getString(param,null);
        if (border_style != null)
          setBorderStyle(key, border_style);
        return true;
      case Constants.Name.BORDER_COLOR:
      case Constants.Name.BORDER_TOP_COLOR:
      case Constants.Name.BORDER_RIGHT_COLOR:
      case Constants.Name.BORDER_BOTTOM_COLOR:
      case Constants.Name.BORDER_LEFT_COLOR:
        String border_color = WXUtils.getString(param,null);
        if (border_color != null)
          setBorderColor(key, border_color);
        return true;
      case Constants.Name.VISIBILITY:
        String visibility = WXUtils.getString(param,null);
        if (visibility != null)
          setVisibility(visibility);
        return true;
      case Constants.Name.ELEVATION:
        if(param!=null) {
          updateElevation();
        }
        return true;
      case PROP_FIXED_SIZE:
        String fixedSize = WXUtils.getString(param, PROP_FS_MATCH_PARENT);
        setFixedSize(fixedSize);
        return true;
      case Constants.Name.WIDTH:
      case Constants.Name.MIN_WIDTH:
      case Constants.Name.MAX_WIDTH:
      case Constants.Name.HEIGHT:
      case Constants.Name.MIN_HEIGHT:
      case Constants.Name.MAX_HEIGHT:
      case Constants.Name.ALIGN_ITEMS:
      case Constants.Name.ALIGN_SELF:
      case Constants.Name.FLEX:
      case Constants.Name.FLEX_DIRECTION:
      case Constants.Name.JUSTIFY_CONTENT:
      case Constants.Name.FLEX_WRAP:
      case Constants.Name.MARGIN:
      case Constants.Name.MARGIN_TOP:
      case Constants.Name.MARGIN_LEFT:
      case Constants.Name.MARGIN_RIGHT:
      case Constants.Name.MARGIN_BOTTOM:
      case Constants.Name.PADDING:
      case Constants.Name.PADDING_TOP:
      case Constants.Name.PADDING_LEFT:
      case Constants.Name.PADDING_RIGHT:
      case Constants.Name.PADDING_BOTTOM:
      case Constants.Name.LEFT:
      case Constants.Name.TOP:
      case Constants.Name.RIGHT:
      case Constants.Name.BOTTOM:
        return true;
      default:
        return false;
    }
  }

  /**
   * Avoid large size view fail in GPU-Animation.
   * @param fixedSize
   */
  private void setFixedSize(String fixedSize) {
    if(PROP_FS_MATCH_PARENT.equals(fixedSize)){
      mFixedProp = ViewGroup.LayoutParams.MATCH_PARENT;
    }else if(PROP_FS_WRAP_CONTENT.equals(fixedSize)){
      mFixedProp = ViewGroup.LayoutParams.WRAP_CONTENT;
    }else{
      mFixedProp = 0;
      return;
    }
    if(mHost != null){
      ViewGroup.LayoutParams layoutParams = mHost.getLayoutParams();
      if(layoutParams != null){
        layoutParams.height = mFixedProp;
        layoutParams.width = mFixedProp;
        mHost.setLayoutParams(layoutParams);
      }
    }
  }

  /**
   * Add new event to component,this will post a task to DOM thread to add event.
   * @param type
   */
  protected void appendEventToDOM(String type){
    Message message = Message.obtain();
    WXDomTask task = new WXDomTask();
    task.instanceId = getInstanceId();
    task.args = new ArrayList<>();
    task.args.add(getRef());
    task.args.add(type);
    message.obj = task;
    message.what = WXDomHandler.MsgType.WX_DOM_ADD_EVENT;
    WXSDKManager.getInstance().getWXDomManager().sendMessage(message);
  }

  /**
   * Do not use this method to add event, this only apply event already add to DomObject.
   * @param type
   */
  public void addEvent(String type) {
    if (TextUtils.isEmpty(type)) {
      return;
    }
    mAppendEvents.add(type);

    View view = getRealView();
    if (type.equals(Constants.Event.CLICK) && view != null) {
      addClickListener(mClickEventListener);
    } else if ((type.equals( Constants.Event.FOCUS) || type.equals( Constants.Event.BLUR)) ) {
      addFocusChangeListener(new OnFocusChangeListener() {
        public void onFocusChange(boolean hasFocus) {
          Map<String, Object> params = new HashMap<>();
          params.put("timeStamp", System.currentTimeMillis());
          fireEvent(hasFocus ? Constants.Event.FOCUS : Constants.Event.BLUR, params);
        }
      });
    } else if (view != null &&
               needGestureDetector(type)) {
      if (view instanceof WXGestureObservable) {
        if (wxGesture == null) {
          wxGesture = new WXGesture(this, mContext);
        }
        mGestureType.add(type);
        ((WXGestureObservable) view).registerGestureListener(wxGesture);
      } else {
        WXLogUtils.e(view.getClass().getSimpleName() + " don't implement " +
                     "WXGestureObservable, so no gesture is supported.");
      }
    } else {
      Scrollable scroller = getParentScroller();
      if (type.equals(Constants.Event.APPEAR) && scroller != null) {
        scroller.bindAppearEvent(this);
      }
      if (type.equals(Constants.Event.DISAPPEAR) && scroller != null) {
        scroller.bindDisappearEvent(this);
      }
    }
  }

  public View getRealView() {
    return mHost;
  }

  /**
   * Judge whether need to set an onTouchListener.<br>
   * As there is only one onTouchListener in each view, so all the gesture that use onTouchListener should put there.
   *
   * @param type eventType {@link com.taobao.weex.common.Constants.Event}
   * @return true for set an onTouchListener, otherwise false
   */
  private boolean needGestureDetector(String type) {
    if (mHost != null) {
      for (WXGestureType gesture : WXGestureType.LowLevelGesture.values()) {
        if (type.equals(gesture.toString())) {
          return true;
        }
      }
      for (WXGestureType gesture : WXGestureType.HighLevelGesture.values()) {
        if (type.equals(gesture.toString())) {
          return true;
        }
      }
    }
    return false;
  }

  /**
   * get Scroller components
   */
  public Scrollable getParentScroller() {
    WXComponent component = this;
    WXVContainer container;
    Scrollable scroller;
    for (; ; ) {
      container = component.getParent();
      if (container == null) {
        return null;
      }
      if (container instanceof Scrollable) {
        scroller = (Scrollable) container;
        return scroller;
      }
      if (container.getRef().equals(WXDomObject.ROOT)) {
        return null;
      }
      component = container;
    }
  }

  public WXVContainer getParent() {
    return mParent;
  }

  public String getRef() {
    if (mDomObj == null) {
      return null;
    }
    return mCurrentRef;
  }

  /**
   * create view
   *
   */
  public final void createView() {
    if(!isLazy()) {
      createViewImpl();
    }
  }

  protected void createViewImpl() {
    if (mContext != null) {
      mHost = initComponentHostView(mContext);
      if (mHost == null) {
        //compatible
        initView();
      }
      if(mHost != null){
        mHost.setId(WXViewUtils.generateViewId());
      }
      onHostViewInitialized(mHost);
    }else{
      WXLogUtils.e("createViewImpl","Context is null");
    }
  }

  /**
   * Use {@link #initComponentHostView(Context context)} instead.
   */
  @Deprecated
  protected void initView() {
    if (mContext != null)
      mHost = initComponentHostView(mContext);
  }


  /**
   * Create corresponding view for this component.
   * @param context
   * @return
   */
  protected T initComponentHostView(@NonNull Context context){
    /**
     * compatible old initView
     * TODO: change to abstract method in next V1.0 .
     */
    return null;
  }

  /**
   * Called after host view init. <br>
   * Any overriding methods should invoke this method at the right time, to ensure the cached animation can be triggered correctly.
   * (the animation will be cached when {@link #isLazy()} is true)
   * @param host the host view
   */
  @CallSuper
  protected void onHostViewInitialized(T host){
    if (mAnimationHolder != null) {
      //Performs cached animation
      mAnimationHolder.execute(mInstance, this);
    }
    setActiveTouchListener();
  }

  public T getHostView() {
    return mHost;
  }

  /**
   * use {@link #getHostView()} instead
   * @return
   */
  @Deprecated
  public View getView(){
    return mHost;
  }

  public int getAbsoluteY() {
    return mAbsoluteY;
  }

  public int getAbsoluteX() {
    return mAbsoluteX;
  }

  public void updateDom(WXDomObject dom) {
    if (dom == null) {
      return;
    }
    mDomObj = dom.clone();
  }

  public final void removeEvent(String type) {
    if (TextUtils.isEmpty(type)) {
      return;
    }
    mAppendEvents.remove(type);//only clean append events, not dom's events.
    mGestureType.remove(type);
    removeEventFromView(type);
  }

  protected void removeEventFromView(String type) {
    if (type.equals(Constants.Event.CLICK) && getRealView() != null && mHostClickListeners != null) {
      mHostClickListeners.remove(mClickEventListener);
      //click event only remove from listener array
    }
    Scrollable scroller = getParentScroller();
    if (type.equals(Constants.Event.APPEAR) && scroller != null) {
      scroller.unbindAppearEvent(this);
    }
    if (type.equals(Constants.Event.DISAPPEAR) && scroller != null) {
      scroller.unbindDisappearEvent(this);
    }
  }

  public final void removeAllEvent() {
    if (mDomObj == null || mDomObj.getEvents().size() < 1) {
      return;
    }
    for (String event : mDomObj.getEvents()) {
      removeEventFromView(event);
    }
    mAppendEvents.clear();//only clean append events, not dom's events.
    mGestureType.clear();
    wxGesture = null;
    if (getRealView() != null &&
        getRealView() instanceof WXGestureObservable) {
      ((WXGestureObservable) getRealView()).registerGestureListener(null);
    }
    if(mHost != null) {
      mHost.setOnFocusChangeListener(null);
      mHost.setOnClickListener(null);
    }
  }

  public final void removeStickyStyle() {
    if (mDomObj == null ) {
      return;
    }

    if (isSticky()) {
      Scrollable scroller = getParentScroller();
      if (scroller != null) {
        scroller.unbindStickStyle(this);
      }
    }
  }

  public boolean isSticky() {
    return mDomObj.getStyles().isSticky();
  }

  public void setDisabled(boolean disabled) {
    if (mHost == null) {
      return;
    }
    mHost.setEnabled(!disabled);
  }

  public void setSticky(String sticky) {
    if (!TextUtils.isEmpty(sticky) && sticky.equals(Constants.Value.STICKY)) {
      Scrollable waScroller = getParentScroller();
      if (waScroller != null) {
        waScroller.bindStickStyle(this);
      }
    }
  }

  public void setBackgroundColor(String color) {
    if (!TextUtils.isEmpty(color)&& mHost!=null) {
      int colorInt = WXResourceUtils.getColor(color);
      if (!(colorInt == Color.TRANSPARENT && mBackgroundDrawable == null)){
          getOrCreateBorder().setColor(colorInt);
      }
    }
  }

  public void setBackgroundImage(String bgImage) {
    if (!TextUtils.isEmpty(bgImage) && mHost != null) {
      Shader shader = WXResourceUtils.getShader(bgImage, mDomObj.getLayoutWidth(), mDomObj.getLayoutHeight());
      getOrCreateBorder().setImage(shader);
    }
  }

  public void setOpacity(float opacity) {
    if (opacity >= 0 && opacity <= 1 && mHost.getAlpha() != opacity) {
      mHost.setLayerType(View.LAYER_TYPE_HARDWARE, null);
      mHost.setAlpha(opacity);
    }
  }

  public void setBorderRadius(String key, float borderRadius) {
    if (borderRadius >= 0) {
      switch (key) {
        case Constants.Name.BORDER_RADIUS:
          getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_RADIUS_ALL, WXViewUtils.getRealSubPxByWidth(borderRadius,mInstance.getViewPortWidth()));
          break;
        case Constants.Name.BORDER_TOP_LEFT_RADIUS:
          getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_TOP_LEFT_RADIUS, WXViewUtils.getRealSubPxByWidth(borderRadius,mInstance.getViewPortWidth()));
          break;
        case Constants.Name.BORDER_TOP_RIGHT_RADIUS:
          getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_TOP_RIGHT_RADIUS, WXViewUtils.getRealSubPxByWidth(borderRadius,mInstance.getViewPortWidth()));
          break;
        case Constants.Name.BORDER_BOTTOM_RIGHT_RADIUS:
          getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_BOTTOM_RIGHT_RADIUS, WXViewUtils.getRealSubPxByWidth(borderRadius,mInstance.getViewPortWidth()));
          break;
        case Constants.Name.BORDER_BOTTOM_LEFT_RADIUS:
          getOrCreateBorder().setBorderRadius(BorderDrawable.BORDER_BOTTOM_LEFT_RADIUS, WXViewUtils.getRealSubPxByWidth(borderRadius,mInstance.getViewPortWidth()));
          break;
      }
    }
  }

  public void setBorderWidth(String key, float borderWidth) {
    if (borderWidth >= 0) {
      switch (key) {
        case Constants.Name.BORDER_WIDTH:
          getOrCreateBorder().setBorderWidth(Spacing.ALL, WXViewUtils.getRealSubPxByWidth(borderWidth,getInstance().getViewPortWidth()));
          break;
        case Constants.Name.BORDER_TOP_WIDTH:
          getOrCreateBorder().setBorderWidth(Spacing.TOP, WXViewUtils.getRealSubPxByWidth(borderWidth,getInstance().getViewPortWidth()));
          break;
        case Constants.Name.BORDER_RIGHT_WIDTH:
          getOrCreateBorder().setBorderWidth(Spacing.RIGHT, WXViewUtils.getRealSubPxByWidth(borderWidth,getInstance().getViewPortWidth()));
          break;
        case Constants.Name.BORDER_BOTTOM_WIDTH:
          getOrCreateBorder().setBorderWidth(Spacing.BOTTOM, WXViewUtils.getRealSubPxByWidth(borderWidth,getInstance().getViewPortWidth()));
          break;
        case Constants.Name.BORDER_LEFT_WIDTH:
          getOrCreateBorder().setBorderWidth(Spacing.LEFT, WXViewUtils.getRealSubPxByWidth(borderWidth,getInstance().getViewPortWidth()));
          break;
      }
    }
  }

  public void setBorderStyle(String key, String borderStyle) {
    if(!TextUtils.isEmpty(borderStyle)){
      switch (key){
        case Constants.Name.BORDER_STYLE:
          getOrCreateBorder().setBorderStyle(Spacing.ALL,borderStyle);
          break;
        case Constants.Name.BORDER_RIGHT_STYLE:
          getOrCreateBorder().setBorderStyle(Spacing.RIGHT,borderStyle);
          break;
        case Constants.Name.BORDER_BOTTOM_STYLE:
          getOrCreateBorder().setBorderStyle(Spacing.BOTTOM,borderStyle);
          break;
        case Constants.Name.BORDER_LEFT_STYLE:
          getOrCreateBorder().setBorderStyle(Spacing.LEFT,borderStyle);
          break;
        case Constants.Name.BORDER_TOP_STYLE:
          getOrCreateBorder().setBorderStyle(Spacing.TOP,borderStyle);
          break;
      }
    }
  }

  public void setBorderColor(String key, String borderColor) {
    if (!TextUtils.isEmpty(borderColor)) {
      int colorInt = WXResourceUtils.getColor(borderColor);
      if (colorInt != Integer.MIN_VALUE) {
        switch (key) {
          case Constants.Name.BORDER_COLOR:
            getOrCreateBorder().setBorderColor(Spacing.ALL, colorInt);
            break;
          case Constants.Name.BORDER_TOP_COLOR:
            getOrCreateBorder().setBorderColor(Spacing.TOP, colorInt);
            break;
          case Constants.Name.BORDER_RIGHT_COLOR:
            getOrCreateBorder().setBorderColor(Spacing.RIGHT, colorInt);
            break;
          case Constants.Name.BORDER_BOTTOM_COLOR:
            getOrCreateBorder().setBorderColor(Spacing.BOTTOM, colorInt);
            break;
          case Constants.Name.BORDER_LEFT_COLOR:
            getOrCreateBorder().setBorderColor(Spacing.LEFT, colorInt);
            break;
        }
      }
    }
  }

  public
  @Nullable
  String getVisibility() {
    try {
      return (String) getDomObject().getStyles().get(Constants.Name.VISIBILITY);
    } catch (Exception e) {
      return Constants.Value.VISIBLE;
    }
  }

  public void setVisibility(String visibility) {
    View view;
    if ((view = getRealView()) != null) {
      if (TextUtils.equals(visibility, Constants.Value.VISIBLE)) {
        view.setVisibility(View.VISIBLE);
      } else if (TextUtils.equals(visibility, Constants.Value.HIDDEN)) {
        view.setVisibility(View.GONE);
      }
    }
  }

  /**
   * This is an experimental feature for elevation of material design.
   */
  private void updateElevation() {
    float elevation = getDomObject().getAttrs().getElevation(getInstance().getViewPortWidth());
    if (!Float.isNaN(elevation)) {
      ViewCompat.setElevation(getHostView(), elevation);
    }
  }

  @Deprecated
  public void registerActivityStateListener() {

  }


  /********************************
   *  begin hook Activity life cycle callback
   ********************************************************/
  public void onActivityCreate() {

  }

  public void onActivityStart() {

  }

  public void onActivityPause() {

  }

  public void onActivityResume() {

  }

  public void onActivityStop() {

  }

  public void onActivityDestroy() {

  }

  public boolean onActivityBack() {
    return false;
  }

  public void onActivityResult(int requestCode, int resultCode, Intent data){

  }

  public boolean onCreateOptionsMenu(Menu menu){return false;}

  public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

  }

  /********************************
   *  end hook Activity life cycle callback
   ********************************************************/


  public void destroy() {
    if (WXEnvironment.isApkDebugable() && !WXUtils.isUiThread()) {
      throw new WXRuntimeException("[WXComponent] destroy can only be called in main thread");
    }
    if(mHost!= null && mHost.getLayerType()==View.LAYER_TYPE_HARDWARE) {
      mHost.setLayerType(View.LAYER_TYPE_NONE, null);
    }
    removeAllEvent();
    removeStickyStyle();
    if (mDomObj != null) {
      mDomObj = null;
    }
  }

  /**
   * Detach view from its component. Components,
   * which have difference between getHostView and getRealView or have temp calculation results,
   * must<strong> override</strong>  this method with their own implementation.
   *
   * @return the original View
   */
  public View detachViewAndClearPreInfo() {
    View original = mHost;
    mPreRealLeft = 0;
    mPreRealWidth = 0;
    mPreRealHeight = 0;
    mPreRealTop = 0;
//    mHost = null;
    return original;
  }

  /**
   * This method computes user visible left-top point in view's coordinate.
   * The default implementation uses the scrollX and scrollY of the view as the result,
   * and put the value in the parameter pointer.
   * Components with different computation algorithm
   * <strong> should override </strong> this method.
   *
   * @param pointF the user visible left-top point in view's coordinate.
   */
  public void computeVisiblePointInViewCoordinate(PointF pointF) {
    View view = getRealView();
    pointF.set(view.getScrollX(), view.getScrollY());
  }

  public boolean containsGesture(WXGestureType WXGestureType) {
    return mGestureType != null && mGestureType.contains(WXGestureType.toString());
  }

  protected boolean containsEvent(String event){
    return mDomObj.getEvents().contains(event) || mAppendEvents.contains(event);
  }

  public void notifyAppearStateChange(String wxEventType,String direction){
    if(containsEvent(Constants.Event.APPEAR) || containsEvent(Constants.Event.DISAPPEAR)) {
      Map<String, Object> params = new HashMap<>();
      params.put("direction", direction);
      fireEvent(wxEventType, params);
    }
  }

  public boolean isUsing() {
    return isUsing;
  }

  public void setUsing(boolean using) {
    isUsing = using;
  }

  public void readyToRender() {
    if (mParent != null && getInstance().isTrackComponent()) {
      mParent.readyToRender();
    }
  }

  public static class MeasureOutput {

    public int width;
    public int height;
  }

  /**
   * Determine whether the current component needs to be placed in the real View tree
   * @return false component add subview
   */
  public boolean isVirtualComponent(){
    return false;
  }
  public void removeVirtualComponent() {
  }

  public boolean hasScrollParent(WXComponent component) {
    if (component.getParent() == null) {
      return true;
    } else if (component.getParent() instanceof WXScroller) {
      return false;
    } else {
      return hasScrollParent(component.getParent());
    }
  }

  /**
   * Called when property has empty value
   * @param propName
     */
  @CheckResult
  protected Object convertEmptyProperty(String propName, Object originalValue) {
    if (Constants.Name.BACKGROUND_COLOR.equals(propName)) {
      return "transparent";
    }
    return originalValue;
  }

  private void setActiveTouchListener(){
    boolean hasActivePesudo = mDomObj.getStyles().getPesudoStyles().containsKey(Constants.PESUDO.ACTIVE);
    View view;
    if(hasActivePesudo && (view = getRealView()) != null) {
      boolean hasTouchConsumer = (mHostClickListeners != null && mHostClickListeners.size() > 0) || wxGesture != null;
      view.setOnTouchListener(new TouchActivePseudoListener(this,!hasTouchConsumer));
    }
  }

  @Override
  public void updateActivePseudo(boolean isSet) {
    setPseudoClassStatus(Constants.PESUDO.ACTIVE,isSet);
  }

  /**
   *
   * @param clzName like ':active' or ':active:enabled'
   * @param status
   */
  protected void setPseudoClassStatus(String clzName,boolean status){
    WXStyle styles = getDomObject().getStyles();
    Map<String, Map<String,Object>> pesudoStyles = styles.getPesudoStyles();

    if(pesudoStyles == null || pesudoStyles.size() == 0){
      return;
    }
    Map<String,Object> resultStyles = mPesudoStatus.updateStatusAndGetUpdateStyles(
        clzName,
        status,
        pesudoStyles,
        styles.getPesudoResetStyles());
    updateStyleByPesudo(resultStyles);
  }

  private void updateStyleByPesudo(Map<String,Object> styles){
    Message message = Message.obtain();
    WXDomTask task = new WXDomTask();
    task.instanceId = getInstanceId();
    task.args = new ArrayList<>();

    JSONObject styleJson = new JSONObject(styles);
    task.args.add(getRef());
    task.args.add(styleJson);
    task.args.add(true);//flag pesudo
    message.obj = task;
    message.what = WXDomHandler.MsgType.WX_DOM_UPDATE_STYLE;
    WXSDKManager.getInstance().getWXDomManager().sendMessage(message);
  }
}