/**
 * Copyright (C) 2004-2011 Jive Software. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.java.sipmack.sip.simple;

import java.text.ParseException;
import java.util.ArrayList;

import javax.sip.ClientTransaction;
import javax.sip.InvalidArgumentException;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.TransactionUnavailableException;
import javax.sip.address.Address;
import javax.sip.address.URI;
import javax.sip.header.CSeqHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.header.ContentLengthHeader;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.ToHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;

import net.java.sipmack.common.Log;
import net.java.sipmack.sip.CommunicationsException;
import net.java.sipmack.sip.SIPConfig;
import net.java.sipmack.sip.SipManager;
import net.java.sipmack.sip.security.SipSecurityException;


public class MessageProcessing {
    protected SipManager sipManCallback = null;

    public MessageProcessing() {
    }

    public MessageProcessing(SipManager sipManCallback) {
        this.sipManCallback = sipManCallback;
    }

    public void setSipManagerCallBack(SipManager sipManCallback) {
        this.sipManCallback = sipManCallback;
    }

    /**
     * Attempts to re-generate the corresponding request with the proper
     * credentials and terminates the call if it fails.
     *
     * @param clientTransaction the corresponding transaction
     * @param response          the challenge
     */
    void processAuthenticationChallenge(ClientTransaction clientTransaction,
                                        Response response) {
        try {
            ClientTransaction retryTran = sipManCallback.sipSecurityManager
                    .handleChallenge(response, clientTransaction);

            retryTran.sendRequest();
        }
        catch (SipSecurityException exc) {
            sipManCallback.fireCommunicationsError(new CommunicationsException(
                    "Authorization failed!", exc));
        }
        catch (Exception exc) {
            sipManCallback.fireCommunicationsError(new CommunicationsException(
                    "Failed to resend a request "
                            + "after a security challenge!", exc));
        }
        finally {

        }
    }

    /**
     * Process MESSAGE requests and send OK response.
     *
     * @param serverTransaction
     * @param request
     */
    public void processMessageRequest(ServerTransaction serverTransaction,
                                      Request request) {
        try {

            // Send OK
            Response ok = null;
            try {
                ok = sipManCallback.messageFactory.createResponse(Response.OK,
                        request);
                // sipManCallback.attachToTag(ok,
                // serverTransaction.getDialog());
            }
            catch (ParseException ex) {
                sipManCallback
                        .fireCommunicationsError(new CommunicationsException(
                                "Failed to construct an OK response to a MESSAGE request!",
                                ex));
                return;
            }
            try {
                serverTransaction.sendResponse(ok);

            }
            catch (SipException ex) {
                // This is not really a problem according to the RFC
                // so just dump to stdout should someone be interested
            } catch (InvalidArgumentException e) {

            }
        }
        finally {

        }
    }

    public void sendKeepAlive() throws CommunicationsException {

        String to = "";
        byte[] messageBody = "".getBytes();

        try {

            to = to.trim();
            // Handle default domain name (i.e. transform 1234 -> 1234@sip.com
            String defaultDomainName = SIPConfig.getDefaultDomain();
            if (defaultDomainName != null // no sip scheme
                    && !to.trim().startsWith("tel:") && to.indexOf('@') == -1) {
                to = to + "@" + defaultDomainName;
            }

            // Let's be uri fault tolerant
            if (to.toLowerCase().indexOf("sip:") == -1 // no sip scheme
                    && to.indexOf('@') != -1 // most probably a sip uri
                    ) {
                to = "sip:" + to;
            }

            // Request URI
            URI requestURI;
            try {
                requestURI = sipManCallback.addressFactory.createURI(to);
            }
            catch (ParseException ex) {

                throw new CommunicationsException(to
                        + " is not a legal SIP uri!", ex);
            }
            // Call ID
            CallIdHeader callIdHeader = sipManCallback.sipProvider
                    .getNewCallId();
            // CSeq
            CSeqHeader cSeqHeader;
            try {
                cSeqHeader = sipManCallback.headerFactory.createCSeqHeader(1L,
                        Request.MESSAGE);
            }
            catch (Exception ex) {
                // Shouldn't happen
                throw new CommunicationsException(
                        "An unexpected erro occurred while"
                                + "constructing the CSeqHeadder", ex);
            }
            // FromHeader
            FromHeader fromHeader = sipManCallback.getFromHeader();
            // ToHeader
            Address toAddress = sipManCallback.addressFactory
                    .createAddress(requestURI);
            ToHeader toHeader;
            try {
                toHeader = sipManCallback.headerFactory.createToHeader(
                        toAddress, null);
            }
            catch (ParseException ex) {
                // Shouldn't happen
                throw new CommunicationsException(
                        "Null is not an allowed tag for the to header!", ex);
            }

            ContentLengthHeader contentLengthHeader = null;
            try {
                contentLengthHeader = sipManCallback.headerFactory
                        .createContentLengthHeader(messageBody.length);
            }
            catch (InvalidArgumentException ex) {
                throw new CommunicationsException(
                        "Cseq Header must contain a integer value!", ex);
            }

            //ExpiresHeader expiresHeader = null;
            try {
                sipManCallback.headerFactory.createExpiresHeader(30);
            }
            catch (InvalidArgumentException ex) {
                throw new CommunicationsException(
                        "Expires Header must be an integer!", ex);
            }

            String contentType = "text/plain";
            ContentTypeHeader contentTypeHeader = null;
            try {
                String[] contentTypeTab = contentType.split("/");
                contentTypeHeader = sipManCallback.headerFactory
                        .createContentTypeHeader(contentTypeTab[0],
                                contentTypeTab[1]);
            }
            catch (ParseException ex) {
                throw new CommunicationsException(
                        "ContentType Header must look like type/subtype!", ex);
            }

            // ViaHeaders
            ArrayList<ViaHeader> viaHeaders = sipManCallback.getLocalViaHeaders();
            // MaxForwards
            MaxForwardsHeader maxForwards = sipManCallback
                    .getMaxForwardsHeader();
            Request message = null;
            try {
                message = sipManCallback.messageFactory.createRequest(
                        requestURI, Request.MESSAGE, callIdHeader, cSeqHeader,
                        fromHeader, toHeader, viaHeaders, maxForwards);
                message.setContentLength(contentLengthHeader);
                message.setContent(messageBody, contentTypeHeader);
                // message.addHeader(eventHeader);
            }
            catch (Exception e) {
                Log.error("sendKeepAlive", e);
            }

            //ClientTransaction messageTransaction = null;
            //String subscriber = sipManCallback.getFromHeader().getAddress()
            //        .getURI().toString();

            try {
                sipManCallback.sipProvider.getNewClientTransaction(message);
            } catch (TransactionUnavailableException e) {
                e.printStackTrace();
            }

            try {
            	// TODO : Anpassen
                //((SIPClientTransaction) messageTransaction).sendRequest("\0".getBytes(), InetAddress.getByName(SIPConfig.getDefaultDomain()), SIPConfig.getRegistrarPort());
            }
            catch (Exception e) {
                Log.error("sendKeepAlive", e);
            }

        }
        finally {
            try {
                this.finalize();
            }
            catch (Throwable e) {
            }
        }

    }

    /**
     * Sends an instant message in pager-mode using a SIMPLE/SIP MESSAGE
     * request. In pager-mode, each message is independent of any other
     * messages. An instant message will be the body of the MESSAGE request to
     * be sent, therefore, its format must conform to the values in the
     * "Content-Type" and "Content-Encoding" header fields. Refer to Message for
     * details.
     *
     * @param to              the address of receiver.
     * @param messageBody     the message to be sent. The messageBody will be the body of
     *                        the MESSAGE request to be sent and its format must conform to
     *                        the values in the parameters contentType and contentEncoding.
     *                        Please refer to the setBody method for details.
     * @param contentType     the Internet media type of the messageBody. Please refer to
     *                        the Message.setBody method for details.
     * @param contentEncoding the encodings that have been applied to the messageBody in
     *                        addition to those specified by contentType. Please refer to
     *                        the Message.setBody method for details.
     * @return the transaction ID associated with the MESSAGE request sent by
     *         this method.
     * @throws CommunicationsException
     */
    public java.lang.String sendMessage(java.lang.String to,
                                        byte[] messageBody, java.lang.String contentType,
                                        java.lang.String contentEncoding) throws CommunicationsException {
        try {

            to = to.trim();
            // Handle default domain name (i.e. transform 1234 -> 1234@sip.com
            String defaultDomainName = SIPConfig.getDefaultDomain();

            if (defaultDomainName != null // no sip scheme
                    && !to.trim().startsWith("tel:") && to.indexOf('@') == -1 // most
                // probably
                // a
                // sip
                // uri
                    ) {
                to = to + "@" + defaultDomainName;
            }

            // Let's be uri fault tolerant
            if (to.toLowerCase().indexOf("sip:") == -1 // no sip scheme
                    && to.indexOf('@') != -1 // most probably a sip uri
                    ) {
                to = "sip:" + to;
            }

            // Request URI
            URI requestURI;
            try {
                requestURI = sipManCallback.addressFactory.createURI(to);
            }
            catch (ParseException ex) {

                throw new CommunicationsException(to
                        + " is not a legal SIP uri!", ex);
            }
            // Call ID
            CallIdHeader callIdHeader = sipManCallback.sipProvider
                    .getNewCallId();
            // CSeq
            CSeqHeader cSeqHeader;
            try {
                cSeqHeader = sipManCallback.headerFactory.createCSeqHeader(1L,
                        Request.MESSAGE);
            }
            catch (Exception ex) {
                // Shouldn't happen
                throw new CommunicationsException(
                        "An unexpected erro occurred while"
                                + "constructing the CSeqHeadder", ex);
            }
            // FromHeader
            FromHeader fromHeader = sipManCallback.getFromHeader();
            // ToHeader
            Address toAddress = sipManCallback.addressFactory
                    .createAddress(requestURI);
            ToHeader toHeader;
            try {
                toHeader = sipManCallback.headerFactory.createToHeader(
                        toAddress, null);
            }
            catch (ParseException ex) {
                // Shouldn't happen
                throw new CommunicationsException(
                        "Null is not an allowed tag for the to header!", ex);
            }

            ContentTypeHeader contentTypeHeader = null;
            try {
                String[] contentTypeTab = contentType.split("/");
                contentTypeHeader = sipManCallback.headerFactory
                        .createContentTypeHeader(contentTypeTab[0],
                                contentTypeTab[1]);
            }
            catch (ParseException ex) {
                throw new CommunicationsException(
                        "ContentType Header must look like type/subtype!", ex);
            }

            ContentLengthHeader contentLengthHeader = null;
            try {
                contentLengthHeader = sipManCallback.headerFactory
                        .createContentLengthHeader(messageBody.length);
            }
            catch (InvalidArgumentException ex) {
                throw new CommunicationsException(
                        "Cseq Header must contain a integer value!", ex);
            }

            /*
                * EventHeader eventHeader = null; try { eventHeader =
                * sipManCallback.headerFactory.createEventHeader("presence"); }
                * catch (ParseException ex) { //Shouldn't happen console.error(
                * "Unable to create event header!", ex); throw new
                * CommunicationsException( "Unable to create event header!", ex); }
                */

            //ExpiresHeader expiresHeader = null;
            try {
                sipManCallback.headerFactory.createExpiresHeader(30);
            }
            catch (InvalidArgumentException ex) {
                throw new CommunicationsException(
                        "Expires Header must be an integer!", ex);
            }

            // ViaHeaders
            ArrayList<ViaHeader> viaHeaders = sipManCallback.getLocalViaHeaders();
            // MaxForwards
            MaxForwardsHeader maxForwards = sipManCallback
                    .getMaxForwardsHeader();
            Request message = null;
            try {
                message = sipManCallback.messageFactory.createRequest(
                        requestURI, Request.MESSAGE, callIdHeader, cSeqHeader,
                        fromHeader, toHeader, viaHeaders, maxForwards);
                message.setContent(messageBody, contentTypeHeader);
                message.setContentLength(contentLengthHeader);
                // message.addHeader(eventHeader);
            }
            catch (ParseException ex) {
                throw new CommunicationsException(
                        "Failed to create message Request!", ex);
            }

            ClientTransaction messageTransaction = null;
            //String subscriber = sipManCallback.getFromHeader().getAddress()
            //        .getURI().toString();

            try {
                messageTransaction = sipManCallback.sipProvider
                        .getNewClientTransaction(message);
                //
            }
            catch (TransactionUnavailableException ex) {
                throw new CommunicationsException(
                        "Failed to create messageTransaction.", ex);
            }

            try {
                messageTransaction.sendRequest();

            }
            catch (SipException ex) {
                throw new CommunicationsException(
                        "An error occurred while sending message request", ex);
            }

            return messageTransaction.toString();

        }
        finally {

        }
    }

}
