/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License).  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the license at
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Copyright (c) Ericsson AB, 2004-2007. All rights reserved.
 */
package org.jvnet.glassfish.comms.clb.core.common.chr.dcr;

import com.ericsson.ssa.sip.SipFactoryImpl;
import com.ericsson.ssa.sip.SipServletRequestImpl;

import org.jvnet.glassfish.comms.clb.proxy.http.util.HttpRequest;
import org.jvnet.glassfish.comms.util.LogUtil;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.Serializable;

import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.TelURL;
import javax.servlet.sip.URI;


/**
 * Condition. The Condition interface is implemented by all the operators (and,
 * or, not) and all the operand (equal, contains, exists, subdomain-of)
 *
 * @reviewed qmigkra 2007-jan-17
 */
public abstract class Condition implements Serializable {
    private static LogUtil logger = new LogUtil(LogUtil.CLB_LOG_DOMAIN);

    // Represent a user part of a sip address
    Pattern userPattern = Pattern.compile(":([^/]+)@");

    // Host pattern in a sip uri
    Pattern hostPattern = Pattern.compile("@([\\w\\.]+)");

    // Port pattern in a uri
    Pattern portPattern = Pattern.compile(":([\\d]+)");

    /**
     * Evaluate the condition
     *
     * @param object The object to evaluate
     * @return True if the operand or operator evaluation is satisfied, false
     *         otherwise
     */
    public abstract String evaluate(Object object);

    public void addChildren(NodeList list) {
        int noNodes = list.getLength();

        for (int i = 0; i < noNodes; i++) {
            Node node = list.item(i);

            if (node instanceof Element) {
                addChild((Element) node);
            }
        }
    }

    public abstract void addChild(Element element);

    public void addElmentData(Element element) {
    }

    public String processReturnLogic(Object request, String name) {
        String returnValue = null;

        if (request instanceof SipServletRequestImpl) {
            returnValue = processSipReturnLogic((SipServletRequestImpl) request,
                    name);
        } else {
            returnValue = processHttpReturnLogic((HttpRequest) request, name);
        }

        return returnValue;
    }

