/*
 * Copyright 2016, Red Hat, Inc. and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.zanata.page;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.Optional;

import javax.annotation.Nullable;

import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.openqa.selenium.support.events.EventFiringWebDriver;
import org.openqa.selenium.support.events.WebDriverEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * DswidParamChecker provides a WebDriverEventListener which tracks dswid
 * parameters (used by DeltaSpike's window/conversation management). It logs
 * a warning when navigation loses the dswid parameter, and throws an
 * AssertionError if the dswid changes to a different value.
 * </p>
 * <p>
 * DswidParamChecker currently only supports a single browser tab, since
 * it assumes a single dswid value.
 * </p>
 * @author Sean Flanigan <a href="mailto:sflaniga@redhat.com">sflaniga@redhat.com</a>
 */
public class DswidParamChecker {
    private static final Logger log = LoggerFactory.getLogger(DswidParamChecker.class);
    private final EventFiringWebDriver driver;
    private final WebDriverEventListener urlListener;
    private @Nullable String oldUrl;
    private @Nullable String oldDswid;

    /**
     * Creates a listener for the specified driver, but does not register it. See getEventListener().
     * @param driver
     */
    public DswidParamChecker(EventFiringWebDriver driver) {
        this.driver = driver;
        this.urlListener =
                (WebDriverEventListener) Proxy.newProxyInstance(
                        DswidParamChecker.class.getClassLoader(),
                        new Class<?>[]{ WebDriverEventListener.class },
                        this::invoke
                );
    }

    /**
     * Returns a WebDriverEventListener which will track dswid parameters. It can be registered like this:
     * driver.register(new DswidParamChecker(driver).getEventListener());
     */
    public WebDriverEventListener getEventListener() {
        return urlListener;
    }

    private Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        String url = driver.getCurrentUrl();
        String query = new URL(url).getQuery();
        Optional<String> dswid = URLEncodedUtils.parse(query, UTF_8)
                .stream()
                .filter(p -> p.getName().equals("dswid"))
                .map(NameValuePair::getValue)
                .findFirst();
        if (oldDswid != null) {
            assert oldUrl != null;
            if (!dswid.isPresent()) {
                log.warn("missing dswid on transition from {} to {}",
                        oldUrl, url);
            } else {
                if (!oldDswid.equals(dswid.get())) {
                    throw new AssertionError(
                            "changed dswid on transition from " +
                                    oldUrl + " to " + url);
                }
            }
        }
        oldDswid = dswid.orElse(null);
        oldUrl = url;
        return null;
    }
}

@@ -1118,16 +1118,55 @@
 .Proxy;%0A
+import java.net.MalformedURLException;%0A
 import j
@@ -3240,24 +3240,42 @@
 Throwable %7B%0A
+        try %7B%0A    
         Stri
@@ -3311,24 +3311,28 @@
 ();%0A        
+    
 String query
@@ -3367,16 +3367,20 @@
         
+  
+  
 Optional
@@ -3387,16 +3387,135 @@
 %3CString%3E
+ dswid;%0A            if (query == null) %7B%0A                dswid = Optional.empty();%0A            %7D else %7B%0A               
  dswid =
@@ -3567,16 +3567,24 @@
         
+        
 .stream(
@@ -3601,16 +3601,24 @@
         
+  
+      
 .filter(
@@ -3667,16 +3667,24 @@
         
+      
+  
 .map(Nam
@@ -3721,16 +3721,24 @@
         
+        
 .findFir
@@ -3739,24 +3739,42 @@
 indFirst();%0A
+            %7D%0A    
         if (
@@ -3805,16 +3805,20 @@
         
+    
 assert o
@@ -3840,24 +3840,28 @@
             
+   
+ 
 if (!dswid.i
@@ -3870,24 +3870,28 @@
 resent()) %7B%0A
+    
             
@@ -3968,24 +3968,28 @@
             
+    
 oldUrl, url)
@@ -3994,32 +3994,36 @@
 l);%0A            
+   
+ 
 %7D else %7B%0A       
@@ -4007,32 +4007,36 @@
        %7D else %7B%0A
+    
                 
@@ -4088,24 +4088,28 @@
             
+    
 throw new As
@@ -4150,16 +4150,20 @@
         
+   
+ 
 %22changed
@@ -4192,16 +4192,20 @@
 rom %22 +%0A
+    
         
@@ -4264,34 +4264,41 @@
                 
-%7D%0A
+    %7D%0A   
             %7D%0A  
@@ -4285,34 +4285,39 @@
 %0A               
+ 
 %7D%0A
+    
         %7D%0A      
@@ -4302,32 +4302,36 @@
 %7D%0A            %7D%0A
+    
         oldDswid
@@ -4357,24 +4357,28 @@
 l);%0A        
+   
+ 
 oldUrl = url
@@ -4383,24 +4383,28 @@
 rl;%0A        
+    
 return null;
@@ -4400,20 +4400,144 @@
 eturn null;%0A
+        %7D catch (MalformedURLException e) %7B%0A            // just ignore this URL entirely%0A            return null;%0A        %7D%0A
     %7D%0A%7D%0A

Handle non-http URLs, missing query string