package com.starcor.xul;

import android.graphics.*;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import com.starcor.xul.Factory.XulFactory;
import com.starcor.xul.Graphics.XulDC;
import com.starcor.xul.Graphics.XulDrawable;
import com.starcor.xul.Prop.XulAttr;
import com.starcor.xul.Prop.XulBinding;
import com.starcor.xul.Prop.XulFocus;
import com.starcor.xul.Prop.XulPropNameCache;
import com.starcor.xul.Render.Drawer.IXulAnimation;
import com.starcor.xul.Render.XulCoverFlashRender;
import com.starcor.xul.Render.XulCustomViewRender;
import com.starcor.xul.Render.XulSliderAreaRender;
import com.starcor.xul.Render.XulViewRender;
import com.starcor.xul.Utils.*;
import com.starcor.xul.Wrapper.XulPageSliderAreaWrapper;
import com.starcor.xul.Wrapper.XulSliderAreaWrapper;

import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;

/**
 * Created by hy on 2014/5/11.
 */
public class XulRenderContext {
	public static final int EVENT_PENDING_IMAGE_LOADED = 0x0001;  // all pending image has loaded, param1 is the round count

	static final String TAG = XulRenderContext.class.getSimpleName();

	ArrayList<XulSelect> _globalSelectors;
	XulPage _page;
	XulTaskCollector _taskCollector;
	XulSuspendTaskCollector _suspendTaskCollector;
	boolean _suspended = false;
	boolean _skipSyncData = false;      // all of changed view's layout have not be finished
	volatile int _pendingImageItemNum = 0;
	volatile int _pendingImageCollectingRound = 0;
	int _pendingImageFinishingRound = 0;

	XulCachedHashMap<XulView, XulView> _dirtyViews = new XulCachedHashMap<XulView, XulView>(64);

	int _scheduledLayoutFinishTasksProtectRange = 0;
	XulSimpleArray<Runnable> _scheduledLayoutFinishedTasks = new XulSimpleArray<Runnable>() {
		@Override
		protected Runnable[] allocArrayBuf(int size) {
			return new Runnable[size];
		}
	};

	boolean _enableMouse;
	MotionFocusFilter focusFilter;

	class FocusTracker {
		Matrix _tmpDrawMatrix = new Matrix();
		RectF _focusRect = new RectF();

		XulView _focusTrackView;

		public FocusTracker(XulView focusTrackerView) {
			_focusTrackView = focusTrackerView;
		}

		public void postDrawFocus(XulViewRender xulViewRender, XulDC dc, Rect rect, int xBase, int yBase) {
			Canvas canvas = dc.getCanvas();
			canvas.getMatrix(_tmpDrawMatrix);

			XulView view = xulViewRender.getView();
			int xDelta = 0;
			int yDelta = 0;

			XulArea parent = view.getParent();
			while (parent != null) {
				XulViewRender render = parent.getRender();
				if (render instanceof XulSliderAreaRender) {
					XulSliderAreaRender sliderRender = (XulSliderAreaRender) render;
					if (sliderRender.isVertical()) {
						yDelta += sliderRender.getScrollDelta();
					} else {
						xDelta += sliderRender.getScrollDelta();
					}
				}
				parent = parent.getParent();
			}

			RectF updateRect = xulViewRender.getUpdateRect();
			updateRect.offset(xBase, yBase);
			_tmpDrawMatrix.mapRect(updateRect);

			if (Math.abs(_focusRect.left - updateRect.left) < 0.1 &&
				Math.abs(_focusRect.top - updateRect.top) < 0.1 &&
				Math.abs(_focusRect.right - updateRect.right) < 0.1 &&
				Math.abs(_focusRect.bottom - updateRect.bottom) < 0.1) {
				return;
			}
			XulUtils.copyRect(updateRect, _focusRect);

			if (!_focusTrackView.isVisible()) {
				_focusTrackView.setStyle(XulPropNameCache.TagId.DISPLAY, "block");
			}

			_focusTrackView.setAttr(XulPropNameCache.TagId.X, String.valueOf((int) ((_focusRect.left + xDelta) / getXScalar())));
			_focusTrackView.setAttr(XulPropNameCache.TagId.Y, String.valueOf((int) ((_focusRect.top + yDelta) / getXScalar())));
			_focusTrackView.setAttr(XulPropNameCache.TagId.WIDTH, String.valueOf((int) (_focusRect.width() / getXScalar())));
			_focusTrackView.setAttr(XulPropNameCache.TagId.HEIGHT, String.valueOf((int) (_focusRect.height() / getXScalar())));
			XulViewRender render = _focusTrackView.getRender();
			render.refreshSizingMovingAnimation();
			_focusTrackView.resetRender();
		}

		public void preDrawFocus(XulViewRender xulViewRender, XulDC dc, Rect rect, int xBase, int yBase) {


		}
	}

	class FlashTracker extends FocusTracker {

		String[] flashClass;
		XulCoverFlashRender flashRender;

		public FlashTracker(XulView focusTrackerView) {
			super(focusTrackerView);
			String trackerClass = focusTrackerView.getAttrString(XulCoverFlashRender.ATTR_FLASH_CLASS);
			flashRender = (XulCoverFlashRender) _focusTrackView._render;
			if (TextUtils.isEmpty(trackerClass)) {
				return;
			}
			if (trackerClass.contains(",")) {
				flashClass = trackerClass.split(",");
			} else {
				flashClass = new String[]{trackerClass};
			}
		}

		@Override
		public void postDrawFocus(XulViewRender xulViewRender, XulDC dc, Rect rect, int xBase, int yBase) {
			if (flashClass == null) {
				flashRender.syncSpecialFlash(xulViewRender.getView());
				super.postDrawFocus(xulViewRender, dc, rect, xBase, yBase);
				return;
			}
            XulView view = xulViewRender.getView();
			for (int i = 0; i < flashClass.length; i++) {
				if (view.hasClass(flashClass[i])) {
					flashRender.syncSpecialFlash(view);
					super.postDrawFocus(xulViewRender, dc, rect, xBase, yBase);
					return;
				}
				XulArrayList<XulView> targetList = view.findItemsByClass(flashClass[i]);
				if (targetList != null && !targetList.isEmpty()) {
					flashRender.syncSpecialFlash(view);
					super.postDrawFocus(targetList.get(0).getRender(), dc, rect, xBase, yBase);
					return;
				}
			}
			flashRender.stopFlash();
		}
	}

	FocusTracker _focusTracker;