    private String processHttpReturnLogic(HttpRequest request, String name) {
        if (name.startsWith("request.")) {
            StringTokenizer token = new StringTokenizer(name, ".");
            int ntok = token.countTokens();
            String[] elements = new String[ntok];

            for (int i = 0; i < ntok; i++) {
                elements[i] = token.nextToken();
            }

            String headerName = null;

            if (ntok > 0) {
                headerName = elements[1];
            }

            // logic to match the request.<headerName> or
            // request.<headerName>.uri
            if (name.endsWith(".uri") || name.endsWith(elements[1])) {
                return request.getHeader(headerName);
            }

            // logic to match the request.<headerName>.uri.user
            if (name.endsWith(headerName + ".uri.user")) {
                String requestParameter = request.getHeader(headerName);
                Matcher matcher = userPattern.matcher(requestParameter);

                if (matcher.find()) {
                    // Found matching user in regexp.
                    return matcher.group(1);
                } else {
                    return null;
                }
            }

            // logic to match the request.<headerName>.uri.host
            if (name.endsWith(headerName + ".uri.host")) {
                String requestParameter = request.getHeader(headerName);
                Matcher matcher = hostPattern.matcher(requestParameter);

                if (matcher.find()) {
                    // Found matching host in regexp.
                    return matcher.group(1);
                } else {
                    return null;
                }
            }

            // logic to match the request.<headerName>.uri.resolve
            if (name.endsWith(headerName + ".uri.resolve")) {
                String requestParameter = request.getHeader(headerName);

                return DcrUtils.getInstance().transformURI(requestParameter);
            }

            // logic to match the request.<headerName>.uri.resolve.user
            if (name.endsWith(headerName + ".uri.resolve.user")) {
                String requestParameter = request.getHeader(headerName);
                String user = DcrUtils.getInstance()
                                      .transformURIgetUser(requestParameter);

                if (logger.isLoggable(Level.FINE)) {
                    logger.logMsg(Level.FINE,
                        "request.<headerName>.uri.resolve.user: " + user);
                }

                return user;
            }

            // logic to match the request.<headerName>.uri.resolve.host
            if (name.endsWith(headerName + ".uri.resolve.host")) {
                String requestParameter = request.getHeader(headerName);

                return DcrUtils.getInstance()
                               .transformURIgetHost(requestParameter);
            }
        }

        if (name.startsWith("parameter.")) {
            StringTokenizer token = new StringTokenizer(name, ".");
            int ntok = token.countTokens();
            String[] elements = new String[ntok];

            for (int i = 0; i < ntok; i++) {
                elements[i] = token.nextToken();
            }

            String parameterName = null;

            if (ntok > 0) {
                parameterName = elements[1];
            }

            // logic to match the parameter.<parameterName> or
            // parameter.<parameterName>.uri
            if (name.endsWith(".uri") || name.endsWith(elements[1])) {
                return request.getParameters().getParameter(parameterName);
            }

            // logic to match the parameter.<parameterName>.uri.user
            if (name.endsWith(parameterName + ".uri.user")) {
                String requestParameter = request.getParameters()
                                                 .getParameter(parameterName);
                Matcher matcher = userPattern.matcher(requestParameter);

                if (matcher.find()) {
                    // Found matching user in regexp.
                    return matcher.group(1);
                } else {
                    return null;
                }
            }

            // logic to match the parameter.<parameterName>.uri.host
            if (name.endsWith(parameterName + ".uri.host")) {
                String requestParameter = request.getParameters()
                                                 .getParameter(parameterName);
                Matcher matcher = hostPattern.matcher(requestParameter);

                if (matcher.find()) {
                    // Found matching user in regexp.
                    return matcher.group(1);
                } else {
                    return null;
                }
            }

            // logic to match the parameter.<parameterName>.uri.resolve
            if (name.endsWith(parameterName + ".uri.resolve")) {
                String requestParameter = request.getParameters()
                                                 .getParameter(parameterName);

                return DcrUtils.getInstance().transformURI(requestParameter);
            }

            // logic to match the parameter.<parameterName>.uri.resolve.user
            // HH19522: we need to deal with multiple URIs and take the
            // leftmost SIP URI if present, or the leftmost TEL URI otherwise
            if (name.endsWith(parameterName + ".uri.resolve.user")) {
                String requestParameter = request.getParameters()
                                                 .getParameter(parameterName);

                return DcrUtils.getInstance()
                               .transformMultiURIgetUser(requestParameter);
            }

            // logic to match the parameter.<parameterName>.resolve.host
            if (name.endsWith(parameterName + ".uri.resolve.host")) {
                String requestParameter = request.getParameters()
                                                 .getParameter(parameterName);

                return DcrUtils.getInstance()
                               .transformURIgetHost(requestParameter);
            }
        }

        if (name.startsWith("cookie.")) {
            StringTokenizer token = new StringTokenizer(name, ".");
            int ntok = token.countTokens();
            String[] elements = new String[ntok];

            for (int i = 0; i < ntok; i++) {
                elements[i] = token.nextToken();
            }

            String cookieName = null;

            // String uriName = null;
            if (ntok > 0) {
                cookieName = elements[1];
            }

            // logic to match the cookie.<cookieName>
            if (name.endsWith(elements[1])) {
                return CookieCondition.getCookieValue(request.getCookies(),
                    cookieName, null);
            }
        }

        // no match found so return null
        logger.warning("Unknown User Centric HTTP rule return name: " + name);

        return null;
    }

