/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * 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 android.widget.cts;

import com.android.cts.stub.R;
import com.android.internal.view.menu.ContextMenuBuilder;

import dalvik.annotation.BrokenTest;
import dalvik.annotation.TestLevel;
import dalvik.annotation.TestTargetClass;
import dalvik.annotation.TestTargetNew;
import dalvik.annotation.TestTargets;
import dalvik.annotation.ToBeFixed;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import android.test.TouchUtils;
import android.test.UiThreadTest;
import android.test.ViewAsserts;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.ContextMenu;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Transformation;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.AdapterView.OnItemSelectedListener;

import java.io.IOException;

/**
 * Test {@link Gallery}.
 */
@TestTargetClass(Gallery.class)
public class GalleryTest extends ActivityInstrumentationTestCase2<GalleryStubActivity>  {
    private Gallery mGallery;
    private Activity mActivity;
    private Instrumentation mInstrumentation;
    private Context mContext;
    private final static float DELTA = 0.01f;

    public GalleryTest() {
        super("com.android.cts.stub", GalleryStubActivity.class);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        mActivity = getActivity();
        mInstrumentation = getInstrumentation();
        mContext = mInstrumentation.getContext();
        mGallery = (Gallery) mActivity.findViewById(R.id.gallery_test);
    }

    @TestTargets({
        @TestTargetNew(
            level = TestLevel.COMPLETE,
            method = "Gallery",
            args = {android.content.Context.class}
        ),
        @TestTargetNew(
            level = TestLevel.COMPLETE,
            method = "Gallery",
            args = {android.content.Context.class, android.util.AttributeSet.class}
        ),
        @TestTargetNew(
            level = TestLevel.COMPLETE,
            method = "Gallery",
            args = {android.content.Context.class, android.util.AttributeSet.class, int.class}
        )
    })
    @ToBeFixed(bug = "1695243", explanation = "Android API javadocs are incomplete")
    public void testConstructor() {
        new Gallery(mContext);

        new Gallery(mContext, null);

        new Gallery(mContext, null, 0);

        XmlPullParser parser = getActivity().getResources().getXml(R.layout.gallery_test);
        AttributeSet attrs = Xml.asAttributeSet(parser);
        new Gallery(mContext, attrs);
        new Gallery(mContext, attrs, 0);

        try {
            new Gallery(null);
            fail("should throw NullPointerException.");
        } catch (NullPointerException e) {
            // expected
        }

        try {
            new Gallery(null, null);
            fail("should throw NullPointerException.");
        } catch (NullPointerException e) {
            // expected
        }

        try {
            new Gallery(null, null, 0);
            fail("should throw NullPointerException.");
        } catch (NullPointerException e) {
            // expected
        }
    }

    @TestTargets({
        @TestTargetNew(
            level = TestLevel.COMPLETE,
            method = "setCallbackDuringFling",
            args = {boolean.class}
        ),
        @TestTargetNew(
            level = TestLevel.COMPLETE,
            method = "onTouchEvent",
            args = {android.view.MotionEvent.class}
        ),
        @TestTargetNew(
            level = TestLevel.COMPLETE,
            method = "onFling",
            args = {MotionEvent.class, MotionEvent.class, float.class, float.class}
        ),
        @TestTargetNew(
            level = TestLevel.COMPLETE,
            method = "onDown",
            args = {android.view.MotionEvent.class}
        )
    })
    @BrokenTest("listener.isItemSelected() is false, need to investigate")
    public void testSetCallbackDuringFling() {
        MockOnItemSelectedListener listener = new MockOnItemSelectedListener();
        mGallery.setOnItemSelectedListener(listener);

        mGallery.setCallbackDuringFling(true);

        int[] xy = new int[2];
        getSelectedViewCenter(mGallery, xy);

        // This drags over only one item.
        TouchUtils.drag(this, xy[0], 0, xy[1], xy[1], 1);

        listener.reset();
        // This will drags over several items.
        TouchUtils.drag(this, xy[0], 0, xy[1], xy[1], 1);

        assertTrue(listener.isItemSelected());
        // onItemSelected called more than once
        assertTrue(listener.getItemSelectedCalledCount() > 1);

        listener.reset();
        mGallery.setCallbackDuringFling(false);

        TouchUtils.drag(this, xy[0], 240, xy[1], xy[1], 1);

        assertTrue(listener.isItemSelected());
        // onItemSelected called only once
        assertTrue(listener.getItemSelectedCalledCount() == 1);
    }

    private void getSelectedViewCenter(Gallery gallery, int[] xy) {
        View v = gallery.getSelectedView();
        v.getLocationOnScreen(xy);

        final int viewWidth = v.getWidth();
        final int viewHeight = v.getHeight();

        xy[1] += viewHeight / 2;
        xy[0] += viewWidth / 2;
    }

