/**
 * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
 * This file is part of CSipSimple.
 *
 *  CSipSimple 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
 *  (at your option) any later version.
 *  If you own a pjsip commercial license you can also redistribute it
 *  and/or modify it under the terms of the GNU Lesser General Public License
 *  as an android library.
 *
 *  CSipSimple 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 CSipSimple.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.csipsimple.pjsip;

import android.content.Context;
import android.os.SystemClock;
import android.text.TextUtils;

import com.csipsimple.service.SipService.SameThreadException;
import com.csipsimple.service.impl.SipCallSessionImpl;
import com.csipsimple.utils.Log;

import org.pjsip.pjsua.pj_time_val;
import org.pjsip.pjsua.pjmedia_dir;
import org.pjsip.pjsua.pjsip_event;
import org.pjsip.pjsua.pjsip_inv_state;
import org.pjsip.pjsua.pjsua;
import org.pjsip.pjsua.pjsuaConstants;
import org.pjsip.pjsua.pjsua_call_info;
import org.pjsip.pjsua.zrtp_state_info;

/**
 * Singleton class to manage pjsip calls. It allows to convert retrieve pjsip
 * calls information and convert that into objects that can be easily managed on
 * Android side
 */
public final class PjSipCalls {

    private PjSipCalls() {
    }

    private static final String THIS_FILE = "PjSipCalls";

    /**
     * Update the call session infos
     * 
     * @param session The session to update (input/output). Must have a correct
     *            call id set
     * @param service PjSipService Sip service to retrieve pjsip accounts infos
     * @throws SameThreadException
     */
    public static void updateSessionFromPj(SipCallSessionImpl session, pjsip_event e, Context context)
            throws SameThreadException {
        Log.d(THIS_FILE, "Update call " + session.getCallId());
        pjsua_call_info pjInfo = new pjsua_call_info();
        int status = pjsua.call_get_info(session.getCallId(), pjInfo);

        if (status == pjsua.PJ_SUCCESS) {
            // Transform pjInfo into CallSession object
            updateSession(session, pjInfo, context);
            
            // Update state here because we have pjsip_event here and can get q.850 state
            if(e != null) {
                // Status code
                int status_code = pjsua.get_event_status_code(e);
                if(status_code == 0) {
                    try {
                        status_code = pjInfo.getLast_status().swigValue();
                    } catch (IllegalArgumentException err) {
                        // The status code does not exist in enum ignore it
                    }
                }
                session.setLastStatusCode(status_code);
                Log.d(THIS_FILE, "Last status code is " + status_code);
                // TODO - get comment from q.850 state as well
                String status_text = PjSipService.pjStrToString(pjInfo.getLast_status_text());
                session.setLastStatusComment(status_text);
                
                // Reason code
                int reason_code = pjsua.get_event_reason_code(e);
                if (reason_code != 0) {
                    session.setLastReasonCode(reason_code);
                }
            }
            
            // And now, about secure information
            session.setSignalisationSecure(pjsua.call_secure_sig_level(session.getCallId()));
            String secureInfo = PjSipService.pjStrToString(pjsua.call_secure_media_info(session
                    .getCallId()));
            session.setMediaSecureInfo(secureInfo);
            session.setMediaSecure(!TextUtils.isEmpty(secureInfo));
            zrtp_state_info zrtpInfo = pjsua.jzrtp_getInfoFromCall(session.getCallId());
            session.setZrtpSASVerified(zrtpInfo.getSas_verified() == pjsuaConstants.PJ_TRUE);
            session.setHasZrtp(zrtpInfo.getSecure() == pjsuaConstants.PJ_TRUE);

            // About video info
            int vidStreamIdx = pjsua.call_get_vid_stream_idx(session.getCallId());
            if(vidStreamIdx >= 0) {
                 int hasVid = pjsua.call_vid_stream_is_running(session.getCallId(), vidStreamIdx, pjmedia_dir.PJMEDIA_DIR_DECODING);
                 session.setMediaHasVideo((hasVid == pjsuaConstants.PJ_TRUE));
            }
            

        } else {
            Log.d(THIS_FILE,
                    "Call info from does not exists in stack anymore - assume it has been disconnected");
            session.setCallState(pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED.swigValue());
        }
    }

    /**
     * Copy infos from pjsua call info object to SipCallSession object
     * 
     * @param session the session to copy info to (output)
     * @param pjCallInfo the call info from pjsip
     * @param service PjSipService Sip service to retrieve pjsip accounts infos
     */
    private static void updateSession(SipCallSessionImpl session, pjsua_call_info pjCallInfo,
            Context context) {
        // Should be unecessary cause we usually copy infos from a valid
        session.setCallId(pjCallInfo.getId());

        // Nothing to think about here cause we have a
        // bijection between int / state
        session.setCallState(pjCallInfo.getState().swigValue());
        session.setMediaStatus(pjCallInfo.getMedia_status().swigValue());
        session.setRemoteContact(PjSipService.pjStrToString(pjCallInfo.getRemote_info()));
        session.setConfPort(pjCallInfo.getConf_slot());

        // Try to retrieve sip account related to this call
        int pjAccId = pjCallInfo.getAcc_id();
        session.setAccId(PjSipService.getAccountIdForPjsipId(context, pjAccId));

        pj_time_val duration = pjCallInfo.getConnect_duration();
        session.setConnectStart(SystemClock.elapsedRealtime() - duration.getSec() * 1000
                - duration.getMsec());
    }

    /**
     * Get infos for this pjsip call
     * 
     * @param callId pjsip call id
     * @return Serialized information about this call
     * @throws SameThreadException
     */
    public static String dumpCallInfo(int callId) throws SameThreadException {
        return PjSipService.pjStrToString(pjsua.call_dump(callId, pjsua.PJ_TRUE, " "));
    }

}