/*
 * 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.eas.telurl.TelUrlResolver;
import com.ericsson.eas.telurl.TelUrlResolverException;

import org.jvnet.glassfish.comms.util.LogUtil;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import java.util.logging.Level;

import javax.servlet.sip.Address;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipFactory;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.URI;


/**
 * Utilities for the dynamic data centric rules.
 */
public class DcrUtils {
    private static TelUrlResolver resolver;
    private static SipFactory sf;
    private static DcrUtils instance;
    private static LogUtil logger = new LogUtil(LogUtil.CLB_LOG_DOMAIN);

    private DcrUtils() {
    }

    public static void setup(TelUrlResolver resolver, SipFactory sf) {
        DcrUtils.resolver = resolver;
        DcrUtils.sf = sf;
    }

    public static DcrUtils getInstance() {
        if ((resolver == null) || (sf == null)) {
            throw new IllegalStateException("The DcrUtils has not been setup!");
        }

        if (instance == null) {
            instance = new DcrUtils();
        }

        return instance;
    }

    /**
     * Extract userpart from a SIP URI
     *
     * @param param
     * @return username
     * @throws UnsupportedEncodingException
     */
    public String getUserPart(String param) throws UnsupportedEncodingException {
        String result = null;

        if (param != null) {
            URI uri = null;

            try {
                uri = sf.createAddress(param).getURI();
            } catch (ServletParseException e) {
            }

            // Resolve enum
            SipURI sipuri = null;

            if (uri != null) {
                sipuri = transformURI(uri);
            }

            // Extract username part
            if (sipuri != null) {
                result = sipuri.getUser();
            }
        }

        return result;
    }

    /**
     * Returns the most significant user part of a given comma-separated
     * collection of URIs, or null if no usable user part can be found.
     *
     * @return
     */
    public String transformMultiURIgetUser(String uris) {
        SipURI su = getUsableSipUri(new AddressTokenizer(uris), true, uris);

        return (su == null) ? null : su.getUser();
    }

    public String transformURI(String uriStr) {
        if (uriStr == null) {
            return null;
        }

        try {
            URI uri = sf.createURI(uriStr);

            return transformURI(uri).toString();
        } catch (ServletParseException e) {
            // Return null at failure
            logger.warning("Exception when transforming URI: " + e +
                "; set null as result.");
        }

        return null;
    }

    public URI attemptResolveURI(URI uri) {
        if (resolver.isTelephoneNumber(uri)) {
            try {
                URI resolvedUri = resolver.lookupSipURI(uri);

                if (resolvedUri != null) {
                    return resolvedUri;
                }
            } catch (IOException e) {
                return null;
            } catch (TelUrlResolverException e) {
                return null;
            }
        }

        return uri;
    }

