/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

package com.skia;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.opengl.EGL14;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.util.Log;
import android.view.MotionEvent;

public class SkiaSampleView extends GLSurfaceView {

    private final SkiaSampleRenderer mSampleRenderer;
    private boolean mRequestedOpenGLAPI; // true == use (desktop) OpenGL. false == use OpenGL ES.
    private int mRequestedMSAASampleCount;

    public SkiaSampleView(Context ctx, boolean useOpenGL, int msaaSampleCount) {
        super(ctx);

        mSampleRenderer = new SkiaSampleRenderer(this);
        mRequestedMSAASampleCount = msaaSampleCount;

        setEGLContextClientVersion(2);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            setEGLConfigChooser(8, 8, 8, 8, 0, 8);
        } else {
            mRequestedOpenGLAPI = useOpenGL;
            setEGLConfigChooser(new SampleViewEGLConfigChooser());
        }
        setRenderer(mSampleRenderer);
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int count = event.getPointerCount();
        for (int i = 0; i < count; i++) {
            final float x = event.getX(i);
            final float y = event.getY(i);
            final int owner = event.getPointerId(i);
            int action = event.getAction() & MotionEvent.ACTION_MASK;
            switch (action) {
            case MotionEvent.ACTION_POINTER_UP:
                action = MotionEvent.ACTION_UP;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                action = MotionEvent.ACTION_DOWN;
                break;
            default:
                break;
            }
            final int finalAction = action;
            queueEvent(new Runnable() {
                @Override
                public void run() {
                    mSampleRenderer.handleClick(owner, x, y, finalAction);
                }
            });
        }
        return true;
    }

    public void inval() {
        queueEvent(new Runnable() {
            @Override
            public void run() {
                mSampleRenderer.postInval();
            }
        });
    }

    public void terminate() {
        queueEvent(new Runnable() {
            @Override
            public void run() {
                mSampleRenderer.term();
            }
        });
    }

    public void showOverview() {
        queueEvent(new Runnable() {
            @Override
            public void run() {
                mSampleRenderer.showOverview();
            }
        });
    }

    public void nextSample() {
        queueEvent(new Runnable() {
            @Override
            public void run() {
                mSampleRenderer.nextSample();
            }
        });
    }

    public void previousSample() {
        queueEvent(new Runnable() {
            @Override
            public void run() {
                mSampleRenderer.previousSample();
            }
        });
    }

    public void goToSample(final int position) {
        queueEvent(new Runnable() {
            @Override
            public void run() {
                mSampleRenderer.goToSample(position);
            }
        });
    }

    public void toggleRenderingMode() {
        queueEvent(new Runnable() {
            @Override
            public void run() {
                mSampleRenderer.toggleRenderingMode();
            }
        });
    }

    public void toggleSlideshow() {
        queueEvent(new Runnable() {
            @Override
            public void run() {
                mSampleRenderer.toggleSlideshow();
            }
        });
    }

    public void toggleFPS() {
        queueEvent(new Runnable() {
            @Override
            public void run() {
                mSampleRenderer.toggleFPS();
            }
        });
    }

    public void toggleTiling() {
        queueEvent(new Runnable() {
            @Override
            public void run() {
                mSampleRenderer.toggleTiling();
            }
        });
    }

    public void toggleBBox() {
        queueEvent(new Runnable() {
            @Override
            public void run() {
                mSampleRenderer.toggleBBox();
            }
        });
    }
    
    public void saveToPDF() {
        queueEvent(new Runnable() {
            @Override
            public void run() {
                mSampleRenderer.saveToPDF();
            }
        });
    }

    public boolean getUsesOpenGLAPI() {
        return mRequestedOpenGLAPI;
    }

    public int getMSAASampleCount() {
        return mSampleRenderer.getMSAASampleCount();
    }

    private class SampleViewEGLConfigChooser implements GLSurfaceView.EGLConfigChooser {

        @Override
        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
            int numConfigs = 0;
            int[] configSpec = null;
            int[] value = new int[1];
            
            int[] validAPIs = new int[] {
                EGL14.EGL_OPENGL_API,
                EGL14.EGL_OPENGL_ES_API
            };
            int initialAPI = mRequestedOpenGLAPI ? 0 : 1;
            
            for (int i = initialAPI; i < validAPIs.length && numConfigs == 0; i++) {
                int currentAPI = validAPIs[i];
                EGL14.eglBindAPI(currentAPI);

                // setup the renderableType which will only be included in the
                // spec if we are attempting to get access to the OpenGL APIs.
                int renderableType = EGL14.EGL_OPENGL_BIT;
                if (currentAPI == EGL14.EGL_OPENGL_API) {
                    renderableType = EGL14.EGL_OPENGL_ES2_BIT;
                }

                if (mRequestedMSAASampleCount > 0) {
                    configSpec = new int[] {
                        EGL10.EGL_RED_SIZE, 8,
                        EGL10.EGL_GREEN_SIZE, 8,
                        EGL10.EGL_BLUE_SIZE, 8,
                        EGL10.EGL_ALPHA_SIZE, 8,
                        EGL10.EGL_DEPTH_SIZE, 0,
                        EGL10.EGL_STENCIL_SIZE, 8,
                        EGL10.EGL_SAMPLE_BUFFERS, 1,
                        EGL10.EGL_SAMPLES, mRequestedMSAASampleCount,
                        EGL10.EGL_RENDERABLE_TYPE, renderableType,
                        EGL10.EGL_NONE
                    };

                    // EGL_RENDERABLE_TYPE is only needed when attempting to use
                    // the OpenGL API (not ES) and causes many EGL drivers to fail
                    // with a BAD_ATTRIBUTE error.
                    if (!mRequestedOpenGLAPI) {
                      configSpec[16] = EGL10.EGL_NONE;
                      Log.i("Skia", "spec: " + configSpec);
                    }
                    
                    if (!egl.eglChooseConfig(display, configSpec, null, 0, value)) {
                        Log.i("Skia", "Could not get MSAA context count: " + mRequestedMSAASampleCount);
                    }

                    numConfigs = value[0];
                }

                if (numConfigs <= 0) {
                    // Try without multisampling.
                    configSpec = new int[] {
                        EGL10.EGL_RED_SIZE, 8,
                        EGL10.EGL_GREEN_SIZE, 8,
                        EGL10.EGL_BLUE_SIZE, 8,
                        EGL10.EGL_ALPHA_SIZE, 8,
                        EGL10.EGL_DEPTH_SIZE, 0,
                        EGL10.EGL_STENCIL_SIZE, 8,
                        EGL10.EGL_RENDERABLE_TYPE, renderableType,
                        EGL10.EGL_NONE
                    };
                    
                    // EGL_RENDERABLE_TYPE is only needed when attempting to use
                    // the OpenGL API (not ES) and causes many EGL drivers to fail
                    // with a BAD_ATTRIBUTE error.
                    if (!mRequestedOpenGLAPI) {
                      configSpec[12] = EGL10.EGL_NONE;
                      Log.i("Skia", "spec: " + configSpec);
                    }

                    if (!egl.eglChooseConfig(display, configSpec, null, 0, value)) {
                      Log.i("Skia", "Could not get non-MSAA context count");
                    }
                    numConfigs = value[0];
                }
            }

            if (numConfigs <= 0) {
                throw new IllegalArgumentException("No configs match configSpec");
            }

            // Get all matching configurations.
            EGLConfig[] configs = new EGLConfig[numConfigs];
            if (!egl.eglChooseConfig(display, configSpec, configs, numConfigs, value)) {
                throw new IllegalArgumentException("Could not get config data");
            }

            for (int i = 0; i < configs.length; ++i) {
                EGLConfig config = configs[i];
                if (findConfigAttrib(egl, display, config , EGL10.EGL_RED_SIZE, 0) == 8 &&
                        findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0) == 8 &&
                        findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0) == 8 &&
                        findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0) == 8 &&
                        findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0) == 8) {
                    return config;
                }
            }

            throw new IllegalArgumentException("Could not find suitable EGL config");
        }

        private int findConfigAttrib(EGL10 egl, EGLDisplay display,
                EGLConfig config, int attribute, int defaultValue) {
            int[] value = new int[1];
            if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
                return value[0];
            }
            return defaultValue;
        }

    }
}
