/* 
 * This file is part of the Echo Web Application Framework (hereinafter "Echo").
 * Copyright (C) 2002-2005 NextApp, Inc.
 *
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 */

package nextapp.echo2.webrender;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import nextapp.echo2.webrender.service.SynchronizeService;
import nextapp.echo2.webrender.util.DomUtil;

import org.w3c.dom.Element;

/**
 * <code>SynchronizeService.ClientMessagePartProcessor</code> which creates
 * a <code>ClientProperties</code> object based on the client script's
 * analysis of its environment.
 */
public class ClientAnalyzerProcessor 
implements SynchronizeService.ClientMessagePartProcessor {

    /**
     * <code>Set</code> containing valid properties which may be received from
     * the client. Property settings received from the client that are not in
     * this set are discarded.
     */
    private static final Set VALID_PROPERTIES;
    static {
        Set set = new HashSet();
        set.add(ClientProperties.NAVIGATOR_APP_CODE_NAME);
        set.add(ClientProperties.NAVIGATOR_APP_NAME);
        set.add(ClientProperties.NAVIGATOR_APP_VERSION);
        set.add(ClientProperties.NAVIGATOR_COOKIE_ENABLED);
        set.add(ClientProperties.NAVIGATOR_JAVA_ENABLED);
        set.add(ClientProperties.NAVIGATOR_LANGUAGE);
        set.add(ClientProperties.NAVIGATOR_PLATFORM);
        set.add(ClientProperties.NAVIGATOR_USER_AGENT);
        set.add(ClientProperties.SCREEN_WIDTH);
        set.add(ClientProperties.SCREEN_HEIGHT);
        set.add(ClientProperties.WINDOW_WIDTH);
        set.add(ClientProperties.WINDOW_HEIGHT);
        set.add(ClientProperties.SCREEN_COLOR_DEPTH);
        set.add(ClientProperties.UTC_OFFSET);
        VALID_PROPERTIES = Collections.unmodifiableSet(set);
    }
    
    /**
     * Analyzes the state of <code>ClientProperties</code> and adds additional
     * inferred data, such as quirk attributes based on browser type.
     * 
     * @param clientProperties the <code>ClientProperties</code> to analyze
     *        and update
     */
    private void analyze(ClientProperties clientProperties) {
        Connection conn = WebRenderAction.getActiveConnection();
        
        Enumeration localeEnum = conn.getRequest().getLocales();
        List localeList = new ArrayList();
        while (localeEnum.hasMoreElements()) {
            localeList.add(localeEnum.nextElement());
        }
        clientProperties.setProperty(ClientProperties.LOCALES, localeList.toArray(new Locale[localeList.size()]));
        
        clientProperties.setProperty(ClientProperties.REMOTE_HOST, conn.getRequest().getRemoteHost());
    
        String userAgent = clientProperties.getString(ClientProperties.NAVIGATOR_USER_AGENT).toLowerCase();
        
        boolean browserOpera = userAgent.indexOf("opera") != -1;
        boolean browserSafari = userAgent.indexOf("safari") != -1;
        boolean browserKonqueror = userAgent.indexOf("konqueror") != -1;
        
        // Note deceptive user agent fields:
        // - Konqueror and Safari UA fields contain "like Gecko"
        // - Opera UA field typically contains "MSIE"
        boolean deceptiveUserAgent = browserOpera || browserSafari || browserKonqueror;
        
        boolean browserMozilla = !deceptiveUserAgent && userAgent.indexOf("gecko") != -1;
        boolean browserFireFox = userAgent.indexOf("firefox") != -1;
        boolean browserInternetExplorer = !deceptiveUserAgent && userAgent.indexOf("msie") != -1;
        
        int majorVersion = -1, minorVersion = -1;
        
        // Store browser information.
        if (browserOpera) {
            clientProperties.setProperty(ClientProperties.BROWSER_OPERA, Boolean.TRUE);
        } else if (browserKonqueror) {
            clientProperties.setProperty(ClientProperties.BROWSER_KONQUEROR, Boolean.TRUE);
        } else if (browserSafari) {
            clientProperties.setProperty(ClientProperties.BROWSER_SAFARI, Boolean.TRUE);
        } else if (browserMozilla) {
            clientProperties.setProperty(ClientProperties.BROWSER_MOZILLA, Boolean.TRUE);
            if (browserFireFox) {
                clientProperties.setProperty(ClientProperties.BROWSER_MOZILLA_FIREFOX, Boolean.TRUE);
            }
        } else if (browserInternetExplorer) {
            clientProperties.setProperty(ClientProperties.BROWSER_INTERNET_EXPLORER, Boolean.TRUE);
            if (userAgent.indexOf("msie 6.") != -1) {
                majorVersion = 6;
            } else if (userAgent.indexOf("msie 7.") != -1) {
                majorVersion = 7;
            }
        }
        
        if (majorVersion != -1) {
            clientProperties.setProperty(ClientProperties.BROWSER_VERSION_MAJOR, Integer.toString(majorVersion));
        }
        
        if (minorVersion != -1) {
            clientProperties.setProperty(ClientProperties.BROWSER_VERSION_MINOR, Integer.toString(minorVersion));
        }
        
        // Set quirk flags.
        if (browserInternetExplorer) {
            clientProperties.setProperty(ClientProperties.QUIRK_IE_REPAINT, Boolean.TRUE);
            clientProperties.setProperty(ClientProperties.QUIRK_TEXTAREA_CONTENT, Boolean.TRUE);
            clientProperties.setProperty(ClientProperties.QUIRK_IE_TEXTAREA_NEWLINE_OBLITERATION, Boolean.TRUE);
            clientProperties.setProperty(ClientProperties.QUIRK_IE_SELECT_LIST_DOM_UPDATE, Boolean.TRUE);
            clientProperties.setProperty(ClientProperties.QUIRK_CSS_BORDER_COLLAPSE_INSIDE, Boolean.TRUE);
            clientProperties.setProperty(ClientProperties.QUIRK_CSS_BORDER_COLLAPSE_FOR_0_PADDING, Boolean.TRUE);
            
            clientProperties.setProperty(ClientProperties.PROPRIETARY_IE_CSS_EXPRESSIONS_SUPPORTED, Boolean.TRUE);
            clientProperties.setProperty(ClientProperties.PROPRIETARY_EVENT_MOUSE_ENTER_LEAVE_SUPPORTED, Boolean.TRUE);

            // The following flaws are verified as present in IE7.0Beta2:
            clientProperties.setProperty(ClientProperties.QUIRK_IE_TABLE_PERCENT_WIDTH_SCROLLBAR_ERROR, Boolean.TRUE);
            clientProperties.setProperty(ClientProperties.QUIRK_CSS_POSITIONING_ONE_SIDE_ONLY, Boolean.TRUE);
            clientProperties.setProperty(ClientProperties.QUIRK_IE_SELECT_PERCENT_WIDTH, Boolean.TRUE);
            
            if (majorVersion < 7) {
                // The following flaws are verified as fixed in IE7.0Beta2:
                clientProperties.setProperty(ClientProperties.PROPRIETARY_IE_PNG_ALPHA_FILTER_REQUIRED, Boolean.TRUE);
                clientProperties.setProperty(ClientProperties.QUIRK_CSS_BACKGROUND_ATTACHMENT_USE_FIXED,  Boolean.TRUE);
                clientProperties.setProperty(ClientProperties.QUIRK_IE_SELECT_Z_INDEX, Boolean.TRUE);
            }
        }
        if (browserOpera) {
            clientProperties.setProperty(ClientProperties.QUIRK_TEXTAREA_CONTENT, Boolean.TRUE);
            clientProperties.setProperty(ClientProperties.QUIRK_OPERA_NO_CSS_TEXT, Boolean.TRUE);
        }
        if (browserMozilla) {
            clientProperties.setProperty(ClientProperties.QUIRK_MOZILLA_TEXT_INPUT_REPAINT, Boolean.TRUE);
            clientProperties.setProperty(ClientProperties.QUIRK_MOZILLA_PERFORMANCE_LARGE_DOM_REMOVE, Boolean.TRUE);
        }
    }
    
    /**
     * @see nextapp.echo2.webrender.service.SynchronizeService.ClientMessagePartProcessor#getName()
     */
    public String getName() {
        return "EchoClientAnalyzer";
    }
    
    /**
     * @see nextapp.echo2.webrender.service.SynchronizeService.ClientMessagePartProcessor#process(
     *      nextapp.echo2.webrender.UserInstance, org.w3c.dom.Element)
     */
    public void process(UserInstance userInstance, Element messagePartElement) {
        ClientProperties clientProperties = new ClientProperties();
        Element[] propertyElements = DomUtil.getChildElementsByTagName(messagePartElement, "property");
        for (int i = 0; i < propertyElements.length; ++i) {
            String propertyName = propertyElements[i].getAttribute("name");
            if (VALID_PROPERTIES.contains(propertyName)) {
                String type = propertyElements[i].getAttribute("type");
                if ("text".equals(type)) {
                    clientProperties.setProperty(propertyName, propertyElements[i].getAttribute("value"));
                } else if ("integer".equals(type)) {
                    try {
                        clientProperties.setProperty(propertyName, 
                                new Integer(propertyElements[i].getAttribute("value")));
                    } catch (NumberFormatException ex) { }
                } else if ("boolean".equals(type)) {
                    clientProperties.setProperty(propertyName, 
                            new Boolean("true".equals(propertyElements[i].getAttribute("value"))));
                }
            }
        }
        userInstance.setClientProperties(clientProperties);
        analyze(clientProperties);
    }
}    
