/*
    This file is part of Peers, a java SIP softphone.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program 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/>.
    
    Copyright 2007, 2008, 2009, 2010 Yohann Martineau 
*/

package peers.sip.core.useragent;

import java.io.File;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

import lombok.Getter;
import lombok.Setter;
import peers.Config;

import peers.XmlConfig;
import peers.media.AbstractSoundManager;
import peers.media.Echo;
import peers.media.MediaManager;
import peers.media.MediaMode;
import peers.sdp.SDPManager;
import peers.sip.Utils;
import peers.sip.core.useragent.handlers.ByeHandler;
import peers.sip.core.useragent.handlers.CancelHandler;
import peers.sip.core.useragent.handlers.InviteHandler;
import peers.sip.core.useragent.handlers.OptionsHandler;
import peers.sip.core.useragent.handlers.RegisterHandler;
import peers.sip.syntaxencoding.SipURI;
import peers.sip.syntaxencoding.SipUriSyntaxException;
import peers.sip.transaction.Transaction;
import peers.sip.transaction.TransactionManager;
import peers.sip.transactionuser.Dialog;
import peers.sip.transactionuser.DialogManager;
import peers.sip.transport.SipMessage;
import peers.sip.transport.SipRequest;
import peers.sip.transport.SipResponse;
import peers.sip.transport.TransportManager;


@SuppressWarnings({"unused", "FieldMayBeFinal", "FieldCanBeLocal", "UnusedReturnValue"})
public class UserAgent {

    public final static String CONFIG_FILE = "conf" + File.separator + "peers.xml";
    public final static int RTP_DEFAULT_PORT = 8000;

    @Getter
    private String peersHome;
    @Getter
    private Config config;

    @Getter
    private List<String> peers;
    //private List<Dialog> dialogs;
    
    //TODO factorize echo and captureRtpSender
    @Setter
    @Getter
    private Echo echo;
    
    @Getter
    private UAC uac;
    @Getter
    private UAS uas;

    private ChallengeManager challengeManager;
    
    @Getter
    private DialogManager dialogManager;
    private TransactionManager transactionManager;
    @Getter
    private TransportManager transportManager;

    private int cseqCounter;
    @Getter
    private SipListener sipListener;
    
    private SDPManager sdpManager;
    @Getter
    private AbstractSoundManager soundManager;
    @Getter
    private MediaManager mediaManager;

    public UserAgent(SipListener sipListener, String peersHome,
            AbstractSoundManager soundManager)
                    throws SocketException {
        this(sipListener, null, peersHome, soundManager);
    }

    public UserAgent(SipListener sipListener, Config config,
            AbstractSoundManager soundManager)
                    throws SocketException {
        this(sipListener, config, null, soundManager);
    }

