package org.westmalle.wayland.core;

import com.google.auto.factory.AutoFactory;
import com.google.auto.factory.Provided;
import com.google.common.eventbus.EventBus;

import org.freedesktop.wayland.server.Display;
import org.freedesktop.wayland.server.WlKeyboardResource;
import org.freedesktop.wayland.server.WlSurfaceResource;
import org.freedesktop.wayland.shared.WlKeyboardKeyState;
import org.westmalle.wayland.core.events.Key;

import java.util.Optional;
import java.util.Set;

import javax.annotation.Nonnull;

@AutoFactory(className = "KeyboardDeviceFactory")
public class KeyboardDevice {

    @Nonnull
    private final EventBus inputBus       = new EventBus();
    @Nonnull
    private final Display display;

    private Optional<WlSurfaceResource> focus = Optional.empty();
    private int keySerial;

    KeyboardDevice(@Provided @Nonnull final Display display) {
        this.display = display;
    }

    public void key(final Set<WlKeyboardResource> wlKeyboardResources,
                    final int time,
                    final int key,
                    final WlKeyboardKeyState wlKeyboardKeyState){
        doKey(wlKeyboardResources,
              time,
              key,
              wlKeyboardKeyState);
        this.inputBus.post(Key.create(time,
                                      key,
                                      wlKeyboardKeyState));
    }

    private void doKey(final Set<WlKeyboardResource> wlKeyboardResources,
                       final int time,
                       final int key,
                       final WlKeyboardKeyState wlKeyboardKeyState) {
        getFocus().ifPresent(wlSurfaceResource -> findKeyboardResource(wlKeyboardResources,
                                                                       wlSurfaceResource)
                .ifPresent(wlKeyboardResource -> wlKeyboardResource.key(nextKeySerial(),
                                                                        time,
                                                                        key,
                                                                        wlKeyboardKeyState.getValue())));
    }

    public int nextKeySerial(){
        this.keySerial = this.display.nextSerial();
        return this.keySerial;
    }

    public int getKeySerial() {
        return this.keySerial;
    }

    public void setFocus(Optional<WlSurfaceResource> wlSurfaceResource){
        this.focus = wlSurfaceResource;
    }

    public Optional<WlSurfaceResource> getFocus() {
        return this.focus;
    }

    private Optional<WlKeyboardResource> findKeyboardResource(final Set<WlKeyboardResource> wlKeyboardResources,
                                                            final WlSurfaceResource wlSurfaceResource) {
        for (final WlKeyboardResource wlKeyboardResource : wlKeyboardResources) {
            if (wlSurfaceResource.getClient()
                    .equals(wlKeyboardResource.getClient())) {
                return Optional.of(wlKeyboardResource);
            }
        }
        return Optional.empty();
    }

    public void register(final Object listener) {
        this.inputBus.register(listener);
    }

    public void unregister(final Object listener) {
        this.inputBus.unregister(listener);
    }
}

@@ -207,16 +207,64 @@
 isplay;%0A
+import org.freedesktop.wayland.server.Listener;%0A
 import o
@@ -761,16 +761,113 @@
 isplay;%0A
+    @Nonnull%0A    private Optional%3CListener%3E focusDestroyListener = Optional.empty();%0A    @Nonnull
 %0A    pri
@@ -2004,24 +2004,29 @@
 .key(nextKey
+board
 Serial(),%0A  
@@ -2309,24 +2309,29 @@
  int nextKey
+board
 Serial()%7B%0A  
@@ -2439,16 +2439,21 @@
 t getKey
+board
 Serial()
@@ -2522,102 +2522,1825 @@
 cus(
-Optional%3CWlSurfaceResource%3E wlSurfaceResource)%7B%0A        this.focus = wlSurfaceResource;%0A    %7D%0A
+Set%3CWlKeyboardResource%3E wlKeyboardResources,%0A                         Optional%3CWlSurfaceResource%3E wlSurfaceResource)%7B%0A        Optional%3CWlSurfaceResource%3E oldFocus = getFocus();%0A        updateFocus(wlSurfaceResource);%0A        Optional%3CWlSurfaceResource%3E newFocus = getFocus();%0A        if(!oldFocus.equals(newFocus))%7B%0A            oldFocus.ifPresent(oldFocusResource -%3E findKeyboardResource(wlKeyboardResources,%0A                                                                        oldFocusResource).ifPresent(oldFocusKeyboardResource -%3E oldFocusKeyboardResource.leave(nextKeyboardSerial(),%0A                                                                                                                                                               oldFocusResource)));%0A            newFocus.ifPresent(newFocusResource -%3E findKeyboardResource(wlKeyboardResources,%0A                                                                        newFocusResource).ifPresent(newFocusKeyboardResource -%3E newFocusKeyboardResource.leave(nextKeyboardSerial(),%0A                                                                                                                                                               newFocusResource)));%0A        %7D%0A    %7D%0A%0A    private void updateFocus(Optional%3CWlSurfaceResource%3E wlSurfaceResource)%7B%0A        this.focusDestroyListener.ifPresent(Listener::remove);%0A        this.focus = wlSurfaceResource;%0A        getFocus().ifPresent(focusResource -%3E %7B%0A            this.focusDestroyListener = Optional.of(new Listener() %7B%0A                @Override%0A                public void handle() %7B%0A                    updateFocus(Optional.%3CWlSurfaceResource%3Eempty());%0A                %7D%0A            %7D);%0A            focusResource.addDestroyListener(this.focusDestroyListener.get());%0A        %7D);%0A    %7D%0A%0A    @Nonnull
 %0A   

implement leave/enter events