    @TestTargetNew(
        level = TestLevel.NOT_FEASIBLE,
        method = "setAnimationDuration",
        args = {int.class}
    )
    @ToBeFixed(bug = "1386429", explanation = "No getter and can't check indirectly. "
            + "It is hard to get transition animation to check if the duration is right.")
    public void testSetAnimationDuration() {
    }

    @TestTargetNew(
        level = TestLevel.COMPLETE,
        method = "setSpacing",
        args = {int.class}
    )
    public void testSetSpacing() throws Throwable {
        setSpacingAndCheck(0);

        setSpacingAndCheck(5);

        setSpacingAndCheck(-1);
    }

    private void setSpacingAndCheck(final int spacing) throws Throwable {
        runTestOnUiThread(new Runnable() {
            public void run() {
                mGallery.setSpacing(spacing);
                mGallery.requestLayout();
            }
        });
        mInstrumentation.waitForIdleSync();

        View v0 = mGallery.getChildAt(0);
        View v1 = mGallery.getChildAt(1);
        assertEquals(v0.getRight() + spacing, v1.getLeft());
    }

    @TestTargets({
        @TestTargetNew(
            level = TestLevel.COMPLETE,
            method = "setUnselectedAlpha",
            args = {float.class}
        ),
        @TestTargetNew(
            level = TestLevel.COMPLETE,
            method = "getChildStaticTransformation",
            args = {android.view.View.class, android.view.animation.Transformation.class}
        )
    })
    public void testSetUnselectedAlpha() {
        final MyGallery gallery = (MyGallery) mActivity.findViewById(R.id.gallery_test);

        checkUnselectedAlpha(gallery, 0.0f);

        checkUnselectedAlpha(gallery, 0.5f);
    }

    private void checkUnselectedAlpha(MyGallery gallery, float alpha) {
        final float DEFAULT_ALPHA = 1.0f;
        View v0 = gallery.getChildAt(0);
        View v1 = gallery.getChildAt(1);

        gallery.setUnselectedAlpha(alpha);
        Transformation t = new Transformation();
        gallery.getChildStaticTransformation(v0, t);
        // v0 is selected by default.
        assertEquals(DEFAULT_ALPHA, t.getAlpha(), DELTA);
        gallery.getChildStaticTransformation(v1, t);
        assertEquals(alpha, t.getAlpha(), DELTA);
    }

    @TestTargets({
        @TestTargetNew(
            level = TestLevel.COMPLETE,
            method = "generateLayoutParams",
            args = {android.util.AttributeSet.class}
        ),
        @TestTargetNew(
            level = TestLevel.COMPLETE,
            method = "generateLayoutParams",
            args = {android.view.ViewGroup.LayoutParams.class}
        )
    })
    public void testGenerateLayoutParams() throws XmlPullParserException, IOException {
        final int width = 320;
        final int height = 240;
        LayoutParams lp = new LayoutParams(width, height);
        MyGallery gallery = new MyGallery(mContext);
        LayoutParams layoutParams = gallery.generateLayoutParams(lp);
        assertEquals(width, layoutParams.width);
        assertEquals(height, layoutParams.height);

        XmlPullParser parser = getActivity().getResources().getXml(R.layout.gallery_test);
        WidgetTestUtils.beginDocument(parser, "LinearLayout");
        AttributeSet attrs = Xml.asAttributeSet(parser);
        mGallery = new Gallery(mContext, attrs);

        layoutParams = mGallery.generateLayoutParams(attrs);
        assertEquals(LayoutParams.MATCH_PARENT, layoutParams.width);
        assertEquals(LayoutParams.MATCH_PARENT, layoutParams.height);
    }

    @TestTargets({
        @TestTargetNew(
            level = TestLevel.NOT_NECESSARY,
            method = "onSingleTapUp",
            args = {MotionEvent.class}
        ),
        @TestTargetNew(
            level = TestLevel.NOT_NECESSARY,
            method = "onScroll",
            args = {MotionEvent.class, MotionEvent.class, float.class, float.class}
        ),
        @TestTargetNew(
            level = TestLevel.NOT_NECESSARY,
            method = "onLongPress",
            args = {android.view.MotionEvent.class}
        ),
        @TestTargetNew(
            level = TestLevel.NOT_NECESSARY,
            method = "onShowPress",
            args = {android.view.MotionEvent.class}
        ),
        @TestTargetNew(
            level = TestLevel.NOT_NECESSARY,
            method = "onKeyDown",
            args = {int.class, android.view.KeyEvent.class}
        ),
        @TestTargetNew(
            level = TestLevel.NOT_NECESSARY,
            method = "onKeyUp",
            args = {int.class, android.view.KeyEvent.class}
        ),
        @TestTargetNew(
            level = TestLevel.NOT_NECESSARY,
            method = "onFocusChanged",
            args = {boolean.class, int.class, android.graphics.Rect.class}
        ),
        @TestTargetNew(
            level = TestLevel.NOT_NECESSARY,
            method = "onLayout",
            args = {boolean.class, int.class, int.class, int.class, int.class}
        )
    })
    public void testFoo() {
        // Do not test these APIs. They are callbacks which:
        // 1. The callback machanism has been tested in super class
        // 2. The functionality is implmentation details, no need to test
    }