    public String processSipReturnLogic(SipServletRequestImpl request,
        String name) {
        if (name == null) {
            return null;
        }

        if (name.startsWith("request.uri.resolve")) {
            URI uri = DcrUtils.getInstance()
                              .attemptResolveURI(request.getRequestURI());

            if (uri == null) {
                return null;
            }

            if (name.endsWith("uri.resolve")) {
                return uri.toString();
            }

            if (name.endsWith("uri.resolve.user")) {
                if (uri.isSipURI()) {
                    return ((SipURI) uri).getUser();
                } else if (isTelUrl(uri)) {
                    return ((TelURL) uri).getPhoneNumber();
                }
            }

            if (name.endsWith("uri.resolve.host")) {
                if (uri.isSipURI()) {
                    return ((SipURI) uri).getHost();
                }
            }
        }

        // request.uri
        // request.uri.scheme
        // request.uri.user
        // request.uri.host
        // request.uri.port
        // request.method
        if (name.equals("request.uri")) {
            return request.getRequestURI().toString();
        }

        if (name.equals("request.uri.scheme")) {
            return request.getRequestURI().getScheme();
        }

        if (name.equals("request.uri.user")) {
            URI uri = request.getRequestURI();

            if (uri.isSipURI()) {
                return ((SipURI) uri).getUser();
            } else if (isTelUrl(uri)) {
                return ((TelURL) uri).getPhoneNumber();
            }
        }

        if (name.equals("request.uri.host")) {
            URI uri = request.getRequestURI();

            if (uri.isSipURI()) {
                return ((SipURI) uri).getHost();
            } else {
                Matcher matcher = hostPattern.matcher(uri.toString());

                if (matcher.find()) {
                    // Found matching user in regexp.
                    return matcher.group(1);
                } else {
                    return null;
                }
            }
        }

        if (name.equals("request.uri.port")) {
            URI uri = request.getRequestURI();

            if (uri.isSipURI()) {
                return Integer.toString(((SipURI) uri).getPort());
            } else {
                Matcher matcher = portPattern.matcher(uri.toString());

                if (matcher.find()) {
                    // Found matching user in regexp.
                    return matcher.group(1);
                } else {
                    return null;
                }
            }
        }

        if (name.equals("request.method")) {
            return request.getMethod();
        }
        // General Headers
        //
        // request.<header>
        // request.<header>.uri
        // request.<header>.uri.scheme
        // request.<header>.uri.user
        // request.<header>.uri.host
        // request.<header>.uri.port
        // request.<header>.uri.display-name
        // request.<header>.uri.resolve
        // request.<header>.uri.resolve.user
        // request.<header>.uri.resolve.host
        else if (name.startsWith("request.")) {
            StringTokenizer token = new StringTokenizer(name, ".");
            int ntok = token.countTokens();
            String[] elements = new String[ntok];

            for (int i = 0; i < ntok; i++) {
                elements[i] = token.nextToken();
            }

            // request.<header>
            if (elements.length == 2) {
                return request.getHeader(elements[1]);
            }

            if ((elements.length > 2) && "uri".equals(elements[2])) {
                try {
                    // check if the mapping ends with uri
                    if ((elements.length < 4) && "uri".equals(elements[2])) {
                        return request.getAddressHeader(elements[1]).getURI()
                                      .toString();
                    }

                    // Handle request.<header>.uri.scheme
                    if ("scheme".equals(elements[3])) {
                        return request.getAddressHeader(elements[1]).getURI()
                                      .getScheme();
                    }

                    // request.<header>.uri.display-name
                    if ("display-name".equals(elements[3])) {
                        return request.getAddressHeader(elements[1])
                                      .getDisplayName();
                    }

                    if ("user".equals(elements[3])) {
                        String headerValue = request.getHeader(elements[1]);
                        Matcher matcher = userPattern.matcher(headerValue);

                        if (matcher.find()) {
                            // Found matching user in regexp.
                            return matcher.group(1);
                        } else {
                            return null;
                        }
                    }

                    if ("host".equals(elements[3])) {
                        String headerValue = request.getHeader(elements[1]);
                        Matcher matcher = hostPattern.matcher(headerValue);

                        if (matcher.find()) {
                            // Found matching user in regexp.
                            return matcher.group(1);
                        } else {
                            return null;
                        }
                    }

                    if ("port".equals(elements[3])) {
                        String headerValue = request.getHeader(elements[1]);
                        Matcher matcher = portPattern.matcher(headerValue);

                        if (matcher.find()) {
                            // Found matching user in regexp.
                            return matcher.group(1);
                        } else {
                            return null;
                        }
                    }

                    if ("resolve".equals(elements[3])) {
                        URI uri = DcrUtils.getInstance()
                                          .transformURI(request.getAddressHeader(
                                    elements[1]).getURI());

                        if (uri == null) {
                            return null;
                        }

                        if (name.endsWith("uri.resolve")) {
                            return uri.toString();
                        }

                        if (name.endsWith("uri.resolve.user")) {
                            if (uri.isSipURI()) {
                                return ((SipURI) uri).getUser();
                            }
                        }

                        if (name.endsWith("uri.resolve.host")) {
                            if (uri.isSipURI()) {
                                return ((SipURI) uri).getHost();
                            }
                        }
                    }
                } catch (ServletParseException e) {
                    logger.warning("User Centric SIP rule exception: " + name +
                        " " + e);
                }
            }
        }

        // no match found hence return null.
        logger.warning("Unknown User Centric SIP rule return name: " + name);

        return null;
    }

    private boolean isTelUrl(URI uri) {
        return SipFactoryImpl.TEL_URI_PROTOCOL.equals(uri.getScheme());
    }
}
