/*
 * 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 com.ericsson.ssa.sip;

import com.ericsson.ssa.sip.dns.EnumUtil;

import java.io.Serializable;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;

// inserted by hockey (automatic)
import java.util.logging.Logger;

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


/**
 * Implementation of the TelURL.
 *
 *
 * @author ekrigro
 * @since 2005-maj-04
 * @reviewed qmigkra 2007-jan-17
 *
 */
public class TelURLImpl extends URIImpl implements TelURL, Serializable {
    /**
     *
     */
    private static final long serialVersionUID = 3690194326332258097L;
    static String NO_NUMBER = "The parsed TEL URL did not cantain any number";
    static String DUPLICATE_PARAM = "The parsed TEL URL has two parameter with the same name : ";
    static String PHONE_CTX_MISSING = "The parsed TEL URL doesn't contain the mandatory parameter phone-context";
    static String PHONE_CONTEXT = "phone-context";
    private static Charset _cs = Charset.forName(SipFactoryImpl.SIP_CHARSET);
    private static final Logger _logger = Logger.getLogger("SipContainer");
    private boolean _global = false;
    private String _number = null;

    private TelURLImpl() {
    }

    public TelURLImpl(byte[] bytes, int offset) throws ServletParseException {
        ByteBuffer bb = ByteBuffer.wrap(bytes, offset, bytes.length - offset);
        CharBuffer cb = _cs.decode(bb);
        parse(cb);
    }

    public String getPhoneNumber() {
        return _number;
    }

    public void setPhoneNumber(String number) {
        _number = number;
    }

    public boolean isGlobal() {
        return _global;
    }

    public Object clone() {
        TelURLImpl uri = new TelURLImpl();
        uri._global = _global;
        uri._number = _number;
        uri._parameters = (ParameterByteMap) _parameters.clone();

        return uri;
    }

    /**
     * The comparison is done according to RFC 3966, chapter 4 "URI Comparisons"
     *
     * @see java.lang.Object#equals(java.lang.Object)
     */
    public boolean equals(Object o) {
        if (o == null) {
            return false;
        }

        TelURLImpl uri;

        try {
            uri = (TelURLImpl) o;
        } catch (Exception e) {
            return false;
        }

        if (_global != uri.isGlobal()) {
            return false;
        }

        // Remove visual separators
        String tel1 = EnumUtil.getAUSValue(_number);
        String tel2 = EnumUtil.getAUSValue(uri.getPhoneNumber());

        // and compare
        if (!tel1.equals(tel2)) {
            return false;
        }

        if (!_global) {
            ParameterByteMap map1 = (ParameterByteMap) _parameters.clone();
            ParameterByteMap map2 = (ParameterByteMap) uri._parameters.clone();

            // context = ";phone-context=" descriptor
            // descriptor = domainname / global-number-digits
            // global-number-digits = "+" *phonedigit DIGIT *phonedigit
            // local-number-digits =
            // *phonedigit-hex (HEXDIG / "*" / "#")*phonedigit-hex
            // domainname = *( domainlabel "." ) toplabel [ "." ]
            // domainlabel = alphanum
            // / alphanum *( alphanum / "-" ) alphanum
            // toplabel = ALPHA / ALPHA *( alphanum / "-" ) alphanum
            String phoneContext1 = map1.get(PHONE_CONTEXT);
            map1.remove(PHONE_CONTEXT);

            String phoneContext2 = map2.get(PHONE_CONTEXT);
            map2.remove(PHONE_CONTEXT);

            // If it is a global number
            if (phoneContext1.startsWith("+") && phoneContext2.startsWith("+")) {
                phoneContext1 = EnumUtil.getAUSValue(phoneContext1);
                phoneContext2 = EnumUtil.getAUSValue(phoneContext2);

                if (!phoneContext1.equals(phoneContext2)) {
                    return false;
                }
            } else { // else it is a domain

                if (!phoneContext1.equals(phoneContext2)) {
                    return false;
                }
            }

            if (!map1.equals(map2)) {
                return false;
            }
        } else {
            if (!_parameters.equals(uri._parameters)) {
                return false;
            }
        }

        return true;
    }

    private void parse(CharBuffer cb) throws ServletParseException {
        cb.mark();

        char c = 0;

        if (cb.hasRemaining()) {
            c = cb.get();
        }

        if (c == '+') {
            _global = true;
            cb.mark();

            if (cb.hasRemaining()) {
                c = cb.get();
            }
        }

        CharBuffer rest = null;

        while (cb.hasRemaining()) {
            if (c == ';') {
                rest = cb.slice();
                cb.limit(cb.position() - 1);
            } else {
                c = cb.get();
            }
        }

        cb.reset();
        _number = cb.toString();

        // _number = removeVisual( cb ); // should not remove visual at this stage
        if (rest != null) {
            String pname = null;
            StringBuilder temp = new StringBuilder();
            StringBuilder pvalue = new StringBuilder();

            while (rest.hasRemaining()) {
                c = rest.get();

                if (c == '=') {
                    pname = temp.toString();
                } else if (c == ';') {
                    if (_parameters.get(pname) != null) {
                        throw new ServletParseException(DUPLICATE_PARAM +
                            pname);
                    }

                    _parameters.put(pname, pvalue.toString());
                    pname = null;
                    temp = new StringBuilder();
                    pvalue = new StringBuilder();
                } else if (pname == null) {
                    temp.append(c);
                } else {
                    pvalue.append(c);
                }
            }

            if (_parameters.get(pname) != null) {
                throw new ServletParseException(DUPLICATE_PARAM + pname);
            }

            _parameters.put(pname, pvalue.toString());
        }

        // Some more quality checking before returning
        if ((_number == null) || (_number.length() == 0)) {
            throw new ServletParseException(NO_NUMBER);
        }

        if (!_global && (_parameters.get(PHONE_CONTEXT) == null)) {
            throw new ServletParseException(PHONE_CTX_MISSING);
        }
    }

    public String getScheme() {
        return SipFactoryImpl.TEL_URI_PROTOCOL;
    }

    public boolean isSipURI() {
        return false;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder("tel:");

        if (_global) {
            sb.append('+');
        }

        sb.append(_number);

        if (_parameters != null) {
            Iterator<String> i = _parameters.getKeys();

            while (i.hasNext()) {
                String pname = i.next();
                sb.append(';');
                sb.append(pname);
                sb.append('=');
                sb.append(_parameters.get(pname));
            }
        }

        return sb.toString();
    }
}
