package com.sun.glass.ui.gtk;

import com.sun.glass.ui.Application;
import com.sun.glass.ui.CommonDialogs.ExtensionFilter;
import com.sun.glass.ui.Cursor;
import com.sun.glass.ui.Launchable;
import com.sun.glass.ui.Pen;
import com.sun.glass.ui.Pixels;
import com.sun.glass.ui.Robot;
import com.sun.glass.ui.Screen;
import com.sun.glass.ui.Size;
import com.sun.glass.ui.Timer;
import com.sun.glass.ui.View;
import com.sun.glass.ui.Window;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.List;
import java.util.Map;

/**
 *
 * @author martin
 */
public class GtkApplication extends Application{

    public static  int screen = -1;
    public static  long display = 0;
    public static  long visualID = 0;

    private void initDisplay() {
        Map ds = getDeviceDetails();
        if (ds != null) {
            Object value;
            value = ds.get("XDisplay");
            if (value != null) {
                display = ((Long) value).longValue();
            }
            value = ds.get("XVisualID");
            if (value != null) {
                visualID = ((Long) value).longValue();
            }
            value = ds.get("XScreenID");
            if (value != null) {
                screen = ((Integer) value).intValue();
            }
        }
    }

    private void init() {
        initDisplay();
        _init();
    }

    protected void runLoop(final String args[], final Launchable launchable) {
        boolean isEventThread = AccessController
            .doPrivileged(new PrivilegedAction<Boolean>() {
                public Boolean run() {
                    Boolean result = Boolean.getBoolean("javafx.embed.isEventThread");
                    return result == null ? false : result.booleanValue();
                }
            });

        if (isEventThread) {
            init();
            setEventThread(Thread.currentThread());
            launchable.finishLaunching(args);
            return;
        }

        final Thread toolkitThread =
                AccessController.doPrivileged(new PrivilegedAction<Thread>() {

            public Thread run() {
                Thread th = new Thread(new Runnable() {
                    public void run() {
                        init();
                        _runLoop(args, launchable);
                    }
                }, "GtkNativeMainLoopThread");

                return th;
            }
        });
        setEventThread(toolkitThread);
        toolkitThread.start();
    }
    
    @Override 
    protected void finishTerminating() {
        final Thread toolkitThread = getEventThread();
        if (toolkitThread != null) {
            _terminateLoop();
            setEventThread(null);
        }
        super.finishTerminating();
    }
    
    private native void _terminateLoop();
    
    private native void _init();
    
    protected native void _runLoop(String[] args, Launchable launchable);

    @Override
    protected native void _invokeAndWait(Runnable runnable);

    @Override
    protected void _invokeLater(Runnable runnable) {
        _postOnEventQueue(runnable);
    }

    @Override
    protected native void _postOnEventQueue(Runnable runnable);

    private Object eventLoopExitEnterPassValue;

    private native void enterNestedEventLoopImpl();

    private native void leaveNestedEventLoopImpl();

    @Override
    protected Object _enterNestedEventLoop() {
        enterNestedEventLoopImpl();
        final Object retValue = eventLoopExitEnterPassValue;
        eventLoopExitEnterPassValue = null;
        return retValue;
    }

    @Override
    protected void _leaveNestedEventLoop(Object retValue) {
        eventLoopExitEnterPassValue = retValue;
        leaveNestedEventLoopImpl();
    }

    @Override
    public Window createWindow(Window owner, Screen screen, int styleMask) {
        return new GtkWindow(owner, screen, styleMask);
    }

    @Override
    public Window createWindow(long parent) {
        return new GtkChildWindow(parent);
    }

    @Override
    public View createView(Pen pen) {
        return new GtkView(pen);
    }

    @Override
    public Cursor createCursor(int type) {
        return new GtkCursor(type);
    }

    @Override
    public Cursor createCursor(int x, int y, Pixels pixels) {
        return new GtkCursor(x, y, pixels);
    }

    @Override
    protected void staticCursor_setVisible(boolean visible) {
    }

    @Override
    protected Size staticCursor_getBestSize(int width, int height) {
        return GtkCursor._getBestSize(width, height);
    }

    @Override
    public Pixels createPixels(int width, int height, ByteBuffer data) {
        return new GtkPixels(width, height, data);
    }

    @Override
    public Pixels createPixels(int width, int height, IntBuffer data) {
        return new GtkPixels(width, height, data);
    }

    @Override
    protected int staticPixels_getNativeFormat() {
        return Pixels.Format.BYTE_BGRA_PRE; // TODO
    }

    @Override
    public Robot createRobot() {
        return new GtkRobot();
    }

    @Override
    protected Screen staticScreen_getDeepestScreen() {
        List<Screen> screens = staticScreen_getScreens();
        Screen deepest = screens.get(0);
        int nScreens = screens.size();
        for (int i = 1; i<nScreens; i++) {            
            if (deepest.getDepth() < screens.get(i).getDepth()) {
                deepest = screens.get(i);
            }
        }
        return deepest;
    }

    @Override
    protected Screen staticScreen_getMainScreen() {
        /*
         * can't use gdk_screen_get_primary_monitor is since ver. 2.20
         * primary monitor gtk places at index 0.
         */
        List<Screen> screens = staticScreen_getScreens();
        return screens.get(0);
    }

    @Override
    protected Screen staticScreen_getScreenForLocation(int x, int y) {
        List<Screen> screens = staticScreen_getScreens();
        for (Screen screen : screens) {
            if (x >= screen.getX() &&
                x <= screen.getWidth() + screen.getX() &&
                y >= screen.getY() &&
                y <= screen.getHeight() + screen.getY() ) {
                return screen;
            }                    
        }
        return null;
    }

    @Override
    protected Screen staticScreen_getScreenForPtr(long screenPtr) {
        List<Screen> screens = staticScreen_getScreens();
        for (Screen screen : screens) {
            if (screen.getNativeScreen() == screenPtr) return screen;
        }
        return null;
    }

    @Override
    protected native List<Screen> staticScreen_getScreens();

    @Override
    public Timer createTimer(Runnable runnable) {
        return new GtkTimer(runnable);
    }

    @Override
    protected native int staticTimer_getMinPeriod();

    @Override
    protected native int staticTimer_getMaxPeriod();

    @Override
    protected String[] staticCommonDialogs_showFileChooser(Window owner, String folder, String title, int type, boolean multipleMode, ExtensionFilter[] extensionFilters) {
        return GtkCommonDialogs.showFileChooser(owner, folder, title, type, multipleMode, extensionFilters);
    }

    @Override
    protected String staticCommonDialogs_showFolderChooser(Window owner, String folder, String title) {
        return GtkCommonDialogs.showFolderChooser(owner, folder, title);
    }

    @Override
    protected native long staticView_getMultiClickTime();

    @Override
    protected native int staticView_getMultiClickMaxX();

    @Override
    protected native int staticView_getMultiClickMaxY();

    @Override
    public native boolean supportsTransparentWindows();
    
}
