/**
 * Copyright 2012-2024 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 jogamp.opengl.util.av.impl;

import java.io.IOException;
import java.io.PrintStream;

import com.jogamp.common.av.PTS;
import com.jogamp.common.os.Clock;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GLException;

import com.jogamp.opengl.egl.EGL;
import com.jogamp.opengl.util.av.GLMediaPlayer.State;
import com.jogamp.opengl.util.texture.Texture;
import com.jogamp.opengl.util.texture.TextureSequence;

import jogamp.opengl.util.av.EGLMediaPlayerImpl;

/**
 * OpenMAX IL implementation. This implementation is currently not tested
 * due to lack of an available device or working <i>software</i> implementation.
 * It is kept alive through all changes in the hope of a later availability though.
 */
public class OMXGLMediaPlayer extends EGLMediaPlayerImpl {
    static final boolean available;

    static {
        available = false;
        /** FIXME!
        // OMX binding is included in jogl_desktop and jogl_mobile
        GLProfile.initSingleton();
        available = initIDs0(); */
    }

    public static final boolean isAvailable() { return available; }

    protected long moviePtr = 0;

    public OMXGLMediaPlayer() {
        super(TextureType.KHRImage, true);
        if(!available) {
            throw new RuntimeException("OMXGLMediaPlayer not available");
        }
        initOMX();
    }

    protected void initOMX() {
        moviePtr = _createInstance();
        if(0==moviePtr) {
            throw new GLException("Couldn't create OMXInstance");
        }
    }

    @Override
    public final void printNativeInfo(final PrintStream out) {
        out.println("NullGLMediaPlayer: available "+available);
    }

    @Override
    protected TextureSequence.TextureFrame createTexImage(final GL gl, final int texName) {
        final EGLTextureFrame eglTex = (EGLTextureFrame) super.createTexImage(gl, texName);
        _setStreamEGLImageTexture2D(moviePtr, texName, eglTex.getImage(), eglTex.getSync());
        return eglTex;
    }

    @Override
    protected void destroyTexFrame(final GL gl, final TextureSequence.TextureFrame imgTex) {
        super.destroyTexFrame(gl, imgTex);
    }

    @Override
    protected void destroyImpl() {
        if (moviePtr != 0) {
            _stop(moviePtr);
            _detachVideoRenderer(moviePtr);
            _destroyInstance(moviePtr);
            moviePtr = 0;
        }
    }

    @Override
    protected void initStreamImpl(final int vid, String alang, final int aid, String slang, final int sid) throws IOException {
        if(0==moviePtr) {
            throw new GLException("OMX native instance null");
        }
        if( !getUri().isFileScheme() ) {
            throw new IOException("Only file schemes are allowed: "+getUri());
        }
        final String path=getUri().path.decode();
        if(DEBUG) {
            System.out.println("initGLStream: clean path "+path);
        }

        if(DEBUG) {
            System.out.println("initGLStream: p1 "+this);
        }
        _setStream(moviePtr, getTextureCount(), path);
        if(DEBUG) {
            System.out.println("initGLStream: p2 "+this);
        }
    }
    @Override
    protected final void initGLImpl(final GL gl) throws IOException, GLException {
        // NOP
        setIsGLOriented(true);
    }

    @Override
    protected PTS getAudioPTSImpl() { return audio_pts; }
    @Override
    protected PTS getUpdatedAudioPTS() {
        if( 0 != moviePtr ) {
            audio_pts.set(Clock.currentMillis(), _getCurrentPosition(moviePtr));
        } else {
            audio_pts.set(Clock.currentMillis(), 0);
        }
        return audio_pts;
    }
    @Override
    protected int getAudioQueuedDuration() { return 0; }
    @Override
    protected int getLastBufferedAudioPTS() { return audio_pts.getLast(); }
    private final PTS audio_pts = new PTS( () -> { return State.Playing == getState() ? getPlaySpeed() : 0f; } );

    @Override
    protected boolean setPlaySpeedImpl(final float rate) {
        if(0==moviePtr) {
            throw new GLException("OMX native instance null");
        }
        _setPlaySpeed(moviePtr, rate);
        return true;
    }

    @Override
    public synchronized boolean resumeImpl() {
        if(0==moviePtr) {
            return false;
        }
        _play(moviePtr);
        return true;
    }

    /** @return time position after issuing the command */
    @Override
    public synchronized boolean pauseImpl() {
        if(0==moviePtr) {
            return false;
        }
        _pause(moviePtr);
        return true;
    }

    @Override
    protected final void stopImpl() {
        destroyImpl();
        initOMX();
    }

    /** @return time position after issuing the command */
    @Override
    protected int seekImpl(final int msec) {
        if(0==moviePtr) {
            throw new GLException("OMX native instance null");
        }
        return _seek(moviePtr, msec);
    }

    @Override
    protected int getNextTextureImpl(final GL gl, final TextureFrame vFrame, final Texture sTex, final boolean[] sTexUsed) {
        if(0==moviePtr) {
            throw new GLException("OMX native instance null");
        }
        final int nextTex = _getNextTextureID(moviePtr, true);
        if(0 < nextTex) {
            // FIXME set pts !
            /* FIXME
            final TextureSequence.TextureFrame eglImgTex =
                    texFrameMap.get(new Integer(_getNextTextureID(moviePtr, blocking)));
            if(null!=eglImgTex) {
                lastTex = eglImgTex;
            } */
        }
        return 0; // FIXME: return pts
    }

    private String replaceAll(final String orig, final String search, final String repl) {
        final StringBuilder dest = new StringBuilder();
        // In case replaceAll / java.util.regex.* is not supported (-> CVM)
        int i=0,j;
        while((j=orig.indexOf(search, i))>=0) {
            dest.append(orig.substring(i, j));
            dest.append(repl);
            i=j+1;
        }
        return dest.append(orig.substring(i, orig.length())).toString();
    }

    private void errorCheckEGL(final String s) {
        int e;
        if( (e=EGL.eglGetError()) != EGL.EGL_SUCCESS ) {
            System.out.println("EGL Error: ("+s+"): 0x"+Integer.toHexString(e));
        }
    }

    private static native boolean initIDs0();
    private native long _createInstance();
    private native void _destroyInstance(long moviePtr);

    private native void _detachVideoRenderer(long moviePtr); // stop before
    private native void _attachVideoRenderer(long moviePtr); // detach before
    private native void _setStream(long moviePtr, int textureNum, String path);
    private native void _activateStream(long moviePtr);

    private native void _setStreamEGLImageTexture2D(long moviePtr, int tex, long image, long sync);
    private native int  _seek(long moviePtr, int position);
    private native void _setPlaySpeed(long moviePtr, float rate);
    private native void _play(long moviePtr);
    private native void _pause(long moviePtr);
    private native void _stop(long moviePtr);
    private native int  _getNextTextureID(long moviePtr, boolean blocking);
    private native int  _getCurrentPosition(long moviePtr);
}