    /**
     * Resolve a TelURL to the users SipURI
     *
     * @param uri
     * @return
     */
    private SipURI resolve(URI uri) {
        SipURI sipUri = null;

        if (logger.isLoggable(Level.FINE)) {
            logger.logMsg(Level.FINE, "resolve uri: " + uri);
        }

        // if (uri.isPhoneNumber()) {
        if (resolver.isTelephoneNumber(uri)) {
            try {
                sipUri = resolver.lookupSipURI(uri);

                if (logger.isLoggable(Level.FINE)) {
                    logger.logMsg(Level.FINE, "uri: " + uri);
                    logger.logMsg(Level.FINE, "resolves to: " + sipUri);
                }
            } catch (IOException ioe) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.logMsg(Level.FINE, "failed to resolve: " + ioe);
                }
            } catch (TelUrlResolverException telExc) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.logMsg(Level.FINE,
                        "TelUrlResolverException, failed to resolve: " + uri);
                }
            }
        }

        return sipUri;
    }

    /**
     * Transform a URI to a SipURI. If the URI is a TelURL then perform an ENUM
     * lookup of the SipURI.
     *
     * @param uri
     * @return SipURI or null
     */
    public SipURI transformURI(URI uri) {
        SipURI sipUri = null;

        if (logger.isLoggable(Level.FINE)) {
            logger.logMsg(Level.FINE, "transformURI uri: " + uri);
        }

        if (uri.isSipURI()) {
            sipUri = (SipURI) uri;

            String user = sipUri.getParameter("user");

            // if(logger.isLoggable(Level.FINE))
            // {
            // logger.logMsg(Level.FINE, "transformURI user: "+user);
            // }
            if ((user != null) && user.equals("phone")) {
                // Do the ENUM first
                sipUri = resolve(uri);
            }
        } else if (resolver.isTelephoneNumber(uri)) {
            sipUri = resolve(uri);
        }

        return sipUri;
    }

    public String transformURIgetHost(String uriStr) {
        if (uriStr == null) {
            return null;
        }

        try {
            URI uri = sf.createURI(uriStr);

            return transformURI(uri).getHost();
        } catch (ServletParseException e) {
            // Return null at failure
            logger.warning("Exception when transforming URI: " + e +
                "; set null as result.");
        }

        return null;
    }

    /**
     * Return the effective user part of the given URI string, or null if no
     * user part can be extracted.
     *
     * The transformURI method is always applied, which will perform an ENUM
     * lookup if the URI is a telURL.
     *
     * @param uriStr
     * @return
     */
    public String transformURIgetUser(String uriStr) {
        try {
            if (logger.isLoggable(Level.FINE)) {
                logger.logMsg(Level.FINE,
                    "transformURIgetUser uriStr: " + uriStr);
            }

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

            URI uri = forgivingCreateURI(uriStr);
            SipURI transformedSipURI = transformURI(uri);

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

            return transformedSipURI.getUser();
        } catch (ServletParseException e) {
            logger.warning("Bad uri to transform: " + uriStr);
        }

        return null;
    }

    /**
     * A forgiving variant of createURI. If the given string cannot be parsed as
     * a URI, try to parse it as an Address instead and extract the URI from
     * there.
     *
     */
    private URI forgivingCreateURI(String x) throws ServletParseException {
        try {
            URI u = sf.createURI(x);

            if (logger.isLoggable(Level.FINE)) {
                logger.logMsg(Level.FINE,
                    "String: " + x + ", parsed to URI: " + u);
            }

            return u;
        } catch (ServletParseException e) {
            if (logger.isLoggable(Level.FINE)) {
                logger.logMsg(Level.FINE,
                    "String: " + x + ", try parse as Address instead");
            }

            return nameAddrCreateURI(x);
        }
    }

    /**
     * Try to find a SipURI with a non-empty "user" string usable as a hash key.
     * Addresses are tried in this order: first all addresses that are literally
     * SIP URIs; then all other URIs, such as TEL URLs. All addresses are
     * subjected to resolution by the transformURI method.
     *
     * @param t an AddressTokenizer
     * @param considerSipUris true means consider literally SIP URIs only, false
     *                means consider non-SIP only.
     * @param uris the string of unparsed addresses
     * @return a usable SipURI, or null in case of failure.
     */
    private SipURI getUsableSipUri(AddressTokenizer t, boolean considerSipUris,
        String uris) {
        if (!t.hasMoreTokens()) {
            return (considerSipUris == false) ? null
                                              : getUsableSipUri(new AddressTokenizer(
                    uris), false, null);
        } else {
            AddressTokenizer.Token t0 = t.nextToken();
            URI u0 = null;

            try {
                switch (t0.code) {
                case 0:
                case 1:
                    u0 = forgivingCreateURI(t0.token);

                    break;

                case 2:
                    u0 = nameAddrCreateURI(t0.token);

                    break;
                }
            } catch (ServletParseException e) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.logMsg(Level.FINE,
                        "multiple URIs, exception when parsing this one: " +
                        t0.token);
                }

                return getUsableSipUri(t, considerSipUris, uris);
            }

            if ((u0 == null) || (u0.isSipURI() && !considerSipUris) ||
                    (!u0.isSipURI() && considerSipUris)) {
                return getUsableSipUri(t, considerSipUris, uris);
            } else {
                SipURI su0 = transformURI(u0);

                if ((su0 == null) || (su0.getUser() == null) ||
                        su0.getUser().equals("")) {
                    return getUsableSipUri(t, considerSipUris, uris);
                } else {
                    return su0;
                }
            }
        }
    }

    /**
     * Call this method when we already know that the string to be parsed is a
     * name-addr.
     *
     * @param x
     * @return
     * @throws ServletParseException
     */
    private URI nameAddrCreateURI(String x) throws ServletParseException {
        Address a = sf.createAddress(x);
        URI u = a.getURI();

        if (logger.isLoggable(Level.FINE)) {
            logger.logMsg(Level.FINE,
                "string: " + x + ", parsed to Address: " + a + ", and URI: " +
                u);
        }

        return u;
    }
}
