package jp.sourceforge.nicoro;

import android.app.Activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.SystemClock;
import android.test.InstrumentationTestCase;
import android.test.MoreAsserts;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import junit.framework.Assert;
import junit.framework.AssertionFailedError;

public class TestUtil {

    /**
     * LayoutParamsが等しいかchild viewを含めてチェックする
     * @param view1
     * @param view2
     */
    public static void checkLayoutParams(View view1, View view2) {
        if (view1 == null && view2 == null) {
            return;
        }
        Assert.assertNotNull(view1);
        Assert.assertNotNull(view2);
        Assert.assertEquals(view1.getClass(), view2.getClass());
        ViewGroup.LayoutParams params1 = view1.getLayoutParams();
        ViewGroup.LayoutParams params2 = view2.getLayoutParams();
        if (params1 == null) {
            Assert.assertNull(params2);
        } else {
            Assert.assertNotNull(params2);
            Assert.assertEquals(params1.getClass(), params2.getClass());
            Assert.assertEquals(params1.width, params2.width);
            Assert.assertEquals(params1.height, params2.height);
            if (params1 instanceof RelativeLayout.LayoutParams) {
                MoreAsserts.assertEquals(
                        ((RelativeLayout.LayoutParams) params1).getRules(),
                        ((RelativeLayout.LayoutParams) params2).getRules());
            }
            if (params1 instanceof LinearLayout.LayoutParams) {
                LinearLayout.LayoutParams lp1 =
                    (LinearLayout.LayoutParams) params1;
                LinearLayout.LayoutParams lp2 =
                    (LinearLayout.LayoutParams) params2;
                Assert.assertEquals(lp1.weight, lp2.weight);
                Assert.assertEquals(lp1.gravity, lp2.gravity);
            }
            if (params1 instanceof ViewGroup.MarginLayoutParams) {
                ViewGroup.MarginLayoutParams lp1 =
                    (ViewGroup.MarginLayoutParams) params1;
                ViewGroup.MarginLayoutParams lp2 =
                    (ViewGroup.MarginLayoutParams) params2;
                Assert.assertEquals(lp1.leftMargin, lp2.leftMargin);
                Assert.assertEquals(lp1.rightMargin, lp2.rightMargin);
                Assert.assertEquals(lp1.topMargin, lp2.topMargin);
                Assert.assertEquals(lp1.bottomMargin, lp2.bottomMargin);
            }
            // TODO 他のパターンは今のところないので省略
        }

        if (view1 instanceof ViewGroup) {
            ViewGroup vg1 = (ViewGroup) view1;
            ViewGroup vg2 = (ViewGroup) view2;
            int count1 = vg1.getChildCount();
            int count2 = vg2.getChildCount();
            Assert.assertEquals(count1, count2);
            for (int i = 0; i < count1; ++i) {
                checkLayoutParams(vg1.getChildAt(i), vg2.getChildAt(i));
            }
        }
    }

    public static void checkBitmap(Bitmap bitmap1, Bitmap bitmap2) {
        int width = bitmap2.getWidth();
        int height = bitmap2.getHeight();
        Assert.assertEquals(bitmap1.getWidth(), width);
        Assert.assertEquals(bitmap1.getHeight(), height);
        for (int x = 0; x < width; ++x) {
            for (int y = 0; y < height; ++y) {
                Assert.assertEquals(bitmap1.getPixel(x, y), bitmap2.getPixel(x, y));
            }
        }
    }

    public static void callActivityOnCreate(final InstrumentationTestCase itc,
            final Activity activity, final Bundle savedInstanceState) throws Throwable {
        itc.runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                itc.getInstrumentation().callActivityOnCreate(activity,
                        savedInstanceState);
            }
        });
    }
    public static void callActivityOnStart(final InstrumentationTestCase itc,
            final Activity activity) throws Throwable {
        itc.runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                itc.getInstrumentation().callActivityOnStart(activity);
            }
        });
    }
    public static void callActivityOnResume(final InstrumentationTestCase itc,
            final Activity activity) throws Throwable {
        itc.runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                itc.getInstrumentation().callActivityOnResume(activity);
            }
        });
    }
    public static void callActivityOnPause(final InstrumentationTestCase itc,
            final Activity activity) throws Throwable {
        itc.runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                itc.getInstrumentation().callActivityOnPause(activity);
            }
        });
    }
    public static void callActivityOnStop(final InstrumentationTestCase itc,
            final Activity activity) throws Throwable {
        itc.runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                itc.getInstrumentation().callActivityOnStop(activity);
            }
        });
    }
    public static void callActivityOnDestroy(final InstrumentationTestCase itc,
            final Activity activity) throws Throwable {
        itc.runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                itc.getInstrumentation().callActivityOnDestroy(activity);
            }
        });
    }
    public static void callActivityOnSaveInstanceState(final InstrumentationTestCase itc,
            final Activity activity, final Bundle savedInstanceState) throws Throwable {
        itc.runTestOnUiThread(new Runnable() {
            @Override
            public void run() {
                itc.getInstrumentation().callActivityOnSaveInstanceState(activity,
                        savedInstanceState);
            }
        });
    }

    public interface Until {
        boolean command();
        String errorMessage();
    }

    public static void assertUntil(Until until) {
        long timeover = SystemClock.elapsedRealtime() + 10000L;
        while (!until.command()) {
            if (SystemClock.elapsedRealtime() > timeover) {
                throw new AssertionFailedError(until.errorMessage());
            }
            SystemClock.sleep(100);
        }
    }
}