    @TestTargetNew(
        level = TestLevel.NOT_FEASIBLE,
        method = "showContextMenuForChild",
        args = {android.view.View.class}
    )
    public void testShowContextMenuForChild() {
        // how to check whether the context menu for child is showing.
    }

    @TestTargetNew(
        level = TestLevel.NOT_FEASIBLE,
        method = "showContextMenu",
        args = {}
    )
    public void testShowContextMenu() {
        // how to check whether the context menu is showing.
    }

    @TestTargetNew(
        level = TestLevel.COMPLETE,
        method = "dispatchKeyEvent",
        args = {android.view.KeyEvent.class}
    )
    public void testDispatchKeyEvent() {
        mGallery = new Gallery(mContext);
        final KeyEvent validKeyEvent = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER);
        assertTrue(mGallery.dispatchKeyEvent(validKeyEvent));
        final long time = SystemClock.uptimeMillis();
        final KeyEvent invalidKeyEvent
                = new KeyEvent(time, time, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_A, 5);
        assertFalse(mGallery.dispatchKeyEvent(invalidKeyEvent));
    }

    @TestTargetNew(
        level = TestLevel.COMPLETE,
        method = "setGravity",
        args = {int.class}
    )
    public void testSetGravity() throws Throwable {
        setGalleryGravity(Gravity.CENTER_HORIZONTAL);
        View v0 = mGallery.getChildAt(0);
        ViewAsserts.assertHorizontalCenterAligned(mGallery, v0);

        setGalleryGravity(Gravity.TOP);
        v0 = mGallery.getChildAt(0);
        ViewAsserts.assertTopAligned(mGallery, v0, mGallery.getPaddingTop());

        setGalleryGravity(Gravity.BOTTOM);
        v0 = mGallery.getChildAt(0);
        ViewAsserts.assertBottomAligned(mGallery, v0, mGallery.getPaddingBottom());
    }

    private void setGalleryGravity(final int gravity) throws Throwable {
        runTestOnUiThread(new Runnable() {
            public void run() {
                mGallery.setGravity(gravity);
                mGallery.invalidate();
                mGallery.requestLayout();
            }
        });
        mInstrumentation.waitForIdleSync();
    }

    @TestTargetNew(
        level = TestLevel.COMPLETE,
        method = "checkLayoutParams",
        args = {android.view.ViewGroup.LayoutParams.class}
    )
    @ToBeFixed(bug = "1695243", explanation = "Android API javadocs are incomplete")
    public void testCheckLayoutParams() {
        MyGallery gallery = new MyGallery(mContext);
        ViewGroup.LayoutParams p1 = new ViewGroup.LayoutParams(320, 480);
        assertFalse(gallery.checkLayoutParams(p1));

        Gallery.LayoutParams p2 = new Gallery.LayoutParams(320, 480);
        assertTrue(gallery.checkLayoutParams(p2));
    }

    @TestTargetNew(
        level = TestLevel.COMPLETE,
        method = "computeHorizontalScrollExtent",
        args = {}
    )
    public void testComputeHorizontalScrollExtent() {
        MyGallery gallery = new MyGallery(mContext);

        // only one item is considered to be selected.
        assertEquals(1, gallery.computeHorizontalScrollExtent());
    }

    @TestTargetNew(
        level = TestLevel.COMPLETE,
        method = "computeHorizontalScrollOffset",
        args = {}
    )
    public void testComputeHorizontalScrollOffset() {
        MyGallery gallery = new MyGallery(mContext);
        assertEquals(AdapterView.INVALID_POSITION, gallery.computeHorizontalScrollOffset());
        gallery.setAdapter(new ImageAdapter(mActivity));

        // Current scroll position is the same as the selected position
        assertEquals(gallery.getSelectedItemPosition(), gallery.computeHorizontalScrollOffset());
    }

    @TestTargetNew(
        level = TestLevel.COMPLETE,
        method = "computeHorizontalScrollRange",
        args = {}
    )
    public void testComputeHorizontalScrollRange() {
        MyGallery gallery = new MyGallery(mContext);
        ImageAdapter adapter = new ImageAdapter(mActivity);
        gallery.setAdapter(adapter);

        // Scroll range is the same as the item count
        assertEquals(adapter.getCount(), gallery.computeHorizontalScrollRange());
    }

    @TestTargetNew(
        level = TestLevel.COMPLETE,
        method = "dispatchSetPressed",
        args = {boolean.class}
    )
    @ToBeFixed(bug = "1695243", explanation = "Android API javadocs are not right, "
            + "dispatchSetPressed did not dispatch setPressed to "
            + "all of this View's children, but only the selected view")
    @UiThreadTest
    public void testDispatchSetPressed() {
        final MyGallery gallery = (MyGallery) getActivity().findViewById(R.id.gallery_test);

        gallery.setSelection(0);
        gallery.dispatchSetPressed(true);
        assertTrue(gallery.getSelectedView().isPressed());
        assertFalse(gallery.getChildAt(1).isPressed());

        gallery.dispatchSetPressed(false);
        assertFalse(gallery.getSelectedView().isPressed());
        assertFalse(gallery.getChildAt(1).isPressed());
    }

    @TestTargetNew(
        level = TestLevel.COMPLETE,
        method = "generateDefaultLayoutParams",
        args = {}
    )
    public void testGenerateDefaultLayoutParams() {
        MyGallery gallery = new MyGallery(mContext);
        ViewGroup.LayoutParams p = gallery.generateDefaultLayoutParams();
        assertNotNull(p);
        assertTrue(p instanceof Gallery.LayoutParams);
        assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, p.width);
        assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, p.height);
    }

    @TestTargetNew(
        level = TestLevel.COMPLETE,
        method = "getChildDrawingOrder",
        args = {int.class, int.class}
    )
    @ToBeFixed(bug = "1695243", explanation = "Android API javadocs are incomplete")
    public void testGetChildDrawingOrder() {
        final MyGallery gallery = (MyGallery) getActivity().findViewById(R.id.gallery_test);

        int childCount = 3;
        int index = 2;
        assertEquals(gallery.getSelectedItemPosition(),
                gallery.getChildDrawingOrder(childCount, index));

        childCount = 5;
        index = 2;
        assertEquals(index + 1, gallery.getChildDrawingOrder(childCount, index));

        childCount = 5;
        index = 3;
        assertEquals(index + 1, gallery.getChildDrawingOrder(childCount, index));
    }

    @TestTargetNew(
        level = TestLevel.COMPLETE,
        method = "getContextMenuInfo",
        args = {}
    )
    public void testGetContextMenuInfo() {
        MockOnCreateContextMenuListener listener = new MockOnCreateContextMenuListener();
        MyGallery gallery = new MyGallery(mContext);
        gallery.setOnCreateContextMenuListener(listener);
        assertFalse(listener.hasCreatedContextMenu());
        gallery.createContextMenu(new ContextMenuBuilder(mContext));
        assertTrue(listener.hasCreatedContextMenu());
        assertSame(gallery.getContextMenuInfo(), listener.getContextMenuInfo());
    }

    private static class MockOnCreateContextMenuListener implements OnCreateContextMenuListener {
        private boolean hasCreatedContextMenu;
        private ContextMenuInfo mContextMenuInfo;

        public boolean hasCreatedContextMenu() {
            return hasCreatedContextMenu;
        }

        public ContextMenuInfo getContextMenuInfo() {
            return mContextMenuInfo;
        }

        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
            hasCreatedContextMenu = true;
            mContextMenuInfo = menuInfo;
        }
    }

    private static class ImageAdapter extends BaseAdapter {
        public ImageAdapter(Context c) {
            mContext = c;
        }

        public int getCount() {
            return mImageIds.length;
        }

        public Object getItem(int position) {
            return position;
        }

        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            ImageView i = new ImageView(mContext);

            i.setImageResource(mImageIds[position]);
            i.setScaleType(ImageView.ScaleType.FIT_XY);
            i.setLayoutParams(new Gallery.LayoutParams(136, 88));

            return i;
        }

        private Context mContext;

        private Integer[] mImageIds = {
                R.drawable.faces,
                R.drawable.scenery,
                R.drawable.testimage,
                R.drawable.faces,
                R.drawable.scenery,
                R.drawable.testimage,
                R.drawable.faces,
                R.drawable.scenery,
                R.drawable.testimage,
        };
    }

    private static class MockOnItemSelectedListener implements OnItemSelectedListener {
        private boolean mIsItemSelected;
        private boolean mNothingSelected;
        private int mItemSelectedCalledCount;

        public boolean isItemSelected() {
            return mIsItemSelected;
        }

        public boolean hasNothingSelected() {
            return mNothingSelected;
        }

        public int getItemSelectedCalledCount() {
            return mItemSelectedCalledCount;
        }

        public void reset() {
            mIsItemSelected = false;
            mNothingSelected = true;
            mItemSelectedCalledCount = 0;
        }

        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            mIsItemSelected = true;
            mItemSelectedCalledCount++;
        }

        public void onNothingSelected(AdapterView<?> parent) {
            mNothingSelected = true;
        }
    }
}
