/*
 * Copyright 2007 Sun Microsystems, Inc.
 *
 * This file is part of jVoiceBridge.
 *
 * jVoiceBridge is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation and distributed hereunder
 * to you.
 *
 * jVoiceBridge is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Sun designates this particular file as subject to the "Classpath"
 * exception as provided by Sun in the License file that accompanied this
 * code.
 */

package com.sun.voip.server;

import com.sun.voip.CallParticipant;
import com.sun.voip.CallState;
import com.sun.voip.Logger;
import com.sun.voip.MediaInfo;
import com.sun.voip.RtpPacket;
import com.sun.voip.SdpInfo;
import com.sun.voip.TreatmentManager;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.text.ParseException;

/**
 * Non-Signaling handles calls setup by an external source.
 */
public class NSOutgoingCallAgent extends CallSetupAgent {

    private SipUtil sipUtil;

    private CallParticipant cp;

    private boolean callAnswered = false;

    private MediaInfo mixerMediaPreference;

    public NSOutgoingCallAgent(CallHandler callHandler) {
        super(callHandler);

        cp = callHandler.getCallParticipant();

        mixerMediaPreference = callHandler.getConferenceManager().getMediaInfo();

        sipUtil = new SipUtil(mixerMediaPreference);
    }

    public void initiateCall() throws IOException {
        InetSocketAddress isa = callHandler.getReceiveAddress();

            if (isa == null) {
                throw new IOException("can't get receiver socket!");
            }

        setState(CallState.INVITED);

        TreatmentManager treatmentManager = null;

        if (cp.getInputTreatment() != null)
        {
            if (cp.getInputTreatment().length() > 0)
            {
                try {
                /*
                 * Just make sure we can open the file
                 */
                treatmentManager = new TreatmentManager(cp.getInputTreatment(),
                            RtpPacket.PCM_ENCODING, mixerMediaPreference.getSampleRate(),
                    mixerMediaPreference.getChannels());

                    treatmentManager.stopTreatment(false);
                } catch (IOException e) {
                Logger.println("Invalid input treatment:  " + cp.getInputTreatment());
                throw new IOException(
                    "Invalid input treatment:  " + cp.getInputTreatment());
                }
            }

            /*
             * This is a special call which is used to play
             * an input treatment as its input.
             * There is no remote endpoint.
             */
            try {
                setRemoteMediaInfo(treatmentManager);
            } catch (ParseException e) {
            throw new IOException(e.getMessage());
            }
        }
    }

    public String getSdp() {
        InetSocketAddress isa;

        if (cp.getInputTreatment() != null) {
            isa = new InetSocketAddress(Bridge.getPrivateHost(), 0);
        } else {
            isa = callHandler.getReceiveAddress();
        }

        return sipUtil.generateSdp(cp, isa);
    }

    public void setRemoteMediaInfo(String sdp) throws ParseException {

        if (getState() != CallState.INVITED) {
            Logger.println("Call " + cp
            + ":  NSOutgoingCallAgent:  bad state "
            + getState());
            return;
        }

        sdp = sdp.replaceAll("\\+", "\r\n");

        Logger.println("Call " + cp
            + ":  NSOutgoingCallAgent:  remote SDP:  " + sdp);

        SdpInfo sdpInfo = sipUtil.getSdpInfo(sdp, false);

        MediaInfo mediaInfo = sdpInfo.getMediaInfo();

            InetSocketAddress isa = new InetSocketAddress(
            sdpInfo.getRemoteHost(), sdpInfo.getRemotePort());

        Logger.println("Call " + cp
            + ":  NSOutgoingCallAgent:  " + mediaInfo
            + " remote " + isa);

            setEndpointAddress(isa, mediaInfo.getPayload(),
            sdpInfo.getTransmitMediaInfo().getPayload(),
            sdpInfo.getTelephoneEventPayload());

        if (callAnswered) {
            Logger.writeFile("Call " + cp
            + ":  NSOutgoingCallAgent: done remote SDP");
            return;
        }

        /*
         * The CallParticipant has answered.
         * If join confirmation is required, we remain in the
         * INVITED state.
         */
        callAnswered = true;

        if (cp.getJoinConfirmationTimeout() == 0) {
            setState(CallState.ANSWERED);
        }

        /*
         * Start treatment if any and wait for it to finish.
         * When the treatment finishes, notification will
         * be delivered to our parent which will indicate
         * we're ready for the conference.
         *
         * If there's no treatment to be played, we're ready now
         * unless we're waiting for join confirmation..
         */
        initializeCallAnsweredTreatment();

        if (callAnsweredTreatment != null) {
            startCallAnsweredTreatment();
        } else {
            if (cp.getJoinConfirmationTimeout() == 0) {
                    setState(CallState.ESTABLISHED);
            }
        }
    }

    /*
     * This is called for calls with an input treatment.
     * There is really no endpoint
     * and no data is ever sent to the call.
     * The looks like any other call but is used only as a source of sound.
     */


    public void setRemoteMediaInfo(TreatmentManager treatmentManager)
        throws ParseException {

        if (getState() != CallState.INVITED) {
            Logger.println("Call " + cp
            + ":  NSOutgoingCallAgent:  bad state "
            + getState());
            return;
        }

        MediaInfo mediaInfo = mixerMediaPreference;

        if (treatmentManager != null) {
            int sampleRate = treatmentManager.getSampleRate();
            int channels = treatmentManager.getChannels();

            try {
                mediaInfo = MediaInfo.findMediaInfo(RtpPacket.PCM_ENCODING,
                sampleRate, channels);
            } catch (IOException e) {
                Logger.println("Using conference media preference "
                + mediaInfo + ": " + e.getMessage());
            }
        }

            InetSocketAddress isa =
            callHandler.getMember().getMemberReceiver().getReceiveAddress();

        Logger.println("Call " + cp
            + ":  NSOutgoingCallAgent:  " + mediaInfo
            + " remote " + isa);

            setEndpointAddress(isa, mediaInfo.getPayload(),
            mediaInfo.getPayload(), (byte) 0);

        if (callAnswered) {
            Logger.writeFile("Call " + cp
            + ":  NSOutgoingCallAgent: done remote SDP");
            return;
        }

        /*
         * The CallParticipant has answered.
         * If join confirmation is required, we remain in the
         * INVITED state.
         */
        callAnswered = true;

        if (cp.getJoinConfirmationTimeout() == 0) {
            setState(CallState.ANSWERED);
        }

        /*
         * Start treatment if any and wait for it to finish.
         * When the treatment finishes, notification will
         * be delivered to our parent which will indicate
         * we're ready for the conference.
         *
         * If there's no treatment to be played, we're ready now
         * unless we're waiting for join confirmation..
         */
        initializeCallAnsweredTreatment();

        if (callAnsweredTreatment != null) {
            startCallAnsweredTreatment();
        } else {
            if (cp.getJoinConfirmationTimeout() == 0) {
                    setState(CallState.ESTABLISHED);
            }
        }
    }

    public void terminateCall() {
    }

}
