package org.westmalle.wayland.html5.egl;

import com.google.auto.factory.AutoFactory;
import com.google.auto.factory.Provided;
import org.freedesktop.jaccall.Pointer;
import org.freedesktop.jaccall.Size;
import org.freedesktop.wayland.server.Display;
import org.freedesktop.wayland.server.EventLoop;
import org.westmalle.wayland.core.EglConnector;
import org.westmalle.wayland.core.OutputMode;
import org.westmalle.wayland.core.Renderer;
import org.westmalle.wayland.html5.Html5Connector;
import org.westmalle.wayland.nativ.libGLESv2.LibGLESv2;
import org.westmalle.wayland.protocol.WlOutput;

import javax.annotation.Nonnull;

import java.util.concurrent.atomic.AtomicBoolean;

import static org.freedesktop.jaccall.Pointer.malloc;
import static org.westmalle.wayland.nativ.libGLESv2.LibGLESv2.GL_RGBA;
import static org.westmalle.wayland.nativ.libGLESv2.LibGLESv2.GL_UNSIGNED_BYTE;

@AutoFactory(allowSubclasses = true,
             className = "Html5EglConnectorFactory")
public class Html5EglConnector implements EglConnector {

    @Nonnull
    private final Display        display;
    @Nonnull
    private final LibGLESv2      libGLESv2;
    private final Html5Connector html5Connector;
    private final EglConnector   eglConnector;

    private boolean rendererAvailable = true;
//    private final AtomicBoolean publishPending    = new AtomicBoolean(false);

    Html5EglConnector(@Nonnull @Provided final Display display,
                      @Provided @Nonnull final LibGLESv2 libGLESv2,
                      @Nonnull final Html5Connector html5Connector,
                      @Nonnull final EglConnector eglConnector) {
        this.display = display;
        this.libGLESv2 = libGLESv2;
        this.html5Connector = html5Connector;
        this.eglConnector = eglConnector;
    }

    @Override
    public long getEglSurface() {
        return this.eglConnector.getEglSurface();
    }

    @Override
    public long getEglContext() {
        return this.eglConnector.getEglContext();
    }

    @Override
    public long getEglDisplay() {
        return this.eglConnector.getEglDisplay();
    }

    @Override
    public void renderBegin() {
        this.eglConnector.renderBegin();
    }

    @Override
    public void renderEndBeforeSwap() {
        this.eglConnector.renderEndBeforeSwap();

        //TODO if all sockets from the connector have a render pending, then we don't perform a glreadpixels now, but
        //TODO delay it to the next idle cycle, else we risking performing multiple unused glreadpixels
//        if (this.html5Connector.getCommitBusy() &&
//            this.publishPending.compareAndSet(false,
//                                              true)) {
//            this.display.getEventLoop()
//                        .addIdle(() -> {
//                            this.publishPending.set(false);
        publishFrame();
//                        });
//        }
//        else {
//            publishFrame();
//        }
    }

    private void publishFrame() {
        final OutputMode mode = getWlOutput().getOutput()
                                             .getMode();
        final int width  = mode.getWidth();
        final int height = mode.getHeight();

        final Pointer<Byte> frameBuffer = malloc(width * height * Size.sizeof((Integer) null),
                                                 Byte.class);
        this.libGLESv2.glReadPixels(0,
                                    0,
                                    width,
                                    height,
                                    GL_RGBA,
                                    GL_UNSIGNED_BYTE,
                                    frameBuffer.address);

        this.html5Connector.commitFrame(frameBuffer,
                                        true,
                                        width,
                                        height);
    }

    @Override
    public void renderEndAfterSwap() {
        this.eglConnector.renderEndAfterSwap();
    }

    @Nonnull
    @Override
    public WlOutput getWlOutput() {
        return this.eglConnector.getWlOutput();
    }

    @Override
    public void accept(@Nonnull final Renderer renderer) {
        //TODO unit test 2 cases here: schedule idle, no-op when already scheduled
        whenIdle(() -> renderOn(renderer));
    }

    private void whenIdle(final EventLoop.IdleHandler idleHandler) {
        if (this.rendererAvailable) {
            this.rendererAvailable = false;
            this.display.getEventLoop()
                        .addIdle(idleHandler);
        }
    }

    private void renderOn(@Nonnull final Renderer renderer) {
        renderer.visit(this);
        this.display.flushClients();
        this.rendererAvailable = true;
    }
}

@@ -625,59 +625,8 @@
 l;%0A%0A
-import java.util.concurrent.atomic.AtomicBoolean;%0A%0A
 impo
@@ -1233,88 +1233,8 @@
 rue;
-%0A//    private final AtomicBoolean publishPending    = new AtomicBoolean(false);
 %0A%0A  
@@ -2192,248 +2192,180 @@
 ODO 
-if all sockets from the connector have a render pending, then we don't perform a glreadpixels now, but%0A        //TODO delay it to the next idle cycle, else we risking performing multiple unused glreadpixels%0A//        if (this.
+we can optimize by checking if all html5 clients are busy and don't do any work until at least one client is available again.%0A        //for this to work, the 
 html5
-C
+c
 onnector
 .get
@@ -2364,376 +2364,297 @@
 ctor
-.getCommitBusy() &&%0A//            this.publishPending.compareAndSet(false,%0A//                                              true)) %7B%0A//            this.display.getEventLoop()%0A//                        .addIdle(() -%3E %7B%0A//                            this.publishPending.set(false);%0A        publishFrame();%0A//                        %7D);%0A//        %7D%0A//        else %7B%0A//    
+ needs to trigger a publishFrame as soon as it sees one of it's clients is available again.%0A%0A        //TODO we should not publish a new frame if the html5 connector is still busy processing the previous frame. the html5 connector%0A        //should therefore signal us when it is finished.%0A%0A
     
@@ -2677,20 +2677,8 @@
 ();%0A
-//        %7D%0A
     
@@ -2710,24 +2710,25 @@
 shFrame() %7B%0A
+%0A
         fina

add TODO explaining how to throttle png encoding thread