/*
 * Copyright (C) 2007 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.scroll;

import android.widget.scroll.RequestRectangleVisible;
import com.android.frameworks.coretests.R;

import android.test.ActivityInstrumentationTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.ViewAsserts;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.TextView;
import android.view.View;
import android.view.KeyEvent;

/**
 * {@link RequestRectangleVisible} is set up to exercise the cases of moving a
 * rectangle that is either off screen or not entirely on the screen onto the screen.
 */
public class RequestRectangleVisibleTest extends ActivityInstrumentationTestCase<RequestRectangleVisible> {

    private ScrollView mScrollView;

    private Button mClickToScrollFromAbove;
    private Button mClickToScrollToUpperBlob;
    private TextView mTopBlob;

    private View mChildToScrollTo;

    private TextView mBottomBlob;
    private Button mClickToScrollToBlobLowerBlob;
    private Button mClickToScrollFromBelow;

    public RequestRectangleVisibleTest() {
        super("com.android.frameworks.coretests", RequestRectangleVisible.class);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();

        RequestRectangleVisible a = getActivity();

        mScrollView = (ScrollView) a.findViewById(R.id.scrollView);
        mClickToScrollFromAbove = (Button) a.findViewById(R.id.scrollToRectFromTop);
        mClickToScrollToUpperBlob = (Button) a.findViewById(R.id.scrollToRectFromTop2);
        mTopBlob = (TextView) a.findViewById(R.id.topBlob);
        mChildToScrollTo = a.findViewById(R.id.childToMakeVisible);
        mBottomBlob = (TextView) a.findViewById(R.id.bottomBlob);
        mClickToScrollToBlobLowerBlob = (Button) a.findViewById(R.id.scrollToRectFromBottom2);
        mClickToScrollFromBelow = (Button) a.findViewById(R.id.scrollToRectFromBottom);

        
    }

    
    @MediumTest
    public void testPreconditions() {
        assertNotNull(mScrollView);
        assertNotNull(mClickToScrollFromAbove);
        assertNotNull(mClickToScrollToUpperBlob);
        assertNotNull(mTopBlob);
        assertNotNull(mChildToScrollTo);
        assertNotNull(mBottomBlob);
        assertNotNull(mClickToScrollToBlobLowerBlob);
        assertNotNull(mClickToScrollFromBelow);

        assertTrue("top blob needs to be taller than the screen for many of the "
                + "tests below to work.",
                mTopBlob.getHeight() > mScrollView.getHeight());

        assertTrue("bottom blob needs to be taller than the screen for many of the "
                + "tests below to work.",
                mBottomBlob.getHeight() > mScrollView.getHeight());

        assertTrue("top blob needs to be lower than the fading edge region",
                mTopBlob.getTop() > mScrollView.getVerticalFadingEdgeLength());
    }

    @MediumTest
    public void testScrollToOffScreenRectangleFromTop() {
        // view is off screen
        assertTrue(mClickToScrollFromAbove.hasFocus());
        ViewAsserts.assertOffScreenBelow(mScrollView, mChildToScrollTo);

        // click
        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
        getInstrumentation().waitForIdleSync();  // wait for scrolling to finish

        // should be on screen, positioned at the bottom (with room for
        // fading edge)
        ViewAsserts.assertOnScreen(mScrollView, mChildToScrollTo);
        ViewAsserts.assertHasScreenCoordinates(
                mScrollView, mChildToScrollTo,
                0,
                mScrollView.getHeight()
                        - mChildToScrollTo.getHeight()
                        - mScrollView.getVerticalFadingEdgeLength());
    }

    @MediumTest
    public void testScrollToPartiallyOffScreenRectFromTop() {
        pressDownUntilViewInFocus(mClickToScrollToUpperBlob, 4);

        // make sure the blob is indeed partially on screen below
        assertOnBottomEdgeOfScreen(mScrollView, mTopBlob);

        // click
        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
        getInstrumentation().waitForIdleSync();  // wait for scrolling to finish

        // blob should have moved so top of it is at top of screen (with
        // room for the vertical fading edge
        ViewAsserts.assertHasScreenCoordinates(
                mScrollView, mTopBlob, 0, mScrollView.getVerticalFadingEdgeLength());
    }

    @LargeTest
    public void testScrollToOffScreenRectangleFromBottom() {
        // go to bottom button
        pressDownUntilViewInFocus(mClickToScrollFromBelow, 10);

        // view is off screen above
        assertTrue(mClickToScrollFromBelow.hasFocus());
        ViewAsserts.assertOffScreenAbove(mScrollView, mChildToScrollTo);

        // click
        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
        getInstrumentation().waitForIdleSync();  // wait for scrolling to finish

        // on screen, positioned at top (with room for fading edge)
        ViewAsserts.assertOnScreen(mScrollView, mChildToScrollTo);
        ViewAsserts.assertHasScreenCoordinates(
                mScrollView, mChildToScrollTo, 0, mScrollView.getVerticalFadingEdgeLength());
    }


    @LargeTest
    public void testScrollToPartiallyOffScreenRectFromBottom() {
        pressDownUntilViewInFocus(mClickToScrollToBlobLowerBlob, 10);

        // make sure the blob is indeed partially on screen above
        assertOnTopEdgeOfScreen(mScrollView, mBottomBlob);

        // click
        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
        getInstrumentation().waitForIdleSync();  // wait for scrolling to finish

        // blob should have moved so bottom of it is at bottom of screen
        // with room for vertical fading edge
        ViewAsserts.assertHasScreenCoordinates(
                mScrollView, mBottomBlob,
                0,
                mScrollView.getHeight() - mBottomBlob.getHeight()
                    - mScrollView.getVerticalFadingEdgeLength());
    }


    /**
     * Press the down key until a particular view is in focus
     * @param view The view to get in focus.
     * @param maxKeyPress The maximum times to press down before failing.
     */
    private void pressDownUntilViewInFocus(View view, int maxKeyPress) {
        int count = 0;
        while(!view.hasFocus()) {
            sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
            getInstrumentation().waitForIdleSync();

            // just in case...
            if (++count > maxKeyPress) {
                fail("couldn't move down to bottom button within "
                        + maxKeyPress + " key presses.");
            }
        }
    }

    /**
     * Assert that view overlaps the bottom edge of the screen
     * @param origin The root view of the screen.
     * @param view The view
     */
    static public void assertOnBottomEdgeOfScreen(View origin, View view) {
        int[] xy = new int[2];
        view.getLocationOnScreen(xy);

        int[] xyRoot = new int[2];
        origin.getLocationOnScreen(xyRoot);

        int bottom = xy[1] + view.getHeight();
        int bottomOfRoot = xyRoot[1] + origin.getHeight();

        assertTrue(bottom > bottomOfRoot);

        assertTrue(xy[1] < bottomOfRoot);
        assertTrue(bottom > bottomOfRoot);
    }

    /**
     * Assert that view overlaps the bottom edge of the screen
     * @param origin The root view of the screen.
     * @param view The view
     */
    static public void assertOnTopEdgeOfScreen(View origin, View view) {
        int[] xy = new int[2];
        view.getLocationOnScreen(xy);

        int[] xyRoot = new int[2];
        origin.getLocationOnScreen(xyRoot);

        int bottom = xy[1] + view.getHeight();
        int bottomOfRoot = xyRoot[1] + origin.getHeight();

        assertTrue(bottom < bottomOfRoot);
        assertTrue(bottom > xyRoot[1]);

        assertTrue(xy[1] < xyRoot[1]);
    }


}
