/* $Id*/
/*
* Copyright (C) 2016 Teluu Inc. (http://www.teluu.com)
*
* 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 2 of the License, or
* (at your option) 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#pragma once
#include <pjsua2.hpp>
#include <collection.h>

namespace VoipBackEnd
{
    typedef int TransportId;

    typedef Platform::Collections::Vector<Platform::String^> StringVector;
    typedef Windows::Foundation::Collections::IVector<Platform::String^> 
	IStringVector;

    typedef public enum class INV_STATE
    {
	PJSIP_INV_STATE_NULL,	      /**< Before INVITE is sent or received */
	PJSIP_INV_STATE_CALLING,      /**< After INVITE is sent		     */
	PJSIP_INV_STATE_INCOMING,     /**< After INVITE is received.	     */
	PJSIP_INV_STATE_EARLY,	      /**< After response with To tag.	     */
	PJSIP_INV_STATE_CONNECTING,   /**< After 2xx is sent/received.	     */
	PJSIP_INV_STATE_CONFIRMED,    /**< After ACK is sent/received.	     */
	PJSIP_INV_STATE_DISCONNECTED, /**< Session is terminated.	     */
    } INV_STATE;

    class ImpLogWriter : public pj::LogWriter
    {
    public:
	virtual void write(const pj::LogEntry &entry);
    };

    /* Account related data type. */
    public ref struct OnRegStateParamRT sealed
    {
	OnRegStateParamRT();

	property pj_status_t status
	{
	    pj_status_t get();
	};

	property unsigned code 
	{
	    unsigned get();
	};

	property Platform::String^ reason
	{
	    Platform::String^ get();
	};

	property int expiration
	{
	    int get();
	};

    internal:
	OnRegStateParamRT(const pj::OnRegStateParam& param);

    private:
	pj::OnRegStateParam* inPtr;	    

	~OnRegStateParamRT();
    };

    public ref struct CallInfoRT sealed
    {	
	property Platform::String^ localUri
	{
	    Platform::String^ get();
	}

	property Platform::String^ localContact
	{
	    Platform::String^ get();	    
	}

	property Platform::String^ remoteUri
	{
	    Platform::String^ get();
	}

	property Platform::String^ remoteContact
	{
	    Platform::String^ get();
	}

	property INV_STATE state
	{
	    INV_STATE get();
	};

    internal:
	CallInfoRT(const pj::CallInfo& info);

    private:
	pj::CallInfo* inPtr;

	~CallInfoRT();

    };

    public interface class IntAccount
    {
    public:
	virtual void onRegState(OnRegStateParamRT^ prm);
	virtual void onIncomingCall(CallInfoRT^ info);
    };

    class ImpAccount : public pj::Account
    {
    public:
	void setCallback(IntAccount^ callback);

	virtual void onIncomingCall(pj::OnIncomingCallParam& prm);
	virtual void onRegState(pj::OnRegStateParam& prm);

	~ImpAccount() {};

    private:
	IntAccount^ cb;
    };

    /* Call related data type. */
    public ref class CallOpParamRT sealed
    {
    public:
	CallOpParamRT();

	property unsigned statusCode
	{
	    unsigned get();
	    void set(unsigned val);
	}

	property Platform::String^ reason {
	    Platform::String^ get();
	    void set(Platform::String^ val);
	}

    internal:
	pj::CallOpParam* getPtr();

    private:
	pj::CallOpParam* inPtr;

	~CallOpParamRT();
    };

    public interface class IntCall
    {

    public:
	virtual void onCallState(CallInfoRT^ info);	    
    };

    class ImpCall : public pj::Call
    {	    
    public:	    	    
	ImpCall(pj::Account& account, int call_id);
	virtual ~ImpCall() {};

	void setCallback(IntCall^ callback);

	virtual void onCallState(pj::OnCallStateParam& prm);
	virtual void onCallMediaState(pj::OnCallMediaStateParam& prm);	    

    private:
	IntCall^ cb;
    };

    public ref class AccountInfo sealed
    {
    public:
	property Platform::String^ id {
	    Platform::String^ get();
	}
	property Platform::String^ registrar {
	    Platform::String^ get();
	}
	property Platform::String^ proxy {
	    Platform::String^ get();
	}
	property Platform::String^ username {
	    Platform::String^ get();
	}
	property Platform::String^ password {
	    Platform::String^ get();
	}
    internal:
	AccountInfo(pj::AccountConfig* accConfig);
    private:
	pj::AccountConfig* cfg;
    };

    /* App class. */
    public ref class MyAppRT sealed
    {
    public:
	static property MyAppRT^ Instance {
	    MyAppRT^ get();
	}

	void init(IntAccount^ iAcc, IntCall^ iCall);
	void deInit();

	/* Util */
	void writeLog(int level, Platform::String^ message);

	/* Call handling. */
	void hangupCall();
	void answerCall(CallOpParamRT^ prm);
	void makeCall(Platform::String^ dst_uri);	
	CallInfoRT^ getCallInfo();

	/* Thread handling. */
	void registerThread(Platform::String^ name);
	bool isThreadRegistered();

	/* Account handling. */
	AccountInfo^ getAccountInfo();

	void modifyAccount(Platform::String^ id, Platform::String^ registrar, 
			   Platform::String^ proxy, Platform::String^ username, 
			   Platform::String^ password);

    internal:
	pj_bool_t handleIncomingCall(int callId);

    private:
	MyAppRT();
	~MyAppRT();

	void loadConfig();
	void saveConfig();

	static MyAppRT^ singleton;

	ImpLogWriter* logWriter;
	pj::Endpoint ep;

	/* Configs */
	pj::EpConfig* epConfig;
	pj::AccountConfig* accConfig;
	pj::TransportConfig* sipTpConfig;

	std::string appDir;
	ImpAccount* account;
	ImpCall* activeCall;

	IntAccount^ intAcc;
	IntCall^ intCall;
    };
}