    public UserAgent(SipListener sipListener, Config config, String peersHome,
                     AbstractSoundManager soundManager)
                    throws SocketException {
        this.sipListener = sipListener;
        if (peersHome == null) {
            peersHome = Utils.DEFAULT_PEERS_HOME;
        }
        this.peersHome = peersHome;

        if (config == null) {
            config = new XmlConfig(this.peersHome + File.separator
                    + CONFIG_FILE);
        }
        this.config = config;

        cseqCounter = 1;



        //transaction user
        
        dialogManager = new DialogManager();
        
        //transaction
        
        transactionManager = new TransactionManager();
        
        //transport
        
        transportManager = new TransportManager(transactionManager,
                config);
        
        transactionManager.setTransportManager(transportManager);
        
        //core
        
        InviteHandler inviteHandler = new InviteHandler(this,
                dialogManager,
                transactionManager,
                transportManager);
        CancelHandler cancelHandler = new CancelHandler(this,
                dialogManager,
                transactionManager,
                transportManager);
        ByeHandler byeHandler = new ByeHandler(this,
                dialogManager,
                transactionManager,
                transportManager);
        OptionsHandler optionsHandler = new OptionsHandler(this,
                transactionManager,
                transportManager);
        RegisterHandler registerHandler = new RegisterHandler(this,
                transactionManager,
                transportManager);
        
        InitialRequestManager initialRequestManager =
            new InitialRequestManager(
                this,
                inviteHandler,
                cancelHandler,
                byeHandler,
                optionsHandler,
                registerHandler,
                dialogManager,
                transactionManager,
                transportManager);
        MidDialogRequestManager midDialogRequestManager =
            new MidDialogRequestManager(
                this,
                inviteHandler,
                cancelHandler,
                byeHandler,
                optionsHandler,
                registerHandler,
                dialogManager,
                transactionManager,
                transportManager);
        
        uas = new UAS(this,
                initialRequestManager,
                midDialogRequestManager,
                dialogManager,
                transactionManager,
                transportManager);

        uac = new UAC(this,
                initialRequestManager,
                midDialogRequestManager,
                dialogManager,
                transactionManager,
                transportManager);

        challengeManager = new ChallengeManager(config,
                initialRequestManager,
                midDialogRequestManager,
                dialogManager);
        registerHandler.setChallengeManager(challengeManager);
        inviteHandler.setChallengeManager(challengeManager);
        byeHandler.setChallengeManager(challengeManager);

        peers = new ArrayList<>();
        //dialogs = new ArrayList<Dialog>();

        sdpManager = new SDPManager(this);
        inviteHandler.setSdpManager(sdpManager);
        optionsHandler.setSdpManager(sdpManager);

        this.soundManager = soundManager;
        mediaManager = new MediaManager(this);
    }
    
    // client methods

    public void close() {
        transportManager.closeTransports();
        config.setPublicInetAddress(null);
    }
    
    public SipRequest register() throws SipUriSyntaxException {
        return uac.register();
    }

    public void unregister() throws SipUriSyntaxException {
        uac.unregister();
    }
    
    public SipRequest invite(String requestUri, String callId)
            throws SipUriSyntaxException {
        return uac.invite(requestUri, callId);
    }
    
    public void terminate(SipRequest sipRequest) {
        uac.terminate(sipRequest);
    }
    
    public void acceptCall(SipRequest sipRequest, Dialog dialog) {
        uas.acceptCall(sipRequest, dialog);
    }
    
    public void rejectCall(SipRequest sipRequest) {
        uas.rejectCall(sipRequest);
    }
    
    
    /**
     * Gives the sipMessage if sipMessage is a SipRequest or 
     * the SipRequest corresponding to the SipResponse
     * if sipMessage is a SipResponse
     * @param sipMessage SipMessage
     * @return null if sipMessage is neither a SipRequest neither a SipResponse
     */
    public SipRequest getSipRequest(SipMessage sipMessage) {
        if (sipMessage instanceof SipRequest) {
            return (SipRequest) sipMessage;
        } else if (sipMessage instanceof SipResponse) {
            SipResponse sipResponse = (SipResponse) sipMessage;
            Transaction transaction = (Transaction)transactionManager
                .getClientTransaction(sipResponse);
            if (transaction == null) {
                transaction = (Transaction)transactionManager
                    .getServerTransaction(sipResponse);
            }
            if (transaction == null) {
                return null;
            }
            return transaction.getRequest();
        } else {
            return null;
        }
    }

    public String generateCSeq(String method) {
        return String.valueOf(cseqCounter++) +
                ' ' +
                method;
    }
    
    public boolean isRegistered() {
        return uac.getInitialRequestManager().getRegisterHandler()
            .isRegistered();
    }

    public int getSipPort() {
        return transportManager.getSipPort();
    }

    public int getRtpPort() {
        return config.getRtpPort();
    }

    public String getDomain() {
        return config.getDomain();
    }

    public String getUserpart() {
        return config.getUserPart();
    }

    public MediaMode getMediaMode() {
        return config.getMediaMode();
    }

    public boolean isMediaDebug() {
        return config.isMediaDebug();
    }

    public SipURI getOutboundProxy() {
        return config.getOutboundProxy();
    }

}
