package com.xtremelabs.robolectric.shadows;

import android.view.MotionEvent;
import com.xtremelabs.robolectric.Robolectric;
import com.xtremelabs.robolectric.internal.Implementation;
import com.xtremelabs.robolectric.internal.Implements;
import com.xtremelabs.robolectric.internal.RealObject;

import java.lang.reflect.Constructor;

/**
 * Shadow for {@code MotionEvent} that uses reflection to create {@code MotionEvent} objects, which cannot otherwise
 * be constructed.
 */
@SuppressWarnings({"UnusedDeclaration"})
@Implements(MotionEvent.class)
public class ShadowMotionEvent {
    @RealObject private MotionEvent realObject;

    private int action;
    private float[] x = new float[2];
    private float[] y = new float[2];
    private int pointerCount = 1;
    private long downTime;
    private long eventTime;
    private int[] pointerIds = new int[2];
    private int pointerIndex;

    @Implementation
    public static MotionEvent obtain(long downTime, long eventTime, int action, float x, float y, int metaState) {
        try {
            Constructor<MotionEvent> constructor = MotionEvent.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            MotionEvent motionEvent = constructor.newInstance();
            ShadowMotionEvent shadowMotionEvent = (ShadowMotionEvent) Robolectric.shadowOf_(motionEvent);
            shadowMotionEvent.x[0] = x;
            shadowMotionEvent.y[0] = y;
            shadowMotionEvent.action = action;
            shadowMotionEvent.downTime = downTime;
            shadowMotionEvent.eventTime = eventTime;
            return motionEvent;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Implementation
    public static MotionEvent obtain(MotionEvent motionEvent) {
        return obtain(motionEvent.getDownTime(), motionEvent.getEventTime(), motionEvent.getAction(), motionEvent.getX(), motionEvent.getY(), motionEvent.getMetaState());
    }

    @Implementation
    public int getAction() {
        return action | (pointerIndex << MotionEvent.ACTION_POINTER_ID_SHIFT);
    }

    @Implementation
    public float getRawX() {
        return getX();
    }

    @Implementation
    public float getRawY() {
        return getY();
    }

    @Implementation
    public final float getX() {
        return getX(0);
    }

    @Implementation
    public final float getY() {
        return getY(0);
    }

    @Implementation
    public final float getX(int pointerIndex) {
        return x[pointerIndex];
    }

    @Implementation
    public final float getY(int pointerIndex) {
        return y[pointerIndex];
    }
    
    @Implementation
    public final int getPointerCount() {
    	return pointerCount;
    }

    @Implementation
    public final long getEventTime() {
        return eventTime;
    }

    @Implementation
    public final long getDownTime() {
        return downTime;
    }

    @Implementation
    public final int getPointerId(int index) {
        return pointerIds[index];
    }

    @Implementation
    public final int findPointerIndex(int id) {
        for (int i = 0; i < pointerIds.length; i++) {
            int pointerId = pointerIds[i];

            if (pointerId == id) {
                return i;
            }
        }
        return -1;
    }

    @Implementation
    public final int getActionMasked() {
        return action;
    }

    @Implementation
    public final int getActionIndex() {
        return pointerIndex;
    }

    @Implementation
    public final float getPressure(int pointerIndex) {
        return 1.0f;
    }

    @Implementation
    public final void setLocation(float x, float y) {
        this.x[0] = x;
        this.y[0] = y;
    }

    public MotionEvent setPointer2(float x, float y) {
        this.x[1] = x;
        this.y[1] = y;
        pointerCount = 2;
        return realObject;
    }

    public void setPointerIndex(int pointerIndex) {
        this.pointerIndex = pointerIndex;
    }

    public void setPointerIds(int index0PointerId, int index1PointerId) {
        pointerIds[0] = index0PointerId;
        pointerIds[1] = index1PointerId;
    }
}