	public void preDrawFocus(XulViewRender xulViewRender, XulDC dc, Rect rect, int xBase, int yBase) {
		if (_focusTracker == null) {
			return;
		}
		_focusTracker.preDrawFocus(xulViewRender, dc, rect, xBase, yBase);

	}

	public void postDrawFocus(XulViewRender xulViewRender, XulDC dc, Rect rect, int xBase, int yBase) {
		if (_focusTracker == null) {
			return;
		}
		_focusTracker.postDrawFocus(xulViewRender, dc, rect, xBase, yBase);
	}

	private static class RenderViewList extends XulSimpleArray<XulViewRender> {

		public RenderViewList(int sz) {
			super(sz);
		}

		@Override
		protected XulViewRender[] allocArrayBuf(int size) {
			return new XulViewRender[size];
		}
	}

	private static class AnimationList extends XulSimpleArray<IXulAnimation> {

		public AnimationList(int sz) {
			super(sz);
		}

		@Override
		protected IXulAnimation[] allocArrayBuf(int size) {
			return new IXulAnimation[size];
		}
	}

	RenderViewList _changedViews = new RenderViewList(512);

	AnimationList _animation = new AnimationList(64);

	XulPage.IActionCallback actionCallback = new XulPage.IActionCallback() {
		@Override
		public void doAction(XulView view, String action, String type, String command, Object userdata) {
			if (_handler == null) {
				return;
			}
			try {
				_handler.onDoAction(view, action, type, command, userdata);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	};

	static private Paint _defStrokePaint;
	static private Paint _defTextPaint;
	static private Paint _defShadowTextPaint;
	static private Paint _defSolidPaint;
	static private Paint _defPicPaint;
	static private Paint _defAlphaPicPaint;
	static private Paint _defSolidShadowPaint;
	static private Typeface _defTypeFace;
	private boolean _readyToDraw = false;
	private XulDC _xulDC = new XulDC(this);

	public void setPageSize(int widthPixels, int heightPixels) {
		_page.setPageSize(widthPixels, heightPixels);
	}

	public int getPageWidth() {
		return _page.getPageWidth();
	}

	public int getPageHeight() {
		return _page.getPageHeight();
	}

	public IXulExternalView createExternalView(String cls, int x, int y, int width, int height, XulView view) {
		if (_handler == null) {
			return null;
		}
		return _handler.createExternalView(cls, x, y, width, height, view);
	}

	public boolean isReady() {
		return _readyToDraw;
	}

	public void refreshBinding(String bindingId) {
		if (_page == null) {
			return;
		}
		_page.refreshBinding(bindingId);
	}

	public void refreshBinding(String bindingId, String url) {
		if (_page == null) {
			return;
		}
		_page.refreshBinding(bindingId, url);
	}

	public void refreshBinding(String bindingId, XulDataNode dataNode) {
		if (_page == null) {
			return;
		}
		_page.refreshBinding(bindingId, dataNode);
		_page.applyBinding(getDefaultActionCallback());
	}

	public void updateHandler(IXulRenderHandler handler) {
		_handler = handler;
	}

	public XulPage getPage() {
		return _page;
	}

	long _animationTimestamp;

	public long animationTimestamp() {
		return _animationTimestamp;
	}

	public interface IXulRenderHandler {

		void invalidate(Rect rect);

		void uiRun(Runnable runnable);

		void uiRun(Runnable runnable, int delayMS);

		void onDoAction(XulView view, String action, String type, String command, Object userdata);

		IXulExternalView createExternalView(String cls, int x, int y, int width, int height, XulView view);

		String resolve(XulWorker.DownloadItem item, String path);

		InputStream getAssets(XulWorker.DownloadItem item, String path);

		InputStream getAppData(XulWorker.DownloadItem item, String path);

		InputStream getSdcardData(XulWorker.DownloadItem item, String path);

		void onRenderIsReady();

		void onRenderEvent(int eventId, int param1, int param2, Object msg);
	}

	public interface IXulRenderHandler2 extends IXulRenderHandler {
		Object getCustomObject(int key);
	}

	private IXulRenderHandler _handler;

	public Object getCustomObject(int key) {
		IXulRenderHandler handler = _handler;
		if (handler == null || !(handler instanceof IXulRenderHandler2)) {
			return null;
		}
		return ((IXulRenderHandler2) handler).getCustomObject(key);
	}

	public void invalidate(Rect rect) {
		if (_handler == null) {
			return;
		}
		_handler.invalidate(rect);
	}

	public void uiRun(Runnable runnable, int delayMS) {
		if (_handler == null) {
			Log.e(TAG, "uiRun(delay:" + delayMS + ") - handler is null!!");
			return;
		}
		_handler.uiRun(runnable, delayMS);
	}

	public void uiRun(Runnable runnable) {
		if (_handler == null) {
			try {
				runnable.run();
			} catch (Exception e) {
				e.printStackTrace();
			}
			return;
		}
		_handler.uiRun(runnable);
	}

	private static class TypefaceInfo {
		String name;
		Typeface typeface;
		Paint paint;
	}

	private static XulCachedHashMap<String, TypefaceInfo> _font_map = new XulCachedHashMap<String, TypefaceInfo>();

	static public void addTypeFace(String name, Typeface typeface) {
		TypefaceInfo typefaceInfo = new TypefaceInfo();
		typefaceInfo.name = name;
		typefaceInfo.typeface = typeface;
		_font_map.put(name, typefaceInfo);
	}

	static public void setDefTypeFace(Typeface typeFace) {
		_defTypeFace = typeFace;
	}

	static public Typeface getDefTypeFace() {
		return _defTypeFace;
	}

	static public Paint getDefStrokePaint() {
		if (_defStrokePaint == null) {
			_defStrokePaint = new Paint();
			_defStrokePaint.setColor(Color.WHITE);
			_defStrokePaint.setStrokeWidth(2.0f);
			_defStrokePaint.setAntiAlias(true);
			_defStrokePaint.setStyle(Paint.Style.STROKE);
		}
		return _defStrokePaint;
	}

	static public Paint getDefSolidPaint() {
		if (_defSolidPaint == null) {
			_defSolidPaint = new Paint();
			_defSolidPaint.setAntiAlias(true);
			_defSolidPaint.setColor(Color.WHITE);
			_defSolidPaint.setStrokeWidth(1.0f);
			_defSolidPaint.setStyle(Paint.Style.FILL_AND_STROKE);
		}
		return _defSolidPaint;
	}

	static public Paint getDefSolidShadowPaint() {
		if (_defSolidShadowPaint == null) {
			_defSolidShadowPaint = new Paint();
			_defSolidShadowPaint.setAntiAlias(true);
			_defSolidShadowPaint.setStyle(Paint.Style.FILL_AND_STROKE);
		}
		return _defSolidShadowPaint;
	}

	static public Paint getDefTextPaint() {
		if (_defTextPaint == null) {
			_defTextPaint = new Paint();
			Typeface typeFace = getDefTypeFace();
			if (typeFace != null) {
				_defTextPaint.setTypeface(typeFace);
			}
			_defTextPaint.setAntiAlias(true);
			_defTextPaint.setSubpixelText(true);
			// _defTextPaint.setStyle(Paint.Style.FILL);
		}
		return _defTextPaint;
	}

	static public Paint getTextPaintByName(String name) {
		if (name == null) {
			return getDefTextPaint();
		}
		TypefaceInfo typefaceInfo = _font_map.get(name);
		if (typefaceInfo == null) {
			return getDefTextPaint();
		}
		Paint paint = typefaceInfo.paint;
		if (paint == null) {
			typefaceInfo.paint = paint = new Paint();
			paint.setTypeface(typefaceInfo.typeface);
			paint.setAntiAlias(true);
			paint.setSubpixelText(true);
		}
		return paint;
	}

	static public Paint getDefPicPaint() {
		if (_defPicPaint == null) {
			_defPicPaint = new Paint();
			_defPicPaint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
			_defPicPaint.setColor(Color.BLACK);
			_defPicPaint.setStyle(Paint.Style.FILL_AND_STROKE);
		}
		return _defPicPaint;
	}

	static public Paint getDefAlphaPicPaint() {
		if (_defAlphaPicPaint == null) {
			_defAlphaPicPaint = new Paint();
			_defAlphaPicPaint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
			_defAlphaPicPaint.setColor(Color.BLACK);
			_defAlphaPicPaint.setStyle(Paint.Style.FILL_AND_STROKE);
		}
		return _defAlphaPicPaint;
	}

	static public Paint getDefShadowTextPaint() {
		if (_defShadowTextPaint == null) {
			_defShadowTextPaint = new Paint();
			_defShadowTextPaint.setAntiAlias(true);
			_defShadowTextPaint.setSubpixelText(true);
			Typeface typeFace = getDefTypeFace();
			if (typeFace != null) {
				_defShadowTextPaint.setTypeface(typeFace);
			}
			_defShadowTextPaint.setShadowLayer(3, 0, 0, Color.BLACK);
		}
		return _defShadowTextPaint;
	}

	static public Paint getShadowTextPaintByName(String name) {
		if (name == null) {
			return getDefShadowTextPaint();
		}
		TypefaceInfo typefaceInfo = _font_map.get(name);
		if (typefaceInfo == null) {
			return getDefShadowTextPaint();
		}
		Paint paint = typefaceInfo.paint;
		if (paint == null) {
			typefaceInfo.paint = paint = new Paint();
			paint.setTypeface(typefaceInfo.typeface);
			paint.setAntiAlias(true);
			paint.setSubpixelText(true);
			paint.setShadowLayer(3, 0, 0, Color.BLACK);
		}
		return paint;
	}

	public XulPage.IActionCallback getDefaultActionCallback() {
		return actionCallback;
	}

	public void markDirtyView(XulView view) {
		_dirtyViews.put(view, view);
	}

	public void markDataChanged(XulViewRender view) {
		_skipSyncData = false;
		_changedViews.add(view);
	}

	public void unmarkDirtyView(XulView view) {
		_dirtyViews.remove(view);
	}

	public void unmarkDataChanged(XulViewRender view) {
		_changedViews.remove(view);
	}

	public void addAnimation(IXulAnimation ani) {
		if (_animation.contains(ani)) {
			return;
		}
		_animation.add(ani);
	}

	public void removeAnimation(IXulAnimation ani) {
		_animation.remove(ani);
	}

	public void suspend(boolean isSuspend) {
		if (_suspended == isSuspend) {
			return;
		}
		if (_suspendTaskCollector != null) {
			_suspendTaskCollector.reset();
		}
		_suspended = isSuspend;
	}

	public boolean isSuspended() {
		return _suspended;
	}

	public boolean isDestroyed() {
		return _page == null;
	}

	private synchronized XulWorker.DownloadItem _getPendingItem() {
		if (_suspended) {
			if (_suspendTaskCollector == null) {
				_suspendTaskCollector = new XulSuspendTaskCollector();
			}
			_suspendTaskCollector.init(_page.getLayout());
			_suspendTaskCollector.doSuspendWork();
			return null;
		}
		if (_page == null) {
			return null;
		}
		return _page.getPendingItem();
	}

	private synchronized XulWorker.DrawableItem _getPendingImageItem() {
		if (_suspended) {
			return null;
		}
		if (_page == null || _page.getLayout() == null) {
			return null;
		}
		if (_taskCollector == null) {
			_taskCollector = new XulTaskCollector();
			_taskCollector.init(_page.getLayout());
		}
		XulWorker.DrawableItem drawableItem = _taskCollector.collectPendingDrawableItem();
		if (_taskCollector.isFinished()) {
			++_pendingImageCollectingRound;
		}
		if (drawableItem != null) {
			++_pendingImageItemNum;
		}
		return drawableItem;
	}

	private void _onPendingItemReady(final XulWorker.DownloadItem item, final InputStream data) {
		if (_page == null) {
			return;
		}
		uiRun(new Runnable() {
			@Override
			public void run() {
				XulPage page = _page;
				if (page == null) {
					// page already destroyed
					return;
				}

				boolean bindingFinished = page.isBindingFinished();
				if (page.setPendingItemData(item, data, actionCallback)) {
					if (page != _page) {
						Log.w(TAG, "Page has been destroyed during binding events! " + page.toString());
						return;
					}
					if (bindingFinished) {
						doLayout();
						syncData();
						invalidate(null);
					} else {
						_onBindingFinished();
					}
				}
			}
		});
	}

	private void _onPendingImageItemReady(final XulWorker.DrawableItem item, final XulDrawable bmp) {
		uiRun(new Runnable() {
			@Override
			public void run() {
				if (--_pendingImageItemNum == 0 && _pendingImageCollectingRound > 1) {
					++_pendingImageFinishingRound;
					IXulRenderHandler handler = _handler;
					if (handler != null) {
						handler.onRenderEvent(EVENT_PENDING_IMAGE_LOADED, _pendingImageFinishingRound, 0, null);
					}
				}
				if (item instanceof XulRenderDrawableItem) {
					((XulRenderDrawableItem) item).onImageReady(bmp);
				}
			}
		});
	}

	private boolean _applyBinding() {
		assert _page != null;
		if (!_page.applyBinding(actionCallback)) {
			return false;
		}
		// _page may be set to null during applyBinding()
		if (_page == null) {
			Log.w(TAG, "Page has been destroyed during binding events!");
			return true;
		}
		return _onBindingFinished();
	}

	private boolean _onBindingFinished() {
		XulPage page = _page;
		page.applySelectors();
		page.prepareRender(this);
		page.getLayout().initFocus();
		doLayout();
		syncData();

		if (!_readyToDraw) {
			uiRun(new Runnable() {
				@Override
				public void run() {
					XulPage page = _page;
					if (page == null) {
						Log.e(TAG, "page already destroyed before ready!");
						return;
					}
					XulPage.invokeActionNoPopup(page, "ready", actionCallback);
					if (_handler != null) {
						_handler.onRenderIsReady();
					}
					_readyToDraw = true;
				}
			});
		}
		// UI初始化成功,刷新界面
		invalidate(null);
		return true;
	}

	XulWorker.IXulWorkItemSource _downloader = new XulWorker.IXulWorkItemSource() {
		@Override
		public XulWorker.DownloadItem getDownloadItem() {
			return _getPendingItem();
		}

		@Override
		public void onDownload(XulWorker.DownloadItem item, InputStream data) {
			_onPendingItemReady(item, data);
		}

		@Override
		public XulWorker.DrawableItem getDrawableItem() {
			return _getPendingImageItem();
		}

		@Override
		public void onDrawableLoaded(XulWorker.DrawableItem item, XulDrawable bmp) {
			_onPendingImageItemReady(item, bmp);
		}

		@Override
		public String resolve(XulWorker.DownloadItem item, String path) {
			if (_handler != null) {
				return _handler.resolve(item, path);
			}
			return null;
		}

		@Override
		public InputStream getAssets(XulWorker.DownloadItem item, String path) {
			if (_handler != null) {
				return _handler.getAssets(item, path);
			}
			return null;
		}

		@Override
		public InputStream getAppData(XulWorker.DownloadItem item, String path) {
			if (_handler != null) {
				return _handler.getAppData(item, path);
			}
			return null;
		}

		@Override
		public InputStream getSdcardData(XulWorker.DownloadItem item, String path) {
			if (_handler != null) {
				return _handler.getSdcardData(item, path);
			}
			return null;
		}
	};

	public XulRenderContext(XulPage page, ArrayList<XulSelect> globalSelectors, ArrayList<XulBinding> globalBindings, IXulRenderHandler handler, int pageWidth, int pageHeight) {
		this(page, globalSelectors, globalBindings, handler, pageWidth, pageHeight, false, false);
	}

	public XulRenderContext(XulPage page, ArrayList<XulSelect> globalSelectors, ArrayList<XulBinding> globalBindings, IXulRenderHandler handler, int pageWidth, int pageHeight, final boolean suspend, boolean noGlobalBinding) {
		this(page, globalSelectors, globalBindings, handler, pageWidth, pageHeight, suspend, noGlobalBinding, false);
	}

	public XulRenderContext(XulPage page, ArrayList<XulSelect> globalSelectors, ArrayList<XulBinding> globalBindings, IXulRenderHandler handler, int pageWidth, int pageHeight, final boolean suspend, boolean noGlobalBinding, boolean doNotInit) {
		_globalSelectors = globalSelectors;
		_page = page.makeClone(this, pageWidth, pageHeight);
		_handler = handler;
		if (!noGlobalBinding) {
			_page.setGlobalBindings(globalBindings);
		}
		_suspended = suspend;
		if (doNotInit) {
			return;
		}
		initXulRender();
	}

	private boolean _initialized = false;

	public void initXulRender() {
		if (_initialized) {
			return;
		}
		_initialized = true;
		_page.preloadBinding(_downloader);

		XulPage.invokeActionNoPopup(_page, "load", actionCallback);

		if (!_applyBinding()) {
			_page.applySelectors();
			_page.prepareRender(this);
			_page.getLayout().initFocus();
			doLayout();
			syncData();
		}

		XulView focusTrackerView = getLayout().findItemById("@focus-tracker");
		if (focusTrackerView != null) {
			if (focusTrackerView.getRender() instanceof XulCoverFlashRender) {
				_focusTracker = new FlashTracker(focusTrackerView);
			} else {
				_focusTracker = new FocusTracker(focusTrackerView);
			}
		}

		XulWorker.registerDownloader(_downloader);

		uiRun(new Runnable() {
			Rect _updateRect = new Rect();

			@Override
			public void run() {
				XulPage page = _page;
				if (page == null) {
					return;
				}
				if (_suspended) {
					uiRun(this, 50);
					return;
				}
				final float slowDown = 1f;
				long beginTime = XulUtils.timestamp();
				if (_readyToDraw) {
					final long timestamp = (long) (beginTime * slowDown);
					long newAniTimestamp = (timestamp + 4) & ~0x0007L;
					if (newAniTimestamp != _animationTimestamp) {
						_animationTimestamp = newAniTimestamp;
						if (!_animation.isEmpty()) {
							updateAnimation();
						}
					}

					if (!_dirtyViews.isEmpty()) {
						updateWholeView();
						// updateDirtyRect();
					}
					XulLayout layout = page.getLayout();
					if (layout != null) {
						XulViewRender render = layout.getRender();
						if (render.isLayoutChanged()) {
							updateWholeView();
						}
					}
				}
				long endTime = XulUtils.timestamp();
				long execDelay = endTime - beginTime;
				if (execDelay >= 16) {
					uiRun(this);
				} else {
					uiRun(this, (int) ((12 - execDelay) / slowDown));
				}
			}

			private void updateAnimation() {
				long timestamp = animationTimestamp();
				int curSize = _animation.size();
				for (int i = 0; i < curSize; ) {
					IXulAnimation ani = _animation.get(i);
					if (ani.updateAnimation(timestamp) == false) {
						_animation.remove(i);
						--curSize;
					} else {
						++i;
					}
				}
			}

			private void updateWholeView() {
				_dirtyViews.clear();
				if (!initDraw()) {
					return;
				}
				XulWorker.suspendDrawableWorker(16);
				invalidate(null);
			}

			private void updateDirtyRect() {
				_updateRect.set(99999, 99999, 0, 0);

				for (XulView view : _dirtyViews.values()) {
					RectF rc = view.getRender().getUpdateRect();
					if (_updateRect.left > rc.left) {
						_updateRect.left = XulUtils.roundToInt(rc.left);
					}
					if (_updateRect.top > rc.top) {
						_updateRect.top = XulUtils.roundToInt(rc.top);
					}
					if (_updateRect.right < rc.right) {
						_updateRect.right = XulUtils.roundToInt(rc.right);
					}
					if (_updateRect.bottom < rc.bottom) {
						_updateRect.bottom = XulUtils.roundToInt(rc.bottom);
					}
				}
				_dirtyViews.clear();
				invalidate(_updateRect);
			}
		}, 20);
	}

	public void resetUiTaskCollector() {
		XulTaskCollector taskCollector = _taskCollector;
		if (taskCollector == null) {
			return;
		}
		taskCollector.reset();
	}

	public void destroy() {
		_readyToDraw = false;
		XulWorker.unregisterDownloader(_downloader);
		if (_page != null) {
			_page.destroy();
			_page = null;
		}
		_handler = null;
	}

	public XulLayout getLayout() {
		return _page.getLayout();
	}

	public XulView findItemById(String id) {
		if (_page == null) {
			return null;
		}
		return _page.findItemById(id);
	}

	public XulArrayList<XulView> findItemsByClass(String... cls) {
		if (_page == null) {
			return null;
		}
		return _page.findItemsByClass(cls);
	}

	public XulView findCustomItemByExtView(IXulExternalView extView) {
		if (_page == null) {
			return null;
		}
		return _page.findCustomItemByExtView(extView);
	}

	public float getXScalar() {
		return _page.getXScalar();
	}

	public float getYScalar() {
		return _page.getYScalar();
	}

	XulUtils.ticketMarker _tsMarker;

	{
		if (XulManager.PERFORMANCE_BENCH) {
			_tsMarker = new XulUtils.ticketMarker("render duration ", false);
		}
	}

	public boolean beginDraw(Canvas canvas, Rect rect) {
		if (!initDraw()) {
			return false;
		}
		return beginDrawEx(canvas, rect);
	}

	public boolean beginDrawEx(Canvas canvas, Rect rect) {
		if (_tsMarker != null) {
			_tsMarker.mark("");
		}
		_xulDC.beginDraw(canvas);
		if (_tsMarker != null) {
			_tsMarker.mark("begin");
		}
		_page.draw(_xulDC, rect, 0, 0);
		if (_tsMarker != null) {
			_tsMarker.mark("page");
		}
		return true;
	}

	public boolean initDraw() {
		// if (!_readyToDraw) {
		// 	return false;
		// }
		if (_tsMarker != null) {
			_tsMarker.reset();
		}
		if (_page == null) {
			return false;
		}
		if (_tsMarker != null) {
			_tsMarker.mark();
		}
		if (!_dirtyViews.isEmpty()) {
			// Rect focusRc = _page.getLayout().getFocusRc();
			// Rect updateRc = new Rect(focusRc.right, focusRc.bottom, focusRc.left, focusRc.top);
			// for (XulView view : _dirtyViews) {
			// 	Rect viewRC = view.getFocusRc();
			// 	if (updateRc.bottom < viewRC.bottom) {
			// 		updateRc.bottom = viewRC.bottom;
			// 	}
			// 	if (updateRc.right < viewRC.right) {
			// 		updateRc.right = viewRC.right;
			// 	}
			// 	if (updateRc.left > viewRC.left) {
			// 		updateRc.left = viewRC.left;
			// 	}
			// 	if (updateRc.top > viewRC.top) {
			// 		updateRc.top = viewRC.top;
			// 	}
			// }
			// _dirtyViews.clear();
			// rect = updateRc;
		}

		// canvas.save();
		// canvas.clipRect(rect);
		doLayout();
		if (_tsMarker != null) {
			_tsMarker.mark("layout");
		}

		syncData();
		if (_tsMarker != null) {
			_tsMarker.mark("syncData");
		}

		XulLayout layout = _page.getLayout();
		if (layout != null) {
			XulViewRender render = layout.getRender();
			if (render != null && render.isLayoutChanged()) {
				internalDoLayout();
				if (render.isLayoutChanged()) {
					Log.e(TAG, "invalid layout state!!!!");
				}
				_page.markDirtyView();
			}
		}
		return true;
	}

	private void syncData() {
		for (int round = 0; !_skipSyncData && round < 3 && !_changedViews.isEmpty(); ++round) {
			int viewNum = _changedViews.size();
			int emptyNum = 0;
			for (int i = 0; i < viewNum; ++i) {
				XulViewRender changedView = _changedViews.get(i);
				if (changedView == null) {
					// FIXME: find the root cause
					emptyNum++;
					continue;
				}
				if (changedView.getDrawingRect() == null) {
					_changedViews.add(changedView);
					emptyNum++;
					continue;
				}
				try {
					changedView.doSyncData();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			_changedViews.remove(0, viewNum);
			if (emptyNum == viewNum) {
				// skip sync data operation, until layout or another view has been changed
				_skipSyncData = true;
				break;
			}
		}
	}

	public static void suspendDrawableWorker() {
		XulWorker.suspendDrawableWorker(80);
	}

	public void endDraw() {
		if (_tsMarker != null) {
			_tsMarker.mark("delay");
		}
		_xulDC.endDraw();
		if (_tsMarker != null) {
			_tsMarker.mark("end");
		}

		if (_tsMarker != null) {
			Log.d(TAG, _tsMarker.toString());
		}
	}

	// 对元素进行布局
	public void doLayout() {
		if (internalDoLayout()) {
			return;
		}

		int protectRangeBegin = _scheduledLayoutFinishTasksProtectRange;
		int protectRangeEnd = _scheduledLayoutFinishedTasks.size();
		if (protectRangeEnd > 0) {
			_scheduledLayoutFinishTasksProtectRange = protectRangeEnd;
			Runnable[] data = _scheduledLayoutFinishedTasks.getArray();
			for (int i = protectRangeBegin; i < protectRangeEnd; i++) {
				Runnable runnable = data[i];
				data[i] = null;
				try {
					runnable.run();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			_scheduledLayoutFinishedTasks.remove(protectRangeBegin, protectRangeEnd);
			_scheduledLayoutFinishTasksProtectRange = protectRangeBegin;
		}
	}

	public boolean internalDoLayout() {
		XulPage page = _page;
		if (page == null) {
			return true;
		}
		XulLayout layout = page.getLayout();
		if (layout == null) {
			return true;
		}
		XulViewRender render = layout.getRender();
		if (render == null) {
			return true;
		}
		if (render.isLayoutChanged()) {
			_skipSyncData = false;
		}
		page.doLayout(0, 0);
		return false;
	}

	public void scheduleLayoutFinishedTask(Runnable runnable) {
		if (_scheduledLayoutFinishedTasks.contains(runnable)) {
			return;
		}
		_scheduledLayoutFinishedTasks.add(runnable);
	}

	private ArrayList<WeakReference<XulView>> _findViewCache = new ArrayList<WeakReference<XulView>>();

	ArrayList<WeakReference<XulView>> findViewsByPoint(final int event, final PointF pt) {
		_findViewCache.clear();
		_page.getLayout().eachChild(new XulArea.XulAreaIterator() {

			@Override
			public boolean onXulArea(int pos, XulArea area) {
				if (area.hitTest(event, pt.x, pt.y)) {
					float x = pt.x, y = pt.y;
					area.hitTestTranslate(pt);
					_findViewCache.add(area.getWeakReference());
					area.eachChild(this);
					pt.set(x, y);
				}
				return super.onXulArea(pos, area);
			}

			@Override
			public boolean onXulItem(int pos, XulItem item) {
				if (item.hitTest(event, pt.x, pt.y)) {
					_findViewCache.add(item.getWeakReference());
				}
				return super.onXulItem(pos, item);
			}
		});
		return _findViewCache;
	}

	WeakReference<XulView> lastMotionDownEventView;
	private static PointF _motionEventsHitTestPt = new PointF();
	private static PointF _downEventPoint = new PointF();
	private static PointF _tmpEventPoint = new PointF();

	private XulView pageScrollView;
	private boolean isMotionDown;
	private boolean isDragScrolled;

	// motion point最大移动距离,若超出,则出现移动事件丢失现象
	private static final int DISTANCE_THRESHOLD = 60;

	public boolean onMotionEvent(MotionEvent event) {
		XulLayout layout;
		if (_page == null || (layout = _page.getLayout()) == null) {
			lastMotionDownEventView = null;
			return false;
		}
		float x = event.getX();
		float y = event.getY();

		int action = event.getAction();
		switch (action & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_HOVER_MOVE:
		case MotionEvent.ACTION_MOVE:
			if (_enableMouse) {
				if (isMotionDown && doDragScroll(event) ) {
					isDragScrolled = true;
					_motionEventsHitTestPt.set(x, y);
                    return true;
                }

				final float distanceX = getDistanceX(event, _motionEventsHitTestPt);
				final float distanceY = getDistanceY(event, _motionEventsHitTestPt);
				if ((Math.abs(distanceX) > DISTANCE_THRESHOLD) || (Math.abs(distanceY) > DISTANCE_THRESHOLD)) {
					// 移动距离超过阈值,出现移动事件丢失现象
					int steps = (int) (Math.ceil(Math.max(Math.abs(distanceX), Math.abs(distanceY))
							/ DISTANCE_THRESHOLD));
					float xStep = distanceX / steps;
					float yStep = distanceY / steps;

					// 使用线性插值算法补充缺失的移动事件
					boolean isHandled = false;
					for (int i = 0; i < steps; i++) {
						isHandled |= handleMotionEvent(_motionEventsHitTestPt.x + xStep,
								_motionEventsHitTestPt.y + yStep);
					}

					return isHandled;
				} else {
					_motionEventsHitTestPt.set(x, y);
					return handleMotionEvent(x, y);
				}
			}
			break;
		case MotionEvent.ACTION_SCROLL: {
			_motionEventsHitTestPt.set(x, y);
			ArrayList<WeakReference<XulView>> viewsByPoint = findViewsByPoint(XulManager.HIT_EVENT_SCROLL, _motionEventsHitTestPt);
			if (viewsByPoint.isEmpty()) {
				return false;
			}
			float hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
			float vScroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
			for (int i = viewsByPoint.size() - 1; i >= 0; --i) {
				XulView xulView = viewsByPoint.get(i).get();
				if (xulView != null && xulView.handleScrollEvent(hScroll, vScroll)) {
					viewsByPoint.clear();
					xulView.resetRender();
					return true;
				}
			}
		}
		break;
		case MotionEvent.ACTION_DOWN: {
			pageScrollView = null;
			isMotionDown = true;
			isDragScrolled = false;
			_downEventPoint.set(x, y);

			lastMotionDownEventView = null;
			_motionEventsHitTestPt.set(x, y);

			ArrayList<WeakReference<XulView>> viewsByPoint = findViewsByPoint(XulManager.HIT_EVENT_DOWN, _motionEventsHitTestPt);
			if (viewsByPoint.isEmpty()) {
				//fix drag in screen which no view, ACTION_DOWN return false and then ACTION_UP will not
				// be call.but i need ACTION_UP event to  calculate move distance.so return true.
				return true;
			}
			for (int i = 0, viewsByPointSize = viewsByPoint.size(); i < viewsByPointSize; i++) {
				XulView xulView = viewsByPoint.get(i).get();
				if (xulView != null && xulView.focusable()) {
					lastMotionDownEventView = xulView.getWeakReference();
					layout.requestFocus(xulView);
					viewsByPoint.clear();
					if (xulView.getRender() instanceof XulCustomViewRender) {
						if ("Android.EditBox".equals(xulView.getAttrString("class"))) {
							return false;
						}
					}
					return true;
				}
			}
			return true;
		}
		case MotionEvent.ACTION_UP: {
			_motionEventsHitTestPt.set(x, y);

			isMotionDown = false;
			if (pageScrollView != null) {
				dragToFlipPage(pageScrollView, event);
				pageScrollView = null;
				return true;
			}
			pageScrollView = null;
			if (isDragScrolled) {
				isDragScrolled = false;
				return true;
			}

			ArrayList<WeakReference<XulView>> viewsByPoint = findViewsByPoint(XulManager.HIT_EVENT_UP, _motionEventsHitTestPt);
			if (viewsByPoint.isEmpty() || lastMotionDownEventView == null) {
				lastMotionDownEventView = null;
				return false;
			}
			for (int i = 0, viewsByPointSize = viewsByPoint.size(); i < viewsByPointSize; i++) {
				XulView xulView = viewsByPoint.get(i).get();
				if (xulView != null && xulView.focusable()) {
					viewsByPoint.clear();
					XulView downEventView = lastMotionDownEventView.get();
					if (downEventView == xulView) {
						layout.doClick(actionCallback);
					}
					if (xulView.getRender() instanceof XulCustomViewRender) {
						if ("Android.EditBox".equals(xulView.getAttrString("class"))) {
							return false;
						}
					}
					return true;
				}
			}
		}
		break;
		}

		return false;
	}

	int lastKeyAction;
	int lastKeyCode;
	boolean lastDownHandled;
	WeakReference<XulView> lastKeyEventView;

	public boolean onKeyEvent(KeyEvent event) {
		XulLayout layout;
		if (_page == null || (layout = _page.getLayout()) == null) {
			lastKeyAction = -1;
			lastKeyCode = -1;
			lastKeyEventView = null;
			lastDownHandled = false;
			return false;
		}
		int keyAction = event.getAction();
		int keyCode = event.getKeyCode();
		XulView focusView = layout.getFocus();
		boolean ret = false;

		if (keyAction == KeyEvent.ACTION_DOWN) {
			if (layout.onKeyEvent(event)) {
				lastKeyAction = keyAction;
				lastKeyCode = keyCode;
				lastKeyEventView = getWeakReference(focusView);
				ret = true;
			} else switch (keyCode) {
			case KeyEvent.KEYCODE_DPAD_LEFT:
				ret = layout.moveFocus(XulLayout.FocusDirection.MOVE_LEFT);
				break;
			case KeyEvent.KEYCODE_DPAD_RIGHT:
				ret = layout.moveFocus(XulLayout.FocusDirection.MOVE_RIGHT);
				break;
			case KeyEvent.KEYCODE_DPAD_UP:
				ret = layout.moveFocus(XulLayout.FocusDirection.MOVE_UP);
				break;
			case KeyEvent.KEYCODE_DPAD_DOWN:
				ret = layout.moveFocus(XulLayout.FocusDirection.MOVE_DOWN);
				break;
			case KeyEvent.KEYCODE_DPAD_CENTER:
			case KeyEvent.KEYCODE_ENTER:
				ret = layout.doClick(actionCallback);
				break;
			case KeyEvent.KEYCODE_BACK:
				ret = _page.popStates();
				if (ret) {
					XulPage.invokeActionNoPopup(_page, "statesRestored");
				}
				break;
			}
			lastDownHandled = ret;
		} else if (keyAction == KeyEvent.ACTION_UP) {
			XulView xulView = lastKeyEventView == null ? null : lastKeyEventView.get();
			if (xulView != focusView) {
				ret = lastDownHandled;
			} else if (layout.onKeyEvent(event)) {
				lastKeyAction = keyAction;
				lastKeyCode = keyCode;
				lastKeyEventView = getWeakReference(focusView);
				ret = true;
			} else switch (keyCode) {
			default:
				ret = lastDownHandled;
				break;
			}
			lastDownHandled = false;
		}

		lastKeyAction = keyAction;
		lastKeyCode = keyCode;
		lastKeyEventView = getWeakReference(focusView);

		if (ret) {
			suspendDrawableWorker();
		}
		return ret;
	}

	private WeakReference<XulView> getWeakReference(XulView focusView) {
		if (focusView == null) {
			return null;
		}
		return focusView.getWeakReference();
	}

	public XulCreator newXulCreator() {
		return new XulCreator();
	}

	public class XulCreator {
		private XulSimpleArray<XulView> _newNodes = new XulSimpleArray<XulView>(32) {
			@Override
			protected XulView[] allocArrayBuf(int size) {
				return new XulView[size];
			}
		};

		protected void addNewNode(XulArea area) {
			XulView[] nodes = _newNodes.getArray();
			for (int i = _newNodes.size() - 1; i >= 0; --i) {
				XulView node = nodes[i];
				if (node.isChildOf(area)) {
					_newNodes.remove(i);
				}
			}
			_newNodes.add(area);
		}

		protected void addNewNode(XulItem item) {
			XulView[] nodes = _newNodes.getArray();
			for (int i = _newNodes.size() - 1; i >= 0; --i) {
				XulView node = nodes[i];
				if (node instanceof XulItem) {
					continue;
				}
				if (item.isChildOf(node)) {
					return;
				}
			}
			_newNodes.add(item);
		}

		public XulArea createChildArea(XulArea parent, int pos, String id, String type, String binding) {
			XulArea area = new XulArea(parent, pos, type);
			area._id = id;
			area._binding = binding;
			addNewNode(area);
			return area;
		}

		public XulArea createChildArea(XulArea parent, int pos, String id, String type) {
			return createChildArea(parent, pos, id, type, null);
		}

		public XulArea createChildArea(XulArea parent, int pos, String type) {
			return createChildArea(parent, pos, null, type);
		}

		public XulArea createChildArea(XulArea parent, String id, String type, String binding) {
			return createChildArea(parent, -1, id, type, binding);
		}

		public XulArea createChildArea(XulArea parent, String id, String type) {
			return createChildArea(parent, -1, id, type, null);
		}

		public XulArea createChildArea(XulArea parent, String type) {
			return createChildArea(parent, -1, null, type);
		}

		public XulItem createChildItem(XulArea parent, int pos, String id, String type, String binding) {
			XulItem item = new XulItem(parent, pos);
			item._id = id;
			item._type = type;
			item._binding = binding;
			addNewNode(item);
			return item;
		}

		public XulItem createChildItem(XulArea parent, int pos, String id, String type) {
			return createChildItem(parent, pos, id, type, null);
		}

		public XulItem createChildItem(XulArea parent, int pos, String id) {
			return createChildItem(parent, pos, id, null, null);
		}

		public XulItem createChildItem(XulArea parent, String id, String type, String binding) {
			return createChildItem(parent, -1, id, type, binding);
		}

		public XulItem createChildItem(XulArea parent, String id, String type) {
			return createChildItem(parent, -1, id, type, null);
		}

		public XulItem createChildItem(XulArea parent, String type) {
			return createChildItem(parent, -1, null, type);
		}

		public XulFocus createFocusProp(XulView owner, final String mode, final String priority) {
			XulFocus._Builder builder = XulFocus._Builder.create(owner);
			builder.initialize("item", new XulFactory.Attributes() {
				@Override
				public String getValue(String name) {
					if ("mode".equals(name)) {
						return mode;
					}
					if ("priority".equals(name)) {
						return priority;
					}
					return null;
				}

				@Override
				public String getValue(int i) {
					return null;
				}

				@Override
				public int getLength() {
					return 0;
				}

				@Override
				public String getName(int i) {
					return null;
				}
			});
			return (XulFocus) builder.finalItem();
		}

		public XulFocus createFocusProp(XulView owner, String mode) {
			return createFocusProp(owner, mode, null);
		}

		public void finish() {
			XulView[] nodes = _newNodes.getArray();
			XulRenderContext ctx = XulRenderContext.this;
			for (int i = 0, size = _newNodes.size(); i < size; i++) {
				XulView node = nodes[i];
				node.prepareRender(ctx);
				if (node instanceof XulArea) {
					_page.addSelectorTargets((XulArea) node);
					_page.applySelectors((XulArea) node);
				} else {
					_page.addSelectorTarget((XulItem) node);
					_page.applySelectors((XulItem) node);
				}
			}
		}
	}

	public void setEnableMouse(boolean enable) {
		_enableMouse = enable;
	}

	public void setMotionFocusFilter(MotionFocusFilter filter) {
		focusFilter = filter;
	}

	private boolean handleMotionEvent(float x, float y) {
		_motionEventsHitTestPt.set(x, y);
		ArrayList<WeakReference<XulView>> viewsByPoint = null;
		try {
			viewsByPoint = findViewsByPoint(XulManager.HIT_EVENT_DOWN, _motionEventsHitTestPt);
		} catch (Exception e) {
			Log.d(TAG, "findViewsByPoint failed!");
		}
		if (viewsByPoint == null) {
			Log.d(TAG, "viewsByPoint null");
			return false;
		}

		for (WeakReference<XulView> viewRef : viewsByPoint) {
			XulView view = viewRef.get();
			if (view != null && view.focusable()) {
				if (view.isFocused()) {
					return false;
				} else {
					doRequestFocus(view);
					return true;
				}
			}
		}
		return false;
	}

	public void doRequestFocus(XulView view) {
		if (view == null) {
			return;
		}
		if (focusFilter != null) {
			if (focusFilter.focusable(view)) {
				getLayout().requestFocus(view);
			}
			return;
		}

		getLayout().requestFocus(view);
	}

	private float getDistanceY(MotionEvent event, PointF point) {
		return event.getY() - point.y;
	}

	private float getDistanceX(MotionEvent event, PointF point) {
		return event.getX() - point.x;
	}

	private boolean isVerticalScroll(float diffX, float diffY) {
		return Math.abs(diffX) < Math.abs(diffY);
	}

	private boolean doDragScroll(MotionEvent event) {
		float diffX = getDistanceX(event, _motionEventsHitTestPt);
		float diffY = getDistanceY(event, _motionEventsHitTestPt);
		boolean isVertical = isVerticalScroll(diffX, diffY);
		float diff = isVertical? diffY : diffX;
		if (diff == 0) {
			return false;
		}

		XulView view = getScrollView(event, isVertical);
		if (view == null) {
			return false;
		}

		if ("slider".equals(view.getType())) {
			XulSliderAreaWrapper sliderAreaWrapper = XulSliderAreaWrapper.fromXulView(view);
			if (sliderAreaWrapper != null) {
				float pos = sliderAreaWrapper.getScrollPos();
				float nextPos = diff > 0? pos - Math.abs(diff) : pos + Math.abs(diff);
				sliderAreaWrapper.scrollTo((int) nextPos, false);
				XulPage.invokeActionNoPopupWithArgs(view, "dragStart", String.valueOf(diffX), String.valueOf(diffY));
				return true;
			}
		} else if ("drag_page_slider".equals(view.getType())) {
			if (isVertical) {
				diffX = 0;
			} else {
				diffY = 0;
			}
			if (view.handleScrollEvent(diffX, diffY)) {
				pageScrollView = view;
				XulPage.invokeActionNoPopupWithArgs(view, "dragStart", String.valueOf(diffX), String.valueOf(diffY));
				return true;
			}
		}
		return false;

	}

	private XulView getScrollView(MotionEvent event, boolean isVertical) {
		_tmpEventPoint.set(event.getX(), event.getY());
		ArrayList<WeakReference<XulView>> viewsByPoint = findViewsByPoint(XulManager.HIT_EVENT_SCROLL, _tmpEventPoint);
		if (viewsByPoint.isEmpty()) {
			return null;
		}

		for (int i = viewsByPoint.size() - 1; i >= 0; --i) {
			XulView xulView = viewsByPoint.get(i).get();
			if (xulView != null && "slider".equals(xulView.getType())) {
				XulSliderAreaWrapper sliderAreaWrapper = XulSliderAreaWrapper.fromXulView(xulView);
				boolean direction = sliderAreaWrapper.isVertical();
				if ((direction == isVertical)) {
					if (sliderAreaWrapper.getScrollRange() > 0) {
						return xulView;
					}
				}
			}
		}
		for (int i = viewsByPoint.size() - 1; i >= 0; --i) {
			XulView xulView = viewsByPoint.get(i).get();
			if (xulView != null && "drag_page_slider".equals(xulView.getType())) {
				XulAttr directionAttr = xulView.getAttr(XulPropNameCache.TagId.DIRECTION);
				boolean _isVertical = false;
				if (directionAttr != null) {
					XulPropParser.xulParsedAttr_Direction direction = directionAttr.getParsedValue();
					_isVertical = direction.vertical;
				}
				if (_isVertical == isVertical) {
					return xulView;
				}
			}
		}
		return null;
	}

	private void dragToFlipPage(XulView view, MotionEvent event) {
		if (view == null || event == null) {
			return;
		}

		String type = view.getType();
		switch (type) {
			case "drag_page_slider":
				float diff = getDistanceX(event, _downEventPoint);
				XulAttr directionAttr = view.getAttr(XulPropNameCache.TagId.DIRECTION);
				boolean _isVertical = false;
				if (directionAttr != null) {
					XulPropParser.xulParsedAttr_Direction direction = directionAttr.getParsedValue();
					_isVertical = direction.vertical;
				}
				if (_isVertical) {
					diff = getDistanceY(event, _downEventPoint);
				}
				XulPageSliderAreaWrapper sliderAreaWrapper = XulPageSliderAreaWrapper.fromXulView(view);
				int pageIndex = sliderAreaWrapper.getCurrentPage();
				if (diff > 600) {
					pageIndex = pageIndex == 0 ? 0: pageIndex - 1;
				} else if (diff < -600){
					pageIndex = pageIndex == sliderAreaWrapper.getPageCount() -1  ? sliderAreaWrapper
							.getPageCount() -1 : pageIndex + 1;
				}
				sliderAreaWrapper.setCurrentPage(pageIndex);
				XulPage.invokeActionNoPopupWithArgs(view, "dragStopped", String.valueOf(diff), String.valueOf(diff));
				break;
		}
	}


	public interface MotionFocusFilter {
		boolean focusable(XulView view);
	}
}