/**
 * Copyright 2011-2023 JogAmp Community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of JogAmp Community.
 */
package com.jogamp.opengl.test.junit.graph;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Locale;

import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2ES2;
import com.jogamp.opengl.GLAnimatorControl;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLCapabilitiesImmutable;
import com.jogamp.opengl.GLException;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.GLRunnable;

import org.junit.Assert;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;

import com.jogamp.common.os.Platform;
import com.jogamp.graph.curve.Region;
import com.jogamp.graph.curve.opengl.GLRegion;
import com.jogamp.graph.curve.opengl.RegionRenderer;
import com.jogamp.graph.font.Font;
import com.jogamp.graph.font.FontFactory;
import com.jogamp.graph.font.FontScale;
import com.jogamp.junit.util.JunitTracer;
import com.jogamp.newt.Window;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.test.junit.util.MiscUtils;
import com.jogamp.opengl.test.junit.util.UITestCase;
import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.GLReadBufferUtil;


/**
 * TestTextRendererNEWT10 Variant
 * - Using listener derived from rudimentary TextRendererGLELBase, w/o much functionality but scaling ..
 * - Type Rendering via TextRegionUtil, multiple
 */
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestTextRendererNEWT10 extends UITestCase {
    static final boolean DEBUG = false;
    static final boolean TRACE = false;
    static long Duration = 2000; // ms
    static int win_width = 1024;
    static int win_height = 640;
    static boolean WaitStartEnd = false;
    static boolean TextAnim = false;
    static int SceneMSAASamples = 0;
    static int GraphVBAASamples = 0;
    static int GraphMSAASamples = 0;
    static boolean ManualTest = false;
    static int SwapInterval = 1;

    static String fontFileName = null;
    static URL fontURL = null;
    static int fontSet = 0;
    static int fontFamily = 0;
    static int fontStylebits = 0;
    static float fontSizeFixed = 14f;

    static int atoi(final String a) {
        try {
            return Integer.parseInt(a);
        } catch (final Exception ex) { throw new RuntimeException(ex); }
    }

    public static void main(final String args[]) throws IOException {
        ManualTest = args.length > 0;
        for(int i=0; i<args.length; i++) {
            if(args[i].equals("-time")) {
                i++;
                Duration = atoi(args[i]);
            } else if(args[i].equals("-width")) {
                i++;
                win_width = atoi(args[i]);
            } else if(args[i].equals("-height")) {
                i++;
                win_height = atoi(args[i]);
            } else if(args[i].equals("-fontURL")) {
                i++;
                fontURL = new URL(args[i]);
            } else if(args[i].equals("-fontFile")) {
                i++;
                fontFileName = args[i];
            } else if(args[i].equals("-fontSet")) {
                i++;
                fontSet = atoi(args[i]);
            } else if(args[i].equals("-fontFamily")) {
                i++;
                fontFamily = atoi(args[i]);
            } else if(args[i].equals("-fontStyle")) {
                i++;
                fontStylebits = atoi(args[i]);
            } else if(args[i].equals("-fontSize")) {
                i++;
                fontSizeFixed = atoi(args[i]);
            } else if(args[i].equals("-smsaa")) {
                i++;
                SceneMSAASamples = atoi(args[i]);
            } else if(args[i].equals("-gmsaa")) {
                i++;
                GraphMSAASamples = atoi(args[i]);
            } else if(args[i].equals("-gvbaa")) {
                i++;
                GraphVBAASamples = atoi(args[i]);
            } else if(args[i].equals("-textAnim")) {
                TextAnim = true;
            } else if(args[i].equals("-vsync")) {
                i++;
                SwapInterval = MiscUtils.atoi(args[i], SwapInterval);
            } else if(args[i].equals("-wait")) {
                WaitStartEnd = true;
            }
        }
        System.err.println("Font [set "+fontSet+", family "+fontFamily+", style "+fontStylebits+", size "+fontSizeFixed+"], fontFileName "+fontFileName);
        System.err.println("Scene MSAA Samples "+SceneMSAASamples);
        System.err.println("Graph MSAA Samples "+GraphMSAASamples);
        System.err.println("Graph VBAA Samples "+GraphVBAASamples);
        System.err.println("swapInterval "+SwapInterval);
        final String tstname = TestTextRendererNEWT10.class.getName();
        org.junit.runner.JUnitCore.main(tstname);
    }

    static void sleep() {
        sleep(Duration);
    }
    static void sleep(final long d) {
        try {
            System.err.println("** new frame ** (sleep: "+d+"ms)");
            Thread.sleep(d);
        } catch (final InterruptedException ie) {}
    }

    static void destroyWindow(final GLWindow window) {
        if(null!=window) {
            window.destroy();
        }
    }

    static GLWindow createWindow(final String title, final GLCapabilitiesImmutable caps, final int width, final int height) {
        Assert.assertNotNull(caps);

        final GLWindow window = GLWindow.create(caps);
        window.setSize(width, height);
        window.setPosition(10, 10);
        window.setTitle(title);
        Assert.assertNotNull(window);
        window.setVisible(true);

        return window;
    }

    @Test
    public void test00Manual() throws InterruptedException {
        if( ManualTest ) {
            testImpl(SceneMSAASamples, GraphMSAASamples, GraphVBAASamples);
        }
    }
    @Test
    public void test00SceneNoAA() throws InterruptedException {
        if( !ManualTest ) {
            testImpl(0, 0, 0);
        }
    }
    @Test
    public void test01SceneMSAA04() throws InterruptedException {
        if( !ManualTest ) {
            testImpl(4, 0, 0);
        }
    }
    @Test
    public void test02GraphMSAA04() throws InterruptedException {
        if( !ManualTest ) {
            testImpl(0, 4, 0);
        }
    }
    @Test
    public void test03GraphVBAA04() throws InterruptedException {
        if( !ManualTest ) {
            testImpl(0, 0, 4);
        }
    }

    public void testImpl(final int sceneMSAASamples, final int graphMSAASamples, final int graphVBAASamples) throws InterruptedException {
        final GLProfile glp = GLProfile.get(GLProfile.GL2ES2);
        final GLCapabilities caps = new GLCapabilities(glp);
        caps.setAlphaBits(4);
        if( 0 < sceneMSAASamples ) {
            caps.setSampleBuffers(true);
            caps.setNumSamples(sceneMSAASamples);
        }
        System.err.println("Requested: "+caps+", graph[msaaSamples "+graphMSAASamples+", vbaaSamples "+graphVBAASamples+"]");

        final GLWindow window = createWindow("text-gvbaa"+graphVBAASamples+"-gmsaa"+graphMSAASamples+"-smsaa"+sceneMSAASamples, caps, win_width, win_height);

        window.display();
        System.err.println("Chosen: "+window.getChosenGLCapabilities());
        if( WaitStartEnd ) {
            JunitTracer.waitForKey("Start");
        }

        final int renderModes, sampleCount;
        if( graphVBAASamples > 0 ) {
            renderModes = Region.VBAA_RENDERING_BIT;
            sampleCount = graphVBAASamples;
        } else if ( graphMSAASamples > 0 ) {
            renderModes = Region.MSAA_RENDERING_BIT;
            sampleCount = graphMSAASamples;
        } else {
            renderModes = 0;
            sampleCount = 0;
        }
        final TextRendererGLEL textGLListener = new TextRendererGLEL(glp, renderModes, sampleCount);
        System.err.println(textGLListener.getFontInfo());

        window.addGLEventListener(textGLListener);

        final Animator anim = new Animator(0 /* w/o AWT */);
        anim.add(window);
        anim.start();
        anim.setUpdateFPSFrames(60, null);
        sleep();
        window.invoke(true, new GLRunnable() {
            @Override
            public boolean run(final GLAutoDrawable drawable) {
                try {
                    textGLListener.printScreen(renderModes, drawable, "./", "TestTextRendererNEWT00-snap"+screenshot_num, false);
                    screenshot_num++;
                } catch (final Exception e) {
                    e.printStackTrace();
                }
                return true;
            }
        });
        anim.stop();
        if( WaitStartEnd ) {
            JunitTracer.waitForKey("Stop");
        }
        destroyWindow(window);
    }
    int screenshot_num = 0;

    static final String textX2 =
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nec sapien tellus. \n"+
        "Ut purus odio, rhoncus sit amet commodo eget, ullamcorper vel urna. Mauris ultricies \n"+
        "quam iaculis urna cursus ornare. Nullam ut felis a ante ultrices ultricies nec a elit. \n"+
        "In hac habitasse platea dictumst. Vivamus et mi a quam lacinia pharetra at venenatis est.\n"+
        "Morbi quis bibendum nibh. Donec lectus orci, sagittis in consequat nec, volutpat nec nisi.\n"+
        "Donec ut dolor et nulla tristique varius. In nulla magna, fermentum id tempus quis, semper \n"+
        "in lorem. Maecenas in ipsum ac justo scelerisque sollicitudin. Quisque sit amet neque lorem,\n" +
        "-------Press H to change text---------\n";

    private static final class TextRendererGLEL extends TextRendererGLELBase {
        private final GLReadBufferUtil screenshot;
        private final GLRegion regionFPS, regionFPSAnim;
        final Font font;
        final float fontSizeMin, fontSizeMax;
        private long t0;
        float fontSizeAnim, fontSizeDelta;
        float dpiV, ppmmV;

        TextRendererGLEL(final GLProfile glp, final int renderModes, final int sampleCount) {
            super(renderModes, new int[] { sampleCount });
            setRendererCallbacks(RegionRenderer.defaultBlendEnable, RegionRenderer.defaultBlendDisable);

            regionFPS = GLRegion.create(glp, renderModes, null, 0, 0);
            regionFPSAnim = GLRegion.create(glp, renderModes, null, 0, 0);
            if( null != fontURL ) {
                Font _font = null;
                try {
                    _font = FontFactory.get(fontURL.openStream(), true);
                } catch (final IOException e) {
                    e.printStackTrace();
                }
                font = _font;
            } else if( null != fontFileName ) {
                Font _font = null;
                try {
                    _font = FontFactory.get(getClass(), fontFileName, false);
                } catch (final IOException e) {
                    e.printStackTrace();
                }
                font = _font;
            } else {
                font = getFont(fontSet, fontFamily, fontStylebits);
            }

            staticRGBAColor[0] = 0.1f;
            staticRGBAColor[1] = 0.1f;
            staticRGBAColor[2] = 0.1f;
            staticRGBAColor[3] = 1.0f;

            this.screenshot = new GLReadBufferUtil(false, false);
            // fontSizeMin = Math.max(8, fontSizeFixed-5);
            fontSizeMin = fontSizeFixed;
            fontSizeMax = fontSizeFixed+8;
            fontSizeAnim = fontSizeFixed;
            fontSizeDelta = 0.01f;
        }

        @Override
        public void init(final GLAutoDrawable drawable) {
            super.init(drawable);
            final GL2ES2 gl = drawable.getGL().getGL2ES2();
            gl.setSwapInterval(SwapInterval);
            gl.glEnable(GL.GL_DEPTH_TEST);
            t0 = Platform.currentTimeMillis();

            final Window win = (Window)drawable.getUpstreamWidget();
            final float[] pixelsPerMM = win.getPixelsPerMM(new float[2]);
            final float[] dotsPerInch = FontScale.ppmmToPPI(pixelsPerMM, new float[2]);
            dpiV = dotsPerInch[1];
            ppmmV = pixelsPerMM[1];
            System.err.println(getFontInfo());
            System.err.println("fontSize "+fontSizeFixed+", dotsPerMM "+pixelsPerMM[0]+"x"+pixelsPerMM[1]+", dpi "+dotsPerInch[0]+"x"+dotsPerInch[1]+", pixelSize "+FontScale.toPixels(fontSizeFixed, dotsPerInch[1] /* dpi display */));
        }

        @Override
        public void dispose(final GLAutoDrawable drawable) {
            final GL2ES2 gl = drawable.getGL().getGL2ES2();
            screenshot.dispose(gl);
            regionFPS.destroy(gl);
            regionFPSAnim.destroy(gl);
            super.dispose(drawable);
        }

        public void printScreen(final int renderModes, final GLAutoDrawable drawable, final String dir, final String objName, final boolean exportAlpha) throws GLException, IOException {
            final String modeS = Region.getRenderModeString(renderModes);
            final String bname = String.format((Locale)null, "%s-msaa%02d-fontsz%02.1f-%03dx%03d-%s%04d", objName,
                    drawable.getChosenGLCapabilities().getNumSamples(),
                    fontSizeFixed, drawable.getSurfaceWidth(), drawable.getSurfaceHeight(), modeS, vbaaSampleCount[0]);
            final String filename = dir + bname +".png";
            if(screenshot.readPixels(drawable.getGL(), false)) {
                screenshot.write(new File(filename));
            }
        }

        String getFontInfo() {
            final float pixelSize = FontScale.toPixels(fontSizeFixed, dpiV);
            final float mmSize = pixelSize / ppmmV;
            final int unitsPerEM = font.getMetrics().getUnitsPerEM();
            return String.format("Resolution dpiV %.2f, %.2f px/mm%nFont %s, unitsPerEM %d, size %.2f pt %.2f px %2f mm%n",
                    dpiV, ppmmV,
                    font.getFullFamilyName(),unitsPerEM, fontSizeFixed, pixelSize, mmSize);
        }

        @Override
        public void display(final GLAutoDrawable drawable) {
            final GL2ES2 gl = drawable.getGL().getGL2ES2();

            gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
            gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

            final GLAnimatorControl anim = drawable.getAnimator();
            final float lfps = null != anim ? anim.getLastFPS() : 0f;
            final float tfps = null != anim ? anim.getTotalFPS() : 0f;

            // Note: MODELVIEW is from [ 0 .. height ]

            final long t1 = Platform.currentTimeMillis();

            // final float fontSize = TestTextRendererNEWT00.fontSize;

            fontSizeAnim += fontSizeDelta;
            if( fontSizeMin >= fontSizeAnim || fontSizeAnim >= fontSizeMax ) {
                fontSizeDelta *= -1f;
            }

            final float pixelSize = FontScale.toPixels(fontSizeFixed, dpiV);
            final float pixelSizeAnim = FontScale.toPixels(fontSizeAnim, dpiV);

            final String modeS = Region.getRenderModeString(renderModes);

            if( false ) {
                // renderString(drawable, font, pixelSize, "I - / H P 7 0", 0, 0, 0, 0, -1000f, true);
                // renderString(drawable, font, pixelSize, "A M > } ] ", 0, 0, 0, 0, -1000f, true);
                // renderString(drawable, font, pixelSize, "M", 0, 0, 0, 0, -1000f, true);
                // renderString(drawable, font, pixelSize, "0 6 9 a b O Q A M > } ] ", 0, 0, 0, 0, -1000f, true);
                // renderString(drawable, font, pixelSize, "012345678901234567890123456789", 0, 0, 0, -1000, true);
                // renderString(drawable, font, pixelSize, textX2,        0, 0,   0, 0, -1000f, true);
                // renderString(drawable, font, pixelSize, text1,         0,    0, 0, -1000f, regionFPS); // no-cache
                final String text1 = lfps+" / "+tfps+" fps, vsync "+gl.getSwapInterval()+", elapsed "+(t1-t0)/1000.0+
                                     " s, fontSize "+fontSizeFixed+", msaa "+drawable.getChosenGLCapabilities().getNumSamples()+
                                     ", "+modeS+"-samples "+vbaaSampleCount[0];
                renderString(drawable, font, pixelSize, text1,              0, 0, 0, 0, -1000, regionFPS.clear(gl)); // no-cache
            } else {
                final String text1 = String.format("%03.1f/%03.1f fps, vsync %d, elapsed %4.1f s, fontSize %2.2f, msaa %d, %s-samples %d",
                        lfps, tfps, gl.getSwapInterval(), (t1-t0)/1000.0, fontSizeFixed,
                        drawable.getChosenGLCapabilities().getNumSamples(), modeS, vbaaSampleCount[0]);
                renderString(drawable, font, pixelSize, getFontInfo(),                    0, 0, 0, 0, -1000, true);
                renderString(drawable, font, pixelSize, "012345678901234567890123456789", 0, 0, 0, -1000, true);
                renderString(drawable, font, pixelSize, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 0, 0, 0, -1000, true);
                renderString(drawable, font, pixelSize, "Hello World",                    0, 0, 0, -1000, true);
                renderString(drawable, font, pixelSize, "4567890123456",                  4, 0, 0, -1000, true);
                renderString(drawable, font, pixelSize, "I like JogAmp",                  4, 0, 0, -1000, true);
                renderString(drawable, font, pixelSize, "Hello World",                    0, 0, 0, -1000, true);
                renderString(drawable, font, pixelSize, textX2,                           0, 0, 0, -1000, true);
                renderString(drawable, font, pixelSize, text1,                            0, 0, 0, -1000, regionFPS.clear(gl)); // no-cache
                if( TextAnim ) {
                    renderString(drawable, font, pixelSizeAnim, text1,                   0, 0, 0, -1000, regionFPSAnim.clear(gl)); // no-cache
                }
            }
        } };

}
