/*
 * Westford Wayland Compositor.
 * Copyright (C) 2016  Erik De Rijcke
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.westford.compositor.core;

import org.freedesktop.wayland.server.WlSurfaceRequests;
import org.freedesktop.wayland.server.WlSurfaceResource;
import org.westford.compositor.protocol.WlSurface;

import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Optional;

@Singleton
public class Scene {
    @Nonnull
    private final LinkedList<WlSurfaceResource> surfacesStack = new LinkedList<>();
    @Nonnull
    private final InfiniteRegion infiniteRegion;

    @Inject
    Scene(@Nonnull final InfiniteRegion infiniteRegion) {
        this.infiniteRegion = infiniteRegion;
    }

    @Nonnull
    public Optional<SurfaceView> pickSurfaceView(final Point global) {

        final Iterator<SurfaceView> surfaceViewIterator = createSurfaceViewStack().descendingIterator();
        Optional<SurfaceView>       pointerOver         = Optional.empty();

        while (surfaceViewIterator.hasNext()) {
            final SurfaceView surfaceView = surfaceViewIterator.next();

            if (!surfaceView.isDrawable() || !surfaceView.isEnabled()) {
                continue;
            }

            final WlSurfaceResource surfaceResource = surfaceView.getWlSurfaceResource();
            final WlSurfaceRequests implementation  = surfaceResource.getImplementation();
            final Surface           surface         = ((WlSurface) implementation).getSurface();

            final Optional<Region> inputRegion = surface.getState()
                                                        .getInputRegion();
            final Region region = inputRegion.orElseGet(() -> this.infiniteRegion);

            final Rectangle size = surface.getSize();

            final Point local = surfaceView.local(global);
            if (region.contains(size,
                                local)) {
                pointerOver = Optional.of(surfaceView);
                break;
            }
        }

        return pointerOver;
    }

    @Nonnull
    public LinkedList<WlSurfaceResource> getSurfacesStack() {
        return this.surfacesStack;
    }


    public LinkedList<SurfaceView> createSurfaceViewStack() {

        final LinkedList<SurfaceView> surfaceViews = new LinkedList<>();
        this.surfacesStack.forEach(wlSurfaceResource -> loopSiblings(wlSurfaceResource,
                                                                     surfaceViews));

        return surfaceViews;
    }

    private void addSiblingViews(final SurfaceView parentSurfaceView,
                                 final LinkedList<SurfaceView> surfaceViews) {

        final WlSurfaceResource parentWlSurfaceResource = parentSurfaceView.getWlSurfaceResource();
        final WlSurface         parentWlSurface         = (WlSurface) parentWlSurfaceResource.getImplementation();
        final Surface           parentSurface           = parentWlSurface.getSurface();

        parentSurface.getSiblings()
                     .forEach(sibling -> {

                         final WlSurface siblingWlSurface = (WlSurface) sibling.getWlSurfaceResource()
                                                                               .getImplementation();
                         final Surface siblingSurface = siblingWlSurface.getSurface();

                         //only consider surface if it has a role.
                         //TODO we could move the views to the generic role itf.
                         if (siblingSurface.getRole()
                                           .isPresent()) {
                             siblingSurface.getViews()
                                           .forEach(siblingSurfaceView -> {

                                               if (siblingSurfaceView.getParent()
                                                                     .orElse(siblingSurfaceView)
                                                                     .equals(parentSurfaceView)) {

                                                   surfaceViews.add(siblingSurfaceView);

                                                   if (!siblingSurfaceView.equals(parentSurfaceView)) {
                                                       addSiblingViews(siblingSurfaceView,
                                                                       surfaceViews);
                                                   }
                                               }
                                           });
                         }
                     });

    }

    private void loopSiblings(WlSurfaceResource wlSurfaceResource,
                              LinkedList<SurfaceView> surfaceViews) {

        final WlSurface wlSurface = (WlSurface) wlSurfaceResource.getImplementation();
        final Surface   surface   = wlSurface.getSurface();

        surface.getViews()
               .forEach(parentSurfaceView -> {

                   addSiblingViews(parentSurfaceView,
                                   surfaceViews);
                   surface.getSiblings()
                          .forEach(sibling -> {
                              final WlSurfaceResource siblingWlSurfaceResource = sibling.getWlSurfaceResource();
                              if (!siblingWlSurfaceResource.equals(wlSurfaceResource)) {
                                  loopSiblings(siblingWlSurfaceResource,
                                               surfaceViews);
                              }
                          });
               });
    }
}

@@ -4325,24 +4325,25 @@
 resent()) %7B%0A
+%0A
             
@@ -4618,22 +4618,22 @@
    .
-orElse
+filter
 (sibling
 Surf
@@ -4620,32 +4620,38 @@
  .filter(sibling
+Parent
 SurfaceView)%0A   
@@ -4645,17 +4645,19 @@
 faceView
-)
+ -%3E
 %0A       
@@ -4718,16 +4718,56 @@
         
+                siblingParentSurfaceView
 .equals(
@@ -4785,20 +4785,35 @@
 ceView))
- %7B%0A%0A
+%0A                  
         
@@ -4859,46 +4859,23 @@
     
-surfaceViews.add(siblingSurfaceView);%0A
+.isPresent()) %7B
 %0A   
@@ -4926,45 +4926,31 @@
     
-if (!siblingSurfaceView.equals(parent
+addSiblingViews(sibling
 Surf
@@ -4956,20 +4956,17 @@
 faceView
-)) %7B
+,
 %0A       
@@ -5017,32 +5017,21 @@
     
-addSiblingViews(siblingS
+            s
 urfa
@@ -5028,33 +5028,35 @@
      surfaceView
-,
+s);
 %0A               
@@ -5069,32 +5069,44 @@
                 
+          %7D%0A
                 
@@ -5119,38 +5119,93 @@
             
-surfaceViews);
+             else if (siblingSurfaceView.equals(parentSurfaceView)) %7B
 %0A           
@@ -5236,33 +5236,69 @@
                 
-%7D
+surfaceViews.add(siblingSurfaceView);
 %0A               
@@ -5430,17 +5430,16 @@
     %7D);%0A
-%0A
     %7D%0A%0A 
@@ -5794,38 +5794,16 @@
 eView -%3E
- %7B%0A%0A                  
  addSibl
@@ -5869,527 +5869,47 @@
     
-surfaceViews);%0A                   surface.getSiblings()%0A                          .forEach(sibling -%3E %7B%0A                              final WlSurfaceResource siblingWlSurfaceResource = sibling.getWlSurfaceResource();%0A                              if (!siblingWlSurfaceResource.equals(wlSurfaceResource)) %7B%0A                                  loopSiblings(siblingWlSurfaceResource,%0A                                               surfaceViews);%0A                              %7D%0A                          %7D);%0A               %7D
+                          surfaceViews)
 );%0A 

fix scene providing views double