/*
 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 * Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
 *
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 *
 * The Initial Developer of the Original Code is
 * Anthony Minessale II <anthmct@yahoo.com>
 * Portions created by the Initial Developer are Copyright (C)
 * the Initial Developer. All Rights Reserved.
 *
 * This module (mod_skypiax) has been contributed by:
 *
 * Giovanni Maruzzelli (gmaruzz@gmail.com)
 *
 *
 * Further Contributors:
 *
 *
 *
 * mod_skypiax.c -- Skype compatible Endpoint Module
 *
 */

#include "skypiax.h"

#ifdef WIN32
/***************/
// from http://www.openasthra.com/c-tidbits/gettimeofday-function-for-windows/

#include <time.h>

#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
#define DELTA_EPOCH_IN_MICROSECS  11644473600000000Ui64
#else /*  */
#define DELTA_EPOCH_IN_MICROSECS  11644473600000000ULL
#endif /*  */
struct sk_timezone {
	int tz_minuteswest;			/* minutes W of Greenwich */
	int tz_dsttime;				/* type of dst correction */
};
int gettimeofday(struct timeval *tv, struct sk_timezone *tz)
{
	FILETIME ft;
	unsigned __int64 tmpres = 0;
	static int tzflag;
	if (NULL != tv) {
		GetSystemTimeAsFileTime(&ft);
		tmpres |= ft.dwHighDateTime;
		tmpres <<= 32;
		tmpres |= ft.dwLowDateTime;

		/*converting file time to unix epoch */
		tmpres /= 10;			/*convert into microseconds */
		tmpres -= DELTA_EPOCH_IN_MICROSECS;
		tv->tv_sec = (long) (tmpres / 1000000UL);
		tv->tv_usec = (long) (tmpres % 1000000UL);
	}
	if (NULL != tz) {
		if (!tzflag) {
			_tzset();
			tzflag++;
		}
		tz->tz_minuteswest = _timezone / 60;
		tz->tz_dsttime = _daylight;
	}
	return 0;
}

/***************/
#endif /* WIN32 */
SWITCH_MODULE_LOAD_FUNCTION(mod_skypiax_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skypiax_shutdown);
SWITCH_MODULE_DEFINITION(mod_skypiax, mod_skypiax_load, mod_skypiax_shutdown, NULL);
SWITCH_STANDARD_API(sk_function);
/* BEGIN: Changes here */
#define SK_SYNTAX "list [full] || console || skype_API_msg || remove < skypeusername | #interface_name | #interface_id > || reload"
/* END: Changes heres */
SWITCH_STANDARD_API(skypiax_function);
#define SKYPIAX_SYNTAX "interface_name skype_API_msg"

/* BEGIN: Changes here */
#define FULL_RELOAD 0
#define SOFT_RELOAD 1
/* END: Changes heres */

static struct {
	int debug;
	char *ip;
	int port;
	char *dialplan;
	char *destination;
	char *context;
	char *codec_string;
	char *codec_order[SWITCH_MAX_CODECS];
	int codec_order_last;
	char *codec_rates_string;
	char *codec_rates[SWITCH_MAX_CODECS];
	int codec_rates_last;
	unsigned int flags;
	int fd;
	int calls;
	int real_interfaces;
	int next_interface;
	char hold_music[256];
	private_t SKYPIAX_INTERFACES[SKYPIAX_MAX_INTERFACES];
	switch_mutex_t *mutex;
	private_t *sk_console;
} globals;

switch_endpoint_interface_t *skypiax_endpoint_interface;
switch_memory_pool_t *skypiax_module_pool = NULL;
int running = 0;

SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, globals.dialplan);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_context, globals.context);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_destination, globals.destination);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_string, globals.codec_string);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_rates_string, globals.codec_rates_string);

/* BEGIN: Changes here */
static switch_status_t interface_exists(char *the_interface);
static switch_status_t remove_interface(char *the_interface);
/* END: Changes here */

static switch_status_t channel_on_init(switch_core_session_t *session);
static switch_status_t channel_on_hangup(switch_core_session_t *session);
static switch_status_t channel_on_destroy(switch_core_session_t *session);
static switch_status_t channel_on_routing(switch_core_session_t *session);
static switch_status_t channel_on_exchange_media(switch_core_session_t *session);
static switch_status_t channel_on_soft_execute(switch_core_session_t *session);
static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session,
													switch_event_t *var_event,
													switch_caller_profile_t *outbound_profile,
													switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags);
static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id);
static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id);
static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig);

static switch_status_t skypiax_codec(private_t * tech_pvt, int sample_rate, int codec_ms)
{
	switch_core_session_t *session = NULL;

	if (switch_core_codec_init
		(&tech_pvt->read_codec, "L16", NULL, sample_rate, codec_ms, 1,
		 SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, NULL) != SWITCH_STATUS_SUCCESS) {
		ERRORA("Can't load codec?\n", SKYPIAX_P_LOG);
		return SWITCH_STATUS_FALSE;
	}

	if (switch_core_codec_init
		(&tech_pvt->write_codec, "L16", NULL, sample_rate, codec_ms, 1,
		 SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, NULL) != SWITCH_STATUS_SUCCESS) {
		ERRORA("Can't load codec?\n", SKYPIAX_P_LOG);
		switch_core_codec_destroy(&tech_pvt->read_codec);
		return SWITCH_STATUS_FALSE;
	}

	tech_pvt->read_frame.rate = sample_rate;
	tech_pvt->read_frame.codec = &tech_pvt->read_codec;

	session = switch_core_session_locate(tech_pvt->session_uuid_str);

	switch_core_session_set_read_codec(session, &tech_pvt->read_codec);
	switch_core_session_set_write_codec(session, &tech_pvt->write_codec);

	switch_core_session_rwunlock(session);

	return SWITCH_STATUS_SUCCESS;

}

void skypiax_tech_init(private_t * tech_pvt, switch_core_session_t *session)
{

	tech_pvt->read_frame.data = tech_pvt->databuf;
	tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf);
	switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
	switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
	switch_core_session_set_private(session, tech_pvt);
	switch_copy_string(tech_pvt->session_uuid_str, switch_core_session_get_uuid(session), sizeof(tech_pvt->session_uuid_str));
	if (skypiax_codec(tech_pvt, SAMPLERATE_SKYPIAX, 20) != SWITCH_STATUS_SUCCESS) {
		ERRORA("skypiax_codec FAILED\n", SKYPIAX_P_LOG);
	} else {
		DEBUGA_SKYPE("skypiax_codec SUCCESS\n", SKYPIAX_P_LOG);
	}

}

/* BEGIN: Changes here */
static switch_status_t interface_exists(char *the_interface)
{
	int i;
	int interface_id;

	if (*the_interface == '#') {	/* look by interface id or interface name */
		the_interface++;
		switch_assert(the_interface);
		interface_id = atoi(the_interface);

		/* take a number as interface id */
		if (interface_id > 0 || (interface_id == 0 && strcmp(the_interface, "0") == 0)) {
			if (strlen(globals.SKYPIAX_INTERFACES[interface_id].name)) {
				return SWITCH_STATUS_SUCCESS;
			}
		} else {
			/* interface name */
			for (interface_id = 0; interface_id < SKYPIAX_MAX_INTERFACES; interface_id++) {
				if (strcmp(globals.SKYPIAX_INTERFACES[interface_id].name, the_interface) == 0) {
					return SWITCH_STATUS_SUCCESS;
					break;
				}
			}
		}
	} else {					/* look by skype_user */


		for (i = 0; i < SKYPIAX_MAX_INTERFACES; i++) {
			if (strlen(globals.SKYPIAX_INTERFACES[i].skype_user)) {
				if (strcmp(globals.SKYPIAX_INTERFACES[i].skype_user, the_interface) == 0) {
					return SWITCH_STATUS_SUCCESS;
				}
			}
		}
	}
	return SWITCH_STATUS_FALSE;
}

static switch_status_t remove_interface(char *the_interface)
{
	int x = 10;
	unsigned int howmany = 8;
	int interface_id = -1;
	private_t *tech_pvt = NULL;
	switch_status_t status;

	//running = 0;


	if (*the_interface == '#') {	/* remove by interface id or interface name */
		the_interface++;
		switch_assert(the_interface);
		interface_id = atoi(the_interface);

		if (interface_id > 0 || (interface_id == 0 && strcmp(the_interface, "0") == 0)) {
			/* take a number as interface id */
			tech_pvt = &globals.SKYPIAX_INTERFACES[interface_id];
		} else {

			for (interface_id = 0; interface_id < SKYPIAX_MAX_INTERFACES; interface_id++) {
				if (strcmp(globals.SKYPIAX_INTERFACES[interface_id].name, the_interface) == 0) {
					tech_pvt = &globals.SKYPIAX_INTERFACES[interface_id];
					break;
				}
			}
		}
	} else {					/* remove by skype_user */
		for (interface_id = 0; interface_id < SKYPIAX_MAX_INTERFACES; interface_id++) {
			if (strcmp(globals.SKYPIAX_INTERFACES[interface_id].skype_user, the_interface) == 0) {
				tech_pvt = &globals.SKYPIAX_INTERFACES[interface_id];
				break;
			}
		}
	}

	if (!tech_pvt) {
		DEBUGA_SKYPE("interface '%s' does not exist\n", SKYPIAX_P_LOG, the_interface);
		goto end;
	}

	if (strlen(globals.SKYPIAX_INTERFACES[interface_id].session_uuid_str)) {
		DEBUGA_SKYPE("interface '%s' is busy\n", SKYPIAX_P_LOG, the_interface);
		goto end;
	}

	globals.SKYPIAX_INTERFACES[interface_id].running = 0;

	if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_signaling_thread) {
#ifdef WIN32
		switch_file_write(tech_pvt->SkypiaxHandles.fdesc[1], "sciutati", &howmany);	// let's the controldev_thread die
#else /* WIN32 */
		howmany = write(tech_pvt->SkypiaxHandles.fdesc[1], "sciutati", howmany);
#endif /* WIN32 */
	}

	if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread) {
#ifdef WIN32
		if (SendMessage(tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle, WM_DESTROY, 0, 0) == FALSE) {	// let's the skypiax_api_thread_func die
			DEBUGA_SKYPE("got FALSE here, thread probably was already dead. GetLastError returned: %d\n", SKYPIAX_P_LOG, GetLastError());
			globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread = NULL;
		}
#else
		XEvent e;
		Atom atom1 = XInternAtom(tech_pvt->SkypiaxHandles.disp, "SKYPECONTROLAPI_MESSAGE_BEGIN", False);
		memset(&e, 0, sizeof(e));
		e.xclient.type = ClientMessage;
		e.xclient.message_type = atom1;	/*  leading message */
		e.xclient.display = tech_pvt->SkypiaxHandles.disp;
		e.xclient.window = tech_pvt->SkypiaxHandles.skype_win;
		e.xclient.format = 8;

		XSendEvent(tech_pvt->SkypiaxHandles.disp, tech_pvt->SkypiaxHandles.win, False, 0, &e);
		XSync(tech_pvt->SkypiaxHandles.disp, False);
#endif
	}

	while (x) {
		x--;
		switch_yield(50000);
	}

	if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_signaling_thread) {
		switch_thread_join(&status, globals.SKYPIAX_INTERFACES[interface_id].skypiax_signaling_thread);
	}

	if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread) {
		switch_thread_join(&status, globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread);
	}

	switch_mutex_lock(globals.mutex);
	if (globals.sk_console == &globals.SKYPIAX_INTERFACES[interface_id]) {
		DEBUGA_SKYPE("interface '%s' no more console\n", SKYPIAX_P_LOG, the_interface);
		globals.sk_console = NULL;
	} else {
		DEBUGA_SKYPE("interface '%s' STILL console\n", SKYPIAX_P_LOG, the_interface);
	}
	memset(&globals.SKYPIAX_INTERFACES[interface_id], '\0', sizeof(private_t));
	globals.real_interfaces--;
	switch_mutex_unlock(globals.mutex);

	DEBUGA_SKYPE("interface '%s' deleted successfully\n", SKYPIAX_P_LOG, the_interface);
	globals.SKYPIAX_INTERFACES[interface_id].running = 1;
  end:
	//running = 1;
	return SWITCH_STATUS_SUCCESS;
}

/* END: Changes here */

/* 
   State methods they get called when the state changes to the specific state 
   returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next
   so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it.
*/
static switch_status_t channel_on_init(switch_core_session_t *session)
{
	switch_channel_t *channel;
	private_t *tech_pvt = NULL;

	tech_pvt = switch_core_session_get_private(session);
	switch_assert(tech_pvt != NULL);

	channel = switch_core_session_get_channel(session);
	switch_assert(channel != NULL);
	switch_set_flag_locked(tech_pvt, TFLAG_IO);

	/* Move channel's state machine to ROUTING. This means the call is trying
	   to get from the initial start where the call because, to the point
	   where a destination has been identified. If the channel is simply
	   left in the initial state, nothing will happen. */
	switch_channel_set_state(channel, CS_ROUTING);
	switch_mutex_lock(globals.mutex);
	globals.calls++;

	switch_mutex_unlock(globals.mutex);

	DEBUGA_SKYPE("%s CHANNEL INIT\n", SKYPIAX_P_LOG, switch_channel_get_name(channel));
	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t channel_on_destroy(switch_core_session_t *session)
{
	private_t *tech_pvt = NULL;

	tech_pvt = switch_core_session_get_private(session);

	if (tech_pvt) {
		if (switch_core_codec_ready(&tech_pvt->read_codec)) {
			switch_core_codec_destroy(&tech_pvt->read_codec);
		}

		if (switch_core_codec_ready(&tech_pvt->write_codec)) {
			switch_core_codec_destroy(&tech_pvt->write_codec);
		}
		switch_core_session_set_private(session, NULL);
	}

	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t channel_on_hangup(switch_core_session_t *session)
{
	switch_channel_t *channel = NULL;
	private_t *tech_pvt = NULL;
	char msg_to_skype[256];

	channel = switch_core_session_get_channel(session);
	switch_assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	switch_assert(tech_pvt != NULL);

	switch_clear_flag_locked(tech_pvt, TFLAG_IO);
	switch_clear_flag_locked(tech_pvt, TFLAG_VOICE);
	//switch_set_flag_locked(tech_pvt, TFLAG_HANGUP);

	if (strlen(tech_pvt->skype_call_id)) {
		//switch_thread_cond_signal(tech_pvt->cond);
		DEBUGA_SKYPE("hanging up skype call: %s\n", SKYPIAX_P_LOG, tech_pvt->skype_call_id);
		sprintf(msg_to_skype, "ALTER CALL %s HANGUP", tech_pvt->skype_call_id);
		skypiax_signaling_write(tech_pvt, msg_to_skype);
	}
	//memset(tech_pvt->session_uuid_str, '\0', sizeof(tech_pvt->session_uuid_str));
	*tech_pvt->session_uuid_str = '\0';
	DEBUGA_SKYPE("%s CHANNEL HANGUP\n", SKYPIAX_P_LOG, switch_channel_get_name(channel));
	switch_mutex_lock(globals.mutex);
	globals.calls--;
	if (globals.calls < 0) {
		globals.calls = 0;
	}
	switch_mutex_unlock(globals.mutex);

	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t channel_on_routing(switch_core_session_t *session)
{
	switch_channel_t *channel = NULL;
	private_t *tech_pvt = NULL;

	channel = switch_core_session_get_channel(session);
	switch_assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	switch_assert(tech_pvt != NULL);

	DEBUGA_SKYPE("%s CHANNEL ROUTING\n", SKYPIAX_P_LOG, switch_channel_get_name(channel));

	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t channel_on_execute(switch_core_session_t *session)
{

	switch_channel_t *channel = NULL;
	private_t *tech_pvt = NULL;

	channel = switch_core_session_get_channel(session);
	switch_assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	switch_assert(tech_pvt != NULL);

	DEBUGA_SKYPE("%s CHANNEL EXECUTE\n", SKYPIAX_P_LOG, switch_channel_get_name(channel));

	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig)
{
	switch_channel_t *channel = NULL;
	private_t *tech_pvt = NULL;

	channel = switch_core_session_get_channel(session);
	switch_assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	switch_assert(tech_pvt != NULL);

	switch (sig) {
	case SWITCH_SIG_KILL:
		DEBUGA_SKYPE("%s CHANNEL got SWITCH_SIG_KILL\n", SKYPIAX_P_LOG, switch_channel_get_name(channel));
		switch_clear_flag_locked(tech_pvt, TFLAG_IO);
		switch_clear_flag_locked(tech_pvt, TFLAG_VOICE);
		switch_set_flag_locked(tech_pvt, TFLAG_HANGUP);
		break;
	case SWITCH_SIG_BREAK:
		DEBUGA_SKYPE("%s CHANNEL got SWITCH_SIG_BREAK\n", SKYPIAX_P_LOG, switch_channel_get_name(channel));
		switch_set_flag_locked(tech_pvt, TFLAG_BREAK);
		break;
	default:
		break;
	}

	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t channel_on_exchange_media(switch_core_session_t *session)
{
	private_t *tech_pvt = NULL;
	DEBUGA_SKYPE("CHANNEL LOOPBACK\n", SKYPIAX_P_LOG);
	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t channel_on_soft_execute(switch_core_session_t *session)
{
	private_t *tech_pvt = NULL;
	DEBUGA_SKYPE("CHANNEL TRANSMIT\n", SKYPIAX_P_LOG);
	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf)
{
	private_t *tech_pvt = switch_core_session_get_private(session);
	switch_assert(tech_pvt != NULL);

	DEBUGA_SKYPE("DTMF: %c\n", SKYPIAX_P_LOG, dtmf->digit);

	skypiax_senddigit(tech_pvt, dtmf->digit);

	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id)
{
	switch_channel_t *channel = NULL;
	private_t *tech_pvt = NULL;
	switch_byte_t *data;

	channel = switch_core_session_get_channel(session);
	switch_assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	switch_assert(tech_pvt != NULL);

	if (!switch_channel_ready(channel) || !switch_test_flag(tech_pvt, TFLAG_IO)) {
		ERRORA("CIAPA \n", SKYPIAX_P_LOG);
		return SWITCH_STATUS_FALSE;
	}

	tech_pvt->read_frame.flags = SFF_NONE;
	*frame = NULL;

	if (!skypiax_audio_read(tech_pvt)) {

		ERRORA("skypiax_audio_read ERROR\n", SKYPIAX_P_LOG);

	} else {
		switch_set_flag_locked(tech_pvt, TFLAG_VOICE);
	}

	while (switch_test_flag(tech_pvt, TFLAG_IO)) {
		if (switch_test_flag(tech_pvt, TFLAG_BREAK)) {
			switch_clear_flag(tech_pvt, TFLAG_BREAK);
			DEBUGA_SKYPE("CHANNEL READ FRAME goto CNG\n", SKYPIAX_P_LOG);
			goto cng;
		}

		if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
			DEBUGA_SKYPE("CHANNEL READ FRAME not IO\n", SKYPIAX_P_LOG);
			return SWITCH_STATUS_FALSE;
		}

		if (switch_test_flag(tech_pvt, TFLAG_IO) && switch_test_flag(tech_pvt, TFLAG_VOICE)) {
			switch_clear_flag_locked(tech_pvt, TFLAG_VOICE);
			if (!tech_pvt->read_frame.datalen) {
				DEBUGA_SKYPE("CHANNEL READ CONTINUE\n", SKYPIAX_P_LOG);
				continue;
			}
			*frame = &tech_pvt->read_frame;
#ifdef BIGENDIAN
			if (switch_test_flag(tech_pvt, TFLAG_LINEAR)) {
				switch_swap_linear((*frame)->data, (int) (*frame)->datalen / 2);
			}
#endif
			return SWITCH_STATUS_SUCCESS;
		}

		DEBUGA_SKYPE("CHANNEL READ no TFLAG_IO\n", SKYPIAX_P_LOG);
		return SWITCH_STATUS_FALSE;

	}

	DEBUGA_SKYPE("CHANNEL READ FALSE\n", SKYPIAX_P_LOG);
	return SWITCH_STATUS_FALSE;

  cng:
	data = (switch_byte_t *) tech_pvt->read_frame.data;
	data[0] = 65;
	data[1] = 0;
	tech_pvt->read_frame.datalen = 2;
	tech_pvt->read_frame.flags = SFF_CNG;
	*frame = &tech_pvt->read_frame;
	return SWITCH_STATUS_SUCCESS;

}

static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
{
	switch_channel_t *channel = NULL;
	private_t *tech_pvt = NULL;
	unsigned int sent;

	channel = switch_core_session_get_channel(session);
	switch_assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	switch_assert(tech_pvt != NULL);

	if (!switch_channel_ready(channel) || !switch_test_flag(tech_pvt, TFLAG_IO)) {
		ERRORA("CIAPA \n", SKYPIAX_P_LOG);
		return SWITCH_STATUS_FALSE;
	}
#ifdef BIGENDIAN
	if (switch_test_flag(tech_pvt, TFLAG_LINEAR)) {
		switch_swap_linear(frame->data, (int) frame->datalen / 2);
	}
#endif

	sent = frame->datalen;
#ifdef WIN32
	switch_file_write(tech_pvt->audioskypepipe[1], frame->data, &sent);
#else /* WIN32 */
	sent = write(tech_pvt->audioskypepipe[1], frame->data, sent);
#endif /* WIN32 */
	if (sent != frame->datalen && sent != -1) {
		DEBUGA_SKYPE("CLI PIPE write %d\n", SKYPIAX_P_LOG, sent);
	}

	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t channel_answer_channel(switch_core_session_t *session)
{
	private_t *tech_pvt;
	switch_channel_t *channel = NULL;

	channel = switch_core_session_get_channel(session);
	switch_assert(channel != NULL);

	tech_pvt = switch_core_session_get_private(session);
	switch_assert(tech_pvt != NULL);

	DEBUGA_SKYPE("ANSWERED! \n", SKYPIAX_P_LOG);

	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg)
{
	switch_channel_t *channel;
	private_t *tech_pvt;

	channel = switch_core_session_get_channel(session);
	switch_assert(channel != NULL);

	tech_pvt = (private_t *) switch_core_session_get_private(session);
	switch_assert(tech_pvt != NULL);

	switch (msg->message_id) {
	case SWITCH_MESSAGE_INDICATE_ANSWER:
		{
			DEBUGA_SKYPE("MSG_ID=%d, TO BE ANSWERED!\n", SKYPIAX_P_LOG, msg->message_id);
			channel_answer_channel(session);
		}
		break;
	default:
		{
			DEBUGA_SKYPE("MSG_ID=%d\n", SKYPIAX_P_LOG, msg->message_id);
		}
		break;
	}

	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t channel_receive_event(switch_core_session_t *session, switch_event_t *event)
{
	struct private_object *tech_pvt = switch_core_session_get_private(session);
	char *body = switch_event_get_body(event);
	switch_assert(tech_pvt != NULL);

	if (!body) {
		body = "";
	}

	WARNINGA("event: |||%s|||\n", SKYPIAX_P_LOG, body);

	return SWITCH_STATUS_SUCCESS;
}

switch_state_handler_table_t skypiax_state_handlers = {
	/*.on_init */ channel_on_init,
	/*.on_routing */ channel_on_routing,
	/*.on_execute */ channel_on_execute,
	/*.on_hangup */ channel_on_hangup,
	/*.on_exchange_media */ channel_on_exchange_media,
	/*.on_soft_execute */ channel_on_soft_execute,
	/*.on_consume_media */ NULL,
	/*.on_hibernate */ NULL,
	/*.on_reset */ NULL,
	/*.on_park */ NULL,
	/*.on_reporting */ NULL,
	/*.on_destroy */ channel_on_destroy
};

switch_io_routines_t skypiax_io_routines = {
	/*.outgoing_channel */ channel_outgoing_channel,
	/*.read_frame */ channel_read_frame,
	/*.write_frame */ channel_write_frame,
	/*.kill_channel */ channel_kill_channel,
	/*.send_dtmf */ channel_send_dtmf,
	/*.receive_message */ channel_receive_message,
	/*.receive_event */ channel_receive_event
};

static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session,
													switch_event_t *var_event,
													switch_caller_profile_t *outbound_profile,
													switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags)
{
	if ((*new_session = switch_core_session_request(skypiax_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, pool)) != 0) {
		private_t *tech_pvt = NULL;
		switch_channel_t *channel;
		switch_caller_profile_t *caller_profile;
		char *rdest;
		int found = 0;
		char interface_name[256];

		switch_core_session_add_stream(*new_session, NULL);


		if (!switch_strlen_zero(outbound_profile->destination_number)) {
			int i;
			char *slash;

			switch_copy_string(interface_name, outbound_profile->destination_number, 255);
			slash = strrchr(interface_name, '/');
			*slash = '\0';

			switch_mutex_lock(globals.mutex);
			if (strncmp("ANY", interface_name, strlen(interface_name)) == 0) {
				/* we've been asked for the "ANY" interface, let's find the first idle interface */
				DEBUGA_SKYPE("Finding one available skype interface\n", SKYPIAX_P_LOG);
				tech_pvt = find_available_skypiax_interface(NULL);
				if (tech_pvt)
					found = 1;
			} else if (strncmp("RR", interface_name, strlen(interface_name)) == 0) {
				/* Find the first idle interface using Round Robin */
				DEBUGA_SKYPE("Finding one available skype interface RR\n", SKYPIAX_P_LOG);
				tech_pvt = find_available_skypiax_interface_rr();
				if (tech_pvt)
					found = 1;
			}

			for (i = 0; !found && i < SKYPIAX_MAX_INTERFACES; i++) {
				/* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */
				if (strlen(globals.SKYPIAX_INTERFACES[i].name)
					&& (strncmp(globals.SKYPIAX_INTERFACES[i].name, interface_name, strlen(interface_name)) == 0)) {
					if (strlen(globals.SKYPIAX_INTERFACES[i].session_uuid_str)) {
						DEBUGA_SKYPE
							("globals.SKYPIAX_INTERFACES[%d].name=|||%s||| session_uuid_str=|||%s||| is BUSY\n",
							 SKYPIAX_P_LOG, i, globals.SKYPIAX_INTERFACES[i].name, globals.SKYPIAX_INTERFACES[i].session_uuid_str);
						switch_core_session_destroy(new_session);
						switch_mutex_unlock(globals.mutex);
						return SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE;
					}

					DEBUGA_SKYPE("globals.SKYPIAX_INTERFACES[%d].name=|||%s|||?\n", SKYPIAX_P_LOG, i, globals.SKYPIAX_INTERFACES[i].name);
					tech_pvt = &globals.SKYPIAX_INTERFACES[i];
					found = 1;
					break;
				}

			}

		} else {
			ERRORA("Doh! no destination number?\n", SKYPIAX_P_LOG);
			switch_core_session_destroy(new_session);
			return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
		}

		if (!found) {
			ERRORA("Doh! no matching interface for |||%s|||?\n", SKYPIAX_P_LOG, interface_name);
			switch_core_session_destroy(new_session);
			switch_mutex_unlock(globals.mutex);
			return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
		}

		channel = switch_core_session_get_channel(*new_session);
		skypiax_tech_init(tech_pvt, *new_session);


		if (outbound_profile) {
			char name[128];

			snprintf(name, sizeof(name), "skypiax/%s", outbound_profile->destination_number);
			//snprintf(name, sizeof(name), "skypiax/%s", tech_pvt->name);
			switch_channel_set_name(channel, name);
			caller_profile = switch_caller_profile_clone(*new_session, outbound_profile);
			switch_channel_set_caller_profile(channel, caller_profile);
			tech_pvt->caller_profile = caller_profile;
		} else {
			ERRORA("Doh! no caller profile\n", SKYPIAX_P_LOG);
			switch_core_session_destroy(new_session);
			switch_mutex_unlock(globals.mutex);
			return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
		}

		rdest = strchr(caller_profile->destination_number, '/');
		*rdest++ = '\0';

		skypiax_call(tech_pvt, rdest, 30);

		switch_copy_string(tech_pvt->session_uuid_str, switch_core_session_get_uuid(*new_session), sizeof(tech_pvt->session_uuid_str));
		caller_profile = tech_pvt->caller_profile;
		caller_profile->destination_number = rdest;

		switch_channel_set_flag(channel, CF_OUTBOUND);
		switch_set_flag_locked(tech_pvt, TFLAG_OUTBOUND);
		switch_channel_set_state(channel, CS_INIT);
		switch_mutex_unlock(globals.mutex);
		return SWITCH_CAUSE_SUCCESS;
	}

	return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
}

/*!
 * \brief This thread runs during a call, and monitor the interface for signaling, like hangup, caller id, etc most of signaling is handled inside the skypiax_signaling_read function
 *
 */
static void *SWITCH_THREAD_FUNC skypiax_signaling_thread_func(switch_thread_t * thread, void *obj)
{
	private_t *tech_pvt = obj;
	int res;
	int forever = 1;

	if (!tech_pvt)
		return NULL;

	DEBUGA_SKYPE("In skypiax_signaling_thread_func: started, p=%p\n", SKYPIAX_P_LOG, (void *) tech_pvt);

	while (forever) {
		if (!(running && tech_pvt->running))
			break;
		res = skypiax_signaling_read(tech_pvt);
		if (res == CALLFLOW_INCOMING_HANGUP) {
			switch_core_session_t *session = NULL;
			//private_t *tech_pvt = NULL;
			switch_channel_t *channel = NULL;

			DEBUGA_SKYPE("skype call ended\n", SKYPIAX_P_LOG);

			if (tech_pvt) {
				session = switch_core_session_locate(tech_pvt->session_uuid_str);

				if (session) {
					channel = switch_core_session_get_channel(session);
					if (channel) {
						switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
						switch_core_session_rwunlock(session);
					} else {
						ERRORA("no channel?\n", SKYPIAX_P_LOG);
						switch_core_session_rwunlock(session);
					}
				} else {
					DEBUGA_SKYPE("no session\n", SKYPIAX_P_LOG);
				}
				tech_pvt->interface_state = SKYPIAX_STATE_DOWN;
				*tech_pvt->session_uuid_str = '\0';
				//memset(tech_pvt->session_uuid_str, '\0', sizeof(tech_pvt->session_uuid_str));
			} else {
				ERRORA("no tech_pvt?\n", SKYPIAX_P_LOG);
			}
		}
	}
	return NULL;
}

/* BEGIN: Changes heres */
static switch_status_t load_config(int reload_type)
/* END: Changes heres */
{
	char *cf = "skypiax.conf";
	switch_xml_t cfg, xml, global_settings, param, interfaces, myinterface;
	private_t *tech_pvt = NULL;

	switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, skypiax_module_pool);
	if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
		ERRORA("open of %s failed\n", SKYPIAX_P_LOG, cf);
		running = 0;
		switch_xml_free(xml);
		return SWITCH_STATUS_TERM;
	}

	switch_mutex_lock(globals.mutex);
	if ((global_settings = switch_xml_child(cfg, "global_settings"))) {
		for (param = switch_xml_child(global_settings, "param"); param; param = param->next) {
			char *var = (char *) switch_xml_attr_soft(param, "name");
			char *val = (char *) switch_xml_attr_soft(param, "value");

			if (!strcasecmp(var, "debug")) {
				DEBUGA_SKYPE("globals.debug=%d\n", SKYPIAX_P_LOG, globals.debug);
				globals.debug = atoi(val);
				DEBUGA_SKYPE("globals.debug=%d\n", SKYPIAX_P_LOG, globals.debug);
			} else if (!strcasecmp(var, "hold-music")) {
				switch_set_string(globals.hold_music, val);
				DEBUGA_SKYPE("globals.hold_music=%s\n", SKYPIAX_P_LOG, globals.hold_music);
			} else if (!strcmp(var, "port")) {
				globals.port = atoi(val);
				DEBUGA_SKYPE("globals.port=%d\n", SKYPIAX_P_LOG, globals.port);
			} else if (!strcmp(var, "codec-master")) {
				if (!strcasecmp(val, "us")) {
					switch_set_flag(&globals, GFLAG_MY_CODEC_PREFS);
				}
				DEBUGA_SKYPE("codec-master globals.debug=%d\n", SKYPIAX_P_LOG, globals.debug);
			} else if (!strcmp(var, "dialplan")) {
				set_global_dialplan(val);
				DEBUGA_SKYPE("globals.dialplan=%s\n", SKYPIAX_P_LOG, globals.dialplan);
			} else if (!strcmp(var, "destination")) {
				set_global_destination(val);
				DEBUGA_SKYPE("globals.destination=%s\n", SKYPIAX_P_LOG, globals.destination);
			} else if (!strcmp(var, "context")) {
				set_global_context(val);
				DEBUGA_SKYPE("globals.context=%s\n", SKYPIAX_P_LOG, globals.context);
			} else if (!strcmp(var, "codec-prefs")) {
				set_global_codec_string(val);
				DEBUGA_SKYPE("globals.codec_string=%s\n", SKYPIAX_P_LOG, globals.codec_string);
				globals.codec_order_last = switch_separate_string(globals.codec_string, ',', globals.codec_order, SWITCH_MAX_CODECS);
			} else if (!strcmp(var, "codec-rates")) {
				set_global_codec_rates_string(val);
				DEBUGA_SKYPE("globals.codec_rates_string=%s\n", SKYPIAX_P_LOG, globals.codec_rates_string);
				globals.codec_rates_last = switch_separate_string(globals.codec_rates_string, ',', globals.codec_rates, SWITCH_MAX_CODECS);
			}

		}
	}

	if ((interfaces = switch_xml_child(cfg, "per_interface_settings"))) {
		int i = 0;

		for (myinterface = switch_xml_child(interfaces, "interface"); myinterface; myinterface = myinterface->next) {
			char *id = (char *) switch_xml_attr(myinterface, "id");
			char *name = (char *) switch_xml_attr(myinterface, "name");
			char *context = "default";
			char *dialplan = "XML";
			char *destination = "5000";
			char *tonegroup = NULL;
			char *digit_timeout = NULL;
			char *max_digits = NULL;
			char *hotline = NULL;
			char *dial_regex = NULL;
			char *hold_music = NULL;
			char *fail_dial_regex = NULL;
			char *enable_callerid = "true";
			char *X11_display = NULL;
			char *tcp_cli_port = NULL;
			char *tcp_srv_port = NULL;
			char *skype_user = NULL;

			uint32_t interface_id = 0, to = 0, max = 0;

			tech_pvt = NULL;

			for (param = switch_xml_child(myinterface, "param"); param; param = param->next) {
				char *var = (char *) switch_xml_attr_soft(param, "name");
				char *val = (char *) switch_xml_attr_soft(param, "value");

				if (!strcasecmp(var, "tonegroup")) {
					tonegroup = val;
				} else if (!strcasecmp(var, "digit_timeout") || !strcasecmp(var, "digit-timeout")) {
					digit_timeout = val;
				} else if (!strcasecmp(var, "context")) {
					context = val;
				} else if (!strcasecmp(var, "dialplan")) {
					dialplan = val;
				} else if (!strcasecmp(var, "destination")) {
					destination = val;
				} else if (!strcasecmp(var, "dial-regex")) {
					dial_regex = val;
				} else if (!strcasecmp(var, "enable-callerid")) {
					enable_callerid = val;
				} else if (!strcasecmp(var, "fail-dial-regex")) {
					fail_dial_regex = val;
				} else if (!strcasecmp(var, "hold-music")) {
					hold_music = val;
				} else if (!strcasecmp(var, "skype_user")) {
					skype_user = val;
				} else if (!strcasecmp(var, "tcp_cli_port")) {
					tcp_cli_port = val;
				} else if (!strcasecmp(var, "tcp_srv_port")) {
					tcp_srv_port = val;
				} else if (!strcasecmp(var, "X11-display") || !strcasecmp(var, "X11_display")) {
					X11_display = val;
				} else if (!strcasecmp(var, "max_digits") || !strcasecmp(var, "max-digits")) {
					max_digits = val;
				} else if (!strcasecmp(var, "hotline")) {
					hotline = val;
				}

			}
			if (!skype_user) {
				ERRORA("interface missing REQUIRED param 'skype_user'\n", SKYPIAX_P_LOG);
				continue;
			}

			/* BEGIN: Changes here */
			if (reload_type == SOFT_RELOAD) {
				char the_interface[256];
				sprintf(the_interface, "#%s", name);

				if (interface_exists(the_interface) == SWITCH_STATUS_SUCCESS) {
					continue;
				}
			}
			/* END: Changes here */

			if (!X11_display) {
				ERRORA("interface missing REQUIRED param 'X11_display'\n", SKYPIAX_P_LOG);
				continue;
			}
			if (!tcp_cli_port) {
				ERRORA("interface missing REQUIRED param 'tcp_cli_port'\n", SKYPIAX_P_LOG);
				continue;
			}

			if (!tcp_srv_port) {
				ERRORA("interface missing REQUIRED param 'tcp_srv_port'\n", SKYPIAX_P_LOG);
				continue;
			}
			if (!id) {
				ERRORA("interface missing REQUIRED param 'id'\n", SKYPIAX_P_LOG);
				continue;
			}
			if (switch_is_number(id)) {
				interface_id = atoi(id);
				DEBUGA_SKYPE("interface_id=%d\n", SKYPIAX_P_LOG, interface_id);
			} else {
				ERRORA("interface param 'id' MUST be a number, now id='%s'\n", SKYPIAX_P_LOG, id);
				continue;
			}

			if (!name) {
				WARNINGA("interface missing param 'name', not nice, but works\n", SKYPIAX_P_LOG);
			}

			if (!tonegroup) {
				tonegroup = "us";
			}

			if (digit_timeout) {
				to = atoi(digit_timeout);
			}

			if (max_digits) {
				max = atoi(max_digits);
			}

			if (name) {
				DEBUGA_SKYPE("name=%s\n", SKYPIAX_P_LOG, name);
			}
#ifndef WIN32
			if (!XInitThreads()) {
				ERRORA("Not initialized XInitThreads!\n", SKYPIAX_P_LOG);
			} else {
				DEBUGA_SKYPE("Initialized XInitThreads!\n", SKYPIAX_P_LOG);
			}
			switch_sleep(100);
#endif /* WIN32 */

			if (interface_id && interface_id < SKYPIAX_MAX_INTERFACES) {
				private_t newconf;
				switch_threadattr_t *skypiax_api_thread_attr = NULL;
				switch_threadattr_t *skypiax_signaling_thread_attr = NULL;

				memset(&newconf, '\0', sizeof(newconf));
				globals.SKYPIAX_INTERFACES[interface_id] = newconf;
				globals.SKYPIAX_INTERFACES[interface_id].running = 1;


				tech_pvt = &globals.SKYPIAX_INTERFACES[interface_id];

				switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].interface_id, id);
				if (name) {
					switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].name, name);
				} else {
					switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].name, "N/A");
				}
				DEBUGA_SKYPE("CONFIGURING interface_id=%d\n", SKYPIAX_P_LOG, interface_id);
#ifdef WIN32
				globals.SKYPIAX_INTERFACES[interface_id].tcp_cli_port = (unsigned short) atoi(tcp_cli_port);
				globals.SKYPIAX_INTERFACES[interface_id].tcp_srv_port = (unsigned short) atoi(tcp_srv_port);
#else /* WIN32 */
				globals.SKYPIAX_INTERFACES[interface_id].tcp_cli_port = atoi(tcp_cli_port);
				globals.SKYPIAX_INTERFACES[interface_id].tcp_srv_port = atoi(tcp_srv_port);
#endif /* WIN32 */
				switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].X11_display, X11_display);
				switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].skype_user, skype_user);
				switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].context, context);
				switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].dialplan, dialplan);
				switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].destination, destination);
				switch_set_string(globals.SKYPIAX_INTERFACES[interface_id].context, context);

				DEBUGA_SKYPE
					("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].X11_display=%s\n",
					 SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].X11_display);
				DEBUGA_SKYPE
					("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].skype_user=%s\n",
					 SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].skype_user);
				DEBUGA_SKYPE
					("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].tcp_cli_port=%d\n",
					 SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].tcp_cli_port);
				DEBUGA_SKYPE
					("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].tcp_srv_port=%d\n",
					 SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].tcp_srv_port);
				DEBUGA_SKYPE("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].name=%s\n",
							 SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].name);
				DEBUGA_SKYPE
					("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].context=%s\n",
					 SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].context);
				DEBUGA_SKYPE
					("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].dialplan=%s\n",
					 SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].dialplan);
				DEBUGA_SKYPE
					("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].destination=%s\n",
					 SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].destination);
				DEBUGA_SKYPE
					("interface_id=%d globals.SKYPIAX_INTERFACES[interface_id].context=%s\n",
					 SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].context);
				WARNINGA("STARTING interface_id=%d\n", SKYPIAX_P_LOG, interface_id);

				switch_threadattr_create(&skypiax_api_thread_attr, skypiax_module_pool);
				switch_threadattr_stacksize_set(skypiax_api_thread_attr, SWITCH_THREAD_STACKSIZE);
				switch_thread_create(&globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread,
									 skypiax_api_thread_attr, skypiax_do_skypeapi_thread, &globals.SKYPIAX_INTERFACES[interface_id], skypiax_module_pool);

				switch_sleep(100000);

				switch_threadattr_create(&skypiax_signaling_thread_attr, skypiax_module_pool);
				switch_threadattr_stacksize_set(skypiax_signaling_thread_attr, SWITCH_THREAD_STACKSIZE);
				switch_thread_create(&globals.SKYPIAX_INTERFACES[interface_id].
									 skypiax_signaling_thread, skypiax_signaling_thread_attr,
									 skypiax_signaling_thread_func, &globals.SKYPIAX_INTERFACES[interface_id], skypiax_module_pool);

				switch_sleep(100000);

				skypiax_audio_init(&globals.SKYPIAX_INTERFACES[interface_id]);

				NOTICA
					("WAITING roughly 10 seconds to find a running Skype client and connect to its SKYPE API for interface_id=%d\n",
					 SKYPIAX_P_LOG, interface_id);
				i = 0;
				while (globals.SKYPIAX_INTERFACES[interface_id].SkypiaxHandles.api_connected == 0 && running && i < 200) {	// 10 seconds! thanks Jeff Lenk
					switch_sleep(50000);
					i++;
				}
				if (globals.SKYPIAX_INTERFACES[interface_id].SkypiaxHandles.api_connected) {
					NOTICA
						("Found a running Skype client, connected to its SKYPE API for interface_id=%d, waiting 60 seconds for CURRENTUSERHANDLE==%s\n",
						 SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].skype_user);
				} else {
					ERRORA
						("Failed to connect to a SKYPE API for interface_id=%d, no SKYPE client running, please (re)start Skype client. Skypiax exiting\n",
						 SKYPIAX_P_LOG, interface_id);
					running = 0;
					switch_mutex_unlock(globals.mutex);
					switch_xml_free(xml);
					return SWITCH_STATUS_FALSE;
				}

				i = 0;
				while (globals.SKYPIAX_INTERFACES[interface_id].SkypiaxHandles.currentuserhandle == 0 && running && i < 1200) {	// 60 seconds! thanks Jeff Lenk
					switch_sleep(50000);
					i++;
				}
				if (globals.SKYPIAX_INTERFACES[interface_id].SkypiaxHandles.currentuserhandle) {
					WARNINGA
						("Interface_id=%d is now STARTED, the Skype client to which we are connected gave us the correct CURRENTUSERHANDLE (%s)\n",
						 SKYPIAX_P_LOG, interface_id, globals.SKYPIAX_INTERFACES[interface_id].skype_user);

					skypiax_signaling_write(&globals.SKYPIAX_INTERFACES[interface_id], "SET AUTOAWAY OFF");
				} else {
					ERRORA
						("The Skype client to which we are connected FAILED to gave us CURRENTUSERHANDLE=%s, interface_id=%d FAILED to start. No Skype client logged in as '%s' has been found. Please (re)launch a Skype client logged in as '%s'. Skypiax exiting now\n",
						 SKYPIAX_P_LOG, globals.SKYPIAX_INTERFACES[interface_id].skype_user,
						 interface_id, globals.SKYPIAX_INTERFACES[interface_id].skype_user, globals.SKYPIAX_INTERFACES[interface_id].skype_user);
					running = 0;
					switch_mutex_unlock(globals.mutex);
					switch_xml_free(xml);
					return SWITCH_STATUS_FALSE;
				}

			} else {
				ERRORA("interface id %d is higher than SKYPIAX_MAX_INTERFACES (%d)\n", SKYPIAX_P_LOG, interface_id, SKYPIAX_MAX_INTERFACES);
				continue;
			}

		}

		for (i = 0; i < SKYPIAX_MAX_INTERFACES; i++) {
			if (strlen(globals.SKYPIAX_INTERFACES[i].name)) {
				/* How many real intterfaces */
				globals.real_interfaces = i + 1;

				tech_pvt = &globals.SKYPIAX_INTERFACES[i];

				DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].interface_id=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].interface_id);
				DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].X11_display=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].X11_display);
				DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].name=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].name);
				DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].context=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].context);
				DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].dialplan=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].dialplan);
				DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].destination=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].destination);
				DEBUGA_SKYPE("i=%d globals.SKYPIAX_INTERFACES[%d].context=%s\n", SKYPIAX_P_LOG, i, i, globals.SKYPIAX_INTERFACES[i].context);
			}
		}
	}

	switch_mutex_unlock(globals.mutex);
	switch_xml_free(xml);

	return SWITCH_STATUS_SUCCESS;
}

SWITCH_MODULE_LOAD_FUNCTION(mod_skypiax_load)
{
	switch_api_interface_t *commands_api_interface;

	skypiax_module_pool = pool;
	memset(&globals, '\0', sizeof(globals));

	running = 1;

	if (load_config(FULL_RELOAD) != SWITCH_STATUS_SUCCESS) {
		running = 0;
		return SWITCH_STATUS_FALSE;
	}

	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
	skypiax_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE);
	skypiax_endpoint_interface->interface_name = "skypiax";
	skypiax_endpoint_interface->io_routines = &skypiax_io_routines;
	skypiax_endpoint_interface->state_handler = &skypiax_state_handlers;

	if (running) {

		SWITCH_ADD_API(commands_api_interface, "sk", "Skypiax console commands", sk_function, SK_SYNTAX);
		SWITCH_ADD_API(commands_api_interface, "skypiax", "Skypiax interface commands", skypiax_function, SKYPIAX_SYNTAX);

		/* indicate that the module should continue to be loaded */
		return SWITCH_STATUS_SUCCESS;
	} else
		return SWITCH_STATUS_FALSE;
}

SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skypiax_shutdown)
{
	int x;
	private_t *tech_pvt = NULL;
	switch_status_t status;
	unsigned int howmany = 8;
	int interface_id;

	running = 0;

	for (interface_id = 0; interface_id < SKYPIAX_MAX_INTERFACES; interface_id++) {
		tech_pvt = &globals.SKYPIAX_INTERFACES[interface_id];

		if (strlen(globals.SKYPIAX_INTERFACES[interface_id].name)) {
			if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_signaling_thread) {
#ifdef WIN32
				switch_file_write(tech_pvt->SkypiaxHandles.fdesc[1], "sciutati", &howmany);	// let's the controldev_thread die
#else /* WIN32 */
				howmany = write(tech_pvt->SkypiaxHandles.fdesc[1], "sciutati", howmany);
#endif /* WIN32 */
			}

			if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread) {
#ifdef WIN32
				if (SendMessage(tech_pvt->SkypiaxHandles.win32_hInit_MainWindowHandle, WM_DESTROY, 0, 0) == FALSE) {	// let's the skypiax_api_thread_func die
					DEBUGA_SKYPE("got FALSE here, thread probably was already dead. GetLastError returned: %d\n", SKYPIAX_P_LOG, GetLastError());
					globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread = NULL;
				}
#else
				XEvent e;
				Atom atom1 = XInternAtom(tech_pvt->SkypiaxHandles.disp, "SKYPECONTROLAPI_MESSAGE_BEGIN",
										 False);
				memset(&e, 0, sizeof(e));
				e.xclient.type = ClientMessage;
				e.xclient.message_type = atom1;	/*  leading message */
				e.xclient.display = tech_pvt->SkypiaxHandles.disp;
				e.xclient.window = tech_pvt->SkypiaxHandles.skype_win;
				e.xclient.format = 8;

				XSendEvent(tech_pvt->SkypiaxHandles.disp, tech_pvt->SkypiaxHandles.win, False, 0, &e);
				XSync(tech_pvt->SkypiaxHandles.disp, False);
#endif
			}
			x = 10;
			while (x) {			//FIXME 0.5 seconds?
				x--;
				switch_yield(50000);
			}
			if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_signaling_thread) {
				switch_thread_join(&status, globals.SKYPIAX_INTERFACES[interface_id].skypiax_signaling_thread);
			}
			if (globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread) {
				switch_thread_join(&status, globals.SKYPIAX_INTERFACES[interface_id].skypiax_api_thread);
			}
#ifndef WIN32
			WARNINGA("SHUTDOWN interface_id=%d\n", SKYPIAX_P_LOG, interface_id);
			shutdown(tech_pvt->audioskypepipe[0], 2);
			close(tech_pvt->audioskypepipe[0]);
			shutdown(tech_pvt->audioskypepipe[1], 2);
			close(tech_pvt->audioskypepipe[1]);
			shutdown(tech_pvt->audiopipe[0], 2);
			close(tech_pvt->audiopipe[0]);
			shutdown(tech_pvt->audiopipe[1], 2);
			close(tech_pvt->audiopipe[1]);
			shutdown(tech_pvt->SkypiaxHandles.fdesc[0], 2);
			close(tech_pvt->SkypiaxHandles.fdesc[0]);
			shutdown(tech_pvt->SkypiaxHandles.fdesc[1], 2);
			close(tech_pvt->SkypiaxHandles.fdesc[1]);
#endif /* WIN32 */
		}

	}
	switch_safe_free(globals.dialplan);
	switch_safe_free(globals.context);
	switch_safe_free(globals.destination);
	switch_safe_free(globals.codec_string);
	switch_safe_free(globals.codec_rates_string);

	return SWITCH_STATUS_SUCCESS;
}

void *SWITCH_THREAD_FUNC skypiax_do_tcp_srv_thread(switch_thread_t * thread, void *obj)
{
	return skypiax_do_tcp_srv_thread_func(obj);
}

void *SWITCH_THREAD_FUNC skypiax_do_tcp_cli_thread(switch_thread_t * thread, void *obj)
{
	return skypiax_do_tcp_cli_thread_func(obj);
}

void *SWITCH_THREAD_FUNC skypiax_do_skypeapi_thread(switch_thread_t * thread, void *obj)
{
	return skypiax_do_skypeapi_thread_func(obj);
}

int dtmf_received(private_t * tech_pvt, char *value)
{
	switch_core_session_t *session = NULL;
	switch_channel_t *channel = NULL;

	session = switch_core_session_locate(tech_pvt->session_uuid_str);
	channel = switch_core_session_get_channel(session);

	if (channel) {

		if (!switch_channel_test_flag(channel, CF_BRIDGED)) {

			switch_dtmf_t dtmf = { (char) value[0], switch_core_default_dtmf_duration(0) };
			DEBUGA_SKYPE("received DTMF %c on channel %s\n", SKYPIAX_P_LOG, dtmf.digit, switch_channel_get_name(channel));
			switch_mutex_lock(tech_pvt->flag_mutex);
			//FIXME: why sometimes DTMFs from here do not seems to be get by FS?
			switch_channel_queue_dtmf(channel, &dtmf);
			switch_set_flag(tech_pvt, TFLAG_DTMF);
			switch_mutex_unlock(tech_pvt->flag_mutex);
		} else {
			DEBUGA_SKYPE
				("received a DTMF on channel %s, but we're BRIDGED, so let's NOT relay it out of band\n", SKYPIAX_P_LOG, switch_channel_get_name(channel));
		}
	} else {
		WARNINGA("received %c DTMF, but no channel?\n", SKYPIAX_P_LOG, value[0]);
	}
	switch_core_session_rwunlock(session);

	return 0;
}

int start_audio_threads(private_t * tech_pvt)
{
	switch_threadattr_t *thd_attr = NULL;

	switch_threadattr_create(&thd_attr, skypiax_module_pool);
	switch_threadattr_detach_set(thd_attr, 1);
	switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
	switch_thread_create(&tech_pvt->tcp_srv_thread, thd_attr, skypiax_do_tcp_srv_thread, tech_pvt, skypiax_module_pool);
	DEBUGA_SKYPE("started tcp_srv_thread thread.\n", SKYPIAX_P_LOG);

	switch_threadattr_create(&thd_attr, skypiax_module_pool);
	switch_threadattr_detach_set(thd_attr, 1);
	switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
	switch_thread_create(&tech_pvt->tcp_cli_thread, thd_attr, skypiax_do_tcp_cli_thread, tech_pvt, skypiax_module_pool);
	DEBUGA_SKYPE("started tcp_cli_thread thread.\n", SKYPIAX_P_LOG);
	switch_sleep(100000);

	return 0;
}

int new_inbound_channel(private_t * tech_pvt)
{
	switch_core_session_t *session = NULL;
	switch_channel_t *channel = NULL;

	if ((session = switch_core_session_request(skypiax_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, NULL)) != 0) {
		switch_core_session_add_stream(session, NULL);
		channel = switch_core_session_get_channel(session);
		skypiax_tech_init(tech_pvt, session);

		if ((tech_pvt->caller_profile =
			 switch_caller_profile_new(switch_core_session_get_pool(session), "skypiax",
									   tech_pvt->dialplan, tech_pvt->callid_name,
									   tech_pvt->callid_number, NULL, NULL, NULL, NULL, "mod_skypiax", tech_pvt->context, tech_pvt->destination)) != 0) {
			char name[128];
			//switch_snprintf(name, sizeof(name), "skypiax/%s/%s", tech_pvt->name, tech_pvt->caller_profile->destination_number);
			switch_snprintf(name, sizeof(name), "skypiax/%s", tech_pvt->name);
			switch_channel_set_name(channel, name);
			switch_channel_set_caller_profile(channel, tech_pvt->caller_profile);
		}
		switch_channel_set_state(channel, CS_INIT);
		if (switch_core_session_thread_launch(session) != SWITCH_STATUS_SUCCESS) {
			ERRORA("Error spawning thread\n", SKYPIAX_P_LOG);
			switch_core_session_destroy(&session);
		}
	}
	switch_channel_mark_answered(channel);

	DEBUGA_SKYPE("Here\n", SKYPIAX_P_LOG);

	return 0;
}

int remote_party_is_ringing(private_t * tech_pvt)
{
	switch_core_session_t *session = NULL;
	switch_channel_t *channel = NULL;

	if (!switch_strlen_zero(tech_pvt->session_uuid_str)) {
		session = switch_core_session_locate(tech_pvt->session_uuid_str);
	} else {
		ERRORA("No session???\n", SKYPIAX_P_LOG);
		goto done;
	}
	if (session) {
		channel = switch_core_session_get_channel(session);
	} else {
		ERRORA("No session???\n", SKYPIAX_P_LOG);
		goto done;
	}
	if (channel) {
		switch_channel_mark_ring_ready(channel);
		DEBUGA_SKYPE("skype_call: REMOTE PARTY RINGING\n", SKYPIAX_P_LOG);
	} else {
		ERRORA("No channel???\n", SKYPIAX_P_LOG);
		goto done;
	}

	switch_core_session_rwunlock(session);

  done:
	return 0;
}

int remote_party_is_early_media(private_t * tech_pvt)
{
	switch_core_session_t *session = NULL;
	switch_channel_t *channel = NULL;

	if (!switch_strlen_zero(tech_pvt->session_uuid_str)) {
		session = switch_core_session_locate(tech_pvt->session_uuid_str);
	} else {
		ERRORA("No session???\n", SKYPIAX_P_LOG);
		goto done;
	}
	if (session) {
		channel = switch_core_session_get_channel(session);
		switch_core_session_add_stream(session, NULL);
	} else {
		ERRORA("No session???\n", SKYPIAX_P_LOG);
		goto done;
	}
	if (channel) {
		switch_channel_mark_pre_answered(channel);
		DEBUGA_SKYPE("skype_call: REMOTE PARTY EARLY MEDIA\n", SKYPIAX_P_LOG);
	} else {
		ERRORA("No channel???\n", SKYPIAX_P_LOG);
		goto done;
	}

	switch_core_session_rwunlock(session);

  done:
	return 0;
}

int outbound_channel_answered(private_t * tech_pvt)
{
	switch_core_session_t *session = NULL;
	switch_channel_t *channel = NULL;

	if (!switch_strlen_zero(tech_pvt->session_uuid_str)) {
		session = switch_core_session_locate(tech_pvt->session_uuid_str);
	} else {
		ERRORA("No session???\n", SKYPIAX_P_LOG);
		goto done;
	}
	if (session) {
		channel = switch_core_session_get_channel(session);
	} else {
		ERRORA("No session???\n", SKYPIAX_P_LOG);
		goto done;
	}
	if (channel) {
		switch_channel_mark_answered(channel);
		//DEBUGA_SKYPE("skype_call: %s, answered\n", SKYPIAX_P_LOG, id);
	} else {
		ERRORA("No channel???\n", SKYPIAX_P_LOG);
		goto done;
	}

	switch_core_session_rwunlock(session);

  done:
	DEBUGA_SKYPE("HERE!\n", SKYPIAX_P_LOG);

	return 0;
}

private_t *find_available_skypiax_interface(private_t * tech_pvt)
{
	private_t *tech_pvt2 = NULL;
	int found = 0;
	int i;

	switch_mutex_lock(globals.mutex);

	for (i = 0; !found && i < SKYPIAX_MAX_INTERFACES; i++) {
		if (strlen(globals.SKYPIAX_INTERFACES[i].name)) {
			int skype_state = 0;

			tech_pvt2 = &globals.SKYPIAX_INTERFACES[i];
			skype_state = tech_pvt2->interface_state;
			DEBUGA_SKYPE("skype interface: %d, name: %s, state: %d\n", SKYPIAX_P_LOG, i, globals.SKYPIAX_INTERFACES[i].name, skype_state);
			if ((tech_pvt ? strcmp(tech_pvt2->skype_user, tech_pvt->skype_user) : 1) && (SKYPIAX_STATE_DOWN == skype_state || SKYPIAX_STATE_RING == skype_state || 0 == skype_state)) {	//(if we got tech_pvt NOT NULL) if user is NOT the same, and iface is idle
				found = 1;
				break;
			}
		}
	}

	switch_mutex_unlock(globals.mutex);
	if (found)
		return tech_pvt2;
	else
		return NULL;
}

private_t *find_available_skypiax_interface_rr(void)
{
	private_t *tech_pvt = NULL;
	int i;
	//int num_interfaces = SKYPIAX_MAX_INTERFACES; 
	//int num_interfaces = globals.real_interfaces;

	switch_mutex_lock(globals.mutex);

	/* Fact is the real interface start from 1 */
	//XXX no, is just a convention, but you can have it start from 0. I do not, for aestetic reasons :-)  
	//if (globals.next_interface == 0) globals.next_interface = 1;

	for (i = 0; i < SKYPIAX_MAX_INTERFACES; i++) {
		int interface_id;

		interface_id = globals.next_interface;
		//interface_id = interface_id < SKYPIAX_MAX_INTERFACES ? interface_id : interface_id - SKYPIAX_MAX_INTERFACES + 1;
		globals.next_interface = interface_id + 1 < SKYPIAX_MAX_INTERFACES ? interface_id + 1 : 0;

		if (strlen(globals.SKYPIAX_INTERFACES[interface_id].name)) {
			int skype_state = 0;

			tech_pvt = &globals.SKYPIAX_INTERFACES[interface_id];
			skype_state = tech_pvt->interface_state;
			DEBUGA_SKYPE("skype interface: %d, name: %s, state: %d\n", SKYPIAX_P_LOG, interface_id,
						 globals.SKYPIAX_INTERFACES[interface_id].name, skype_state);
			if (SKYPIAX_STATE_DOWN == skype_state || 0 == skype_state) {
				/*set to Dialing state to avoid other thread fint it, don't know if it is safe */
				//XXX no, it's not safe 
				//tech_pvt->interface_state = SKYPIAX_STATE_DIALING ;

				switch_mutex_unlock(globals.mutex);
				return tech_pvt;
			}
		}						// else {
		//DEBUGA_SKYPE("Skype interface: %d blank!! A hole here means we cannot hunt the last interface.\n", SKYPIAX_P_LOG, interface_id);
		//}
	}

	switch_mutex_unlock(globals.mutex);
	return NULL;
}

SWITCH_STANDARD_API(sk_function)
{
	char *mycmd = NULL, *argv[10] = { 0 };
	int argc = 0;

	if (globals.sk_console)
		stream->write_function(stream, "sk console is: |||%s|||\n", globals.sk_console->name);
	else
		stream->write_function(stream, "sk console is NOT yet assigned\n");

	if (!switch_strlen_zero(cmd) && (mycmd = strdup(cmd))) {
		argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
	}

	if (!argc || !argv[0]) {
		stream->write_function(stream, "%s", SK_SYNTAX);
		goto end;
	}

	if (!strcasecmp(argv[0], "list")) {
		int i;
		char next_flag_char = ' ';

		for (i = 0; i < SKYPIAX_MAX_INTERFACES; i++) {
			next_flag_char = i == globals.next_interface ? '*' : ' ';

			if (strlen(globals.SKYPIAX_INTERFACES[i].name)) {
				if (strlen(globals.SKYPIAX_INTERFACES[i].session_uuid_str)) {
					stream->write_function(stream,
										   "%c\t%d\t[%s]\tBUSY, session_uuid_str=|||%s|||\n",
										   next_flag_char, i, globals.SKYPIAX_INTERFACES[i].name, globals.SKYPIAX_INTERFACES[i].session_uuid_str);
				} else {
					stream->write_function(stream, "%c\t%d\t[%s]\tIDLE\n", next_flag_char, i, globals.SKYPIAX_INTERFACES[i].name);
				}
			} else if (argc > 1 && !strcasecmp(argv[1], "full")) {
				stream->write_function(stream, "%c\t%d\n", next_flag_char, i);
			}

		}
		stream->write_function(stream, "\nTotal: %d\n", globals.real_interfaces - 1);

	} else if (!strcasecmp(argv[0], "console")) {
		int i;
		int found = 0;

		if (argc == 2) {
			for (i = 0; !found && i < SKYPIAX_MAX_INTERFACES; i++) {
				/* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */
				if (strlen(globals.SKYPIAX_INTERFACES[i].name)
					&& (strncmp(globals.SKYPIAX_INTERFACES[i].name, argv[1], strlen(argv[1])) == 0)) {
					globals.sk_console = &globals.SKYPIAX_INTERFACES[i];
					stream->write_function(stream,
										   "sk console is now: globals.SKYPIAX_INTERFACES[%d].name=|||%s|||\n", i, globals.SKYPIAX_INTERFACES[i].name);
					stream->write_function(stream, "sk console is: |||%s|||\n", globals.sk_console->name);
					found = 1;
					break;
				}

			}
			if (!found)
				stream->write_function(stream, "ERROR: A Skypiax interface with name='%s' was not found\n", argv[1]);
		} else {

			stream->write_function(stream, "-ERR Usage: sk console interface_name\n");
			goto end;
		}

	} else if (!strcasecmp(argv[0], "ciapalino")) {

/* BEGIN: Changes heres */
	} else if (!strcasecmp(argv[0], "reload")) {
		if (load_config(SOFT_RELOAD) != SWITCH_STATUS_SUCCESS) {
			stream->write_function(stream, "sk reload failed\n");
		} else {
			stream->write_function(stream, "sk reload success\n");
		}
	} else if (!strcasecmp(argv[0], "remove")) {
		if (argc == 2) {
			if (remove_interface(argv[1]) == SWITCH_STATUS_SUCCESS) {
				if (interface_exists(argv[1]) == SWITCH_STATUS_SUCCESS) {
					stream->write_function(stream, "sk remove '%s' failed\n", argv[1]);
				} else {
					stream->write_function(stream, "sk remove '%s' success\n", argv[1]);
				}
			}
		} else {
			stream->write_function(stream, "-ERR Usage: sk remove interface_name\n");
			goto end;
		}
/* END: Changes heres */

	} else {
		if (globals.sk_console)
			skypiax_signaling_write(globals.sk_console, (char *) cmd);
		else
			stream->write_function(stream, "sk console is NOT yet assigned\n");
	}
  end:
	switch_safe_free(mycmd);

	return SWITCH_STATUS_SUCCESS;
}

SWITCH_STANDARD_API(skypiax_function)
{
	char *mycmd = NULL, *argv[10] = { 0 };
	int argc = 0;
	private_t *tech_pvt = NULL;

	if (!switch_strlen_zero(cmd) && (mycmd = strdup(cmd))) {
		argc = switch_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
	}

	if (!argc) {
		stream->write_function(stream, "ERROR, usage: %s", SKYPIAX_SYNTAX);
		goto end;
	}

	if (argc < 2) {
		stream->write_function(stream, "ERROR, usage: %s", SKYPIAX_SYNTAX);
		goto end;
	}

	if (argv[0]) {
		int i;
		int found = 0;

		for (i = 0; !found && i < SKYPIAX_MAX_INTERFACES; i++) {
			/* we've been asked for a normal interface name, or we have not found idle interfaces to serve as the "ANY" interface */
			if (strlen(globals.SKYPIAX_INTERFACES[i].name)
				&& (strncmp(globals.SKYPIAX_INTERFACES[i].name, argv[0], strlen(argv[0])) == 0)) {
				tech_pvt = &globals.SKYPIAX_INTERFACES[i];
				stream->write_function(stream, "Using interface: globals.SKYPIAX_INTERFACES[%d].name=|||%s|||\n", i, globals.SKYPIAX_INTERFACES[i].name);
				found = 1;
				break;
			}

		}
		if (!found) {
			stream->write_function(stream, "ERROR: A Skypiax interface with name='%s' was not found\n", argv[0]);
			switch_safe_free(mycmd);

			return SWITCH_STATUS_SUCCESS;
		} else {
			skypiax_signaling_write(tech_pvt, (char *) &cmd[strlen(argv[0]) + 1]);
		}
	} else {
		stream->write_function(stream, "ERROR, usage: %s", SKYPIAX_SYNTAX);
	}
  end:
	switch_safe_free(mycmd);

	return SWITCH_STATUS_SUCCESS;
}

int skypiax_answer(private_t * tech_pvt, char *id, char *value)
{
	char msg_to_skype[1024];
	int i;
	int found = 0;
	private_t *giovatech;
	struct timeval timenow;

	switch_mutex_lock(globals.mutex);

	gettimeofday(&timenow, NULL);
	for (i = 0; !found && i < SKYPIAX_MAX_INTERFACES; i++) {
		if (strlen(globals.SKYPIAX_INTERFACES[i].name)) {

			giovatech = &globals.SKYPIAX_INTERFACES[i];
			//NOTICA("skype interface: %d, name: %s, state: %d, value=%s, giovatech->callid_number=%s, giovatech->skype_user=%s\n", SKYPIAX_P_LOG, i, giovatech->name, giovatech->interface_state, value, giovatech->callid_number, giovatech->skype_user);
			//FIXME check a timestamp here
			if (strlen(giovatech->skype_call_id) && (giovatech->interface_state != SKYPIAX_STATE_DOWN) && (!strcmp(giovatech->skype_user, tech_pvt->skype_user)) && (!strcmp(giovatech->callid_number, value)) && ((((timenow.tv_sec - giovatech->answer_time.tv_sec) * 1000000) + (timenow.tv_usec - giovatech->answer_time.tv_usec)) < 500000)) {	//0.5sec
				found = 1;
				DEBUGA_SKYPE
					("FOUND  (name=%s, giovatech->interface_state=%d != SKYPIAX_STATE_DOWN) && (giovatech->skype_user=%s == tech_pvt->skype_user=%s) && (giovatech->callid_number=%s == value=%s)\n",
					 SKYPIAX_P_LOG, giovatech->name, giovatech->interface_state,
					 giovatech->skype_user, tech_pvt->skype_user, giovatech->callid_number, value)
					if(tech_pvt->interface_state == SKYPIAX_STATE_PRERING){
						tech_pvt->interface_state = SKYPIAX_STATE_DOWN;
					}else if (tech_pvt->interface_state != 0 && tech_pvt->interface_state != SKYPIAX_STATE_DOWN) {
						WARNINGA("Why an interface_state %d HERE?\n", SKYPIAX_P_LOG, tech_pvt->interface_state);
						tech_pvt->interface_state = SKYPIAX_STATE_DOWN;
					}

				break;
			}
		}
	}

	if (found) {
		//tech_pvt->callid_number[0]='\0';
		switch_mutex_unlock(globals.mutex);
		return 0;
	}
	DEBUGA_SKYPE("NOT FOUND\n", SKYPIAX_P_LOG);

	if (tech_pvt && tech_pvt->skype_call_id && !strlen(tech_pvt->skype_call_id)) {
		/* we are not inside an active call */

		sprintf(msg_to_skype, "GET CALL %s PARTNER_DISPNAME", id);
		skypiax_signaling_write(tech_pvt, msg_to_skype);
		switch_sleep(10000);
		sprintf(msg_to_skype, "ALTER CALL %s ANSWER", id);
		skypiax_signaling_write(tech_pvt, msg_to_skype);
		DEBUGA_SKYPE("We answered a Skype RING on skype_call %s\n", SKYPIAX_P_LOG, id);
		//FIXME write a timestamp here
		gettimeofday(&tech_pvt->answer_time, NULL);
		switch_copy_string(tech_pvt->skype_call_id, id, sizeof(tech_pvt->skype_call_id) - 1);

		switch_copy_string(tech_pvt->callid_number, value, sizeof(tech_pvt->callid_number) - 1);

		DEBUGA_SKYPE
			("NEW!  name: %s, state: %d, value=%s, tech_pvt->callid_number=%s, tech_pvt->skype_user=%s\n",
			 SKYPIAX_P_LOG, tech_pvt->name, tech_pvt->interface_state, value, tech_pvt->callid_number, tech_pvt->skype_user);
	} else if (!tech_pvt || !tech_pvt->skype_call_id) {
		ERRORA("No Call ID?\n", SKYPIAX_P_LOG);
	} else {
		ERRORA("We're in a call now %s\n", SKYPIAX_P_LOG, tech_pvt->skype_call_id);
	}

	switch_mutex_unlock(globals.mutex);
	return 0;
}
int skypiax_transfer(private_t * tech_pvt, char *id, char *value)
{
	char msg_to_skype[1024];
	int i;
	int found = 0;
	private_t *giovatech;
	struct timeval timenow;

	switch_mutex_lock(globals.mutex);

	gettimeofday(&timenow, NULL);
	for (i = 0; !found && i < SKYPIAX_MAX_INTERFACES; i++) {
		if (strlen(globals.SKYPIAX_INTERFACES[i].name)) {

			giovatech = &globals.SKYPIAX_INTERFACES[i];
			//NOTICA("skype interface: %d, name: %s, state: %d, value=%s, giovatech->callid_number=%s, giovatech->skype_user=%s\n", SKYPIAX_P_LOG, i, giovatech->name, giovatech->interface_state, value, giovatech->callid_number, giovatech->skype_user);
			//FIXME check a timestamp here
			if (strlen(giovatech->skype_call_id) && (giovatech->interface_state != SKYPIAX_STATE_DOWN) && (!strcmp(giovatech->skype_user, tech_pvt->skype_user)) && (!strcmp(giovatech->callid_number, value)) && ((((timenow.tv_sec - giovatech->answer_time.tv_sec) * 1000000) + (timenow.tv_usec - giovatech->answer_time.tv_usec)) < 500000)) {	//0.5sec
				found = 1;
				DEBUGA_SKYPE
					("FOUND  (name=%s, giovatech->interface_state=%d != SKYPIAX_STATE_DOWN) && (giovatech->skype_user=%s == tech_pvt->skype_user=%s) && (giovatech->callid_number=%s == value=%s)\n",
					 SKYPIAX_P_LOG, giovatech->name, giovatech->interface_state,
					 giovatech->skype_user, tech_pvt->skype_user, giovatech->callid_number, value)
					break;
			}
		}
	}

	if (found) {
		//tech_pvt->callid_number[0]='\0';
		switch_mutex_unlock(globals.mutex);
		return 0;
	}
	DEBUGA_SKYPE("NOT FOUND\n", SKYPIAX_P_LOG);

	if (!tech_pvt || !tech_pvt->skype_call_id || !strlen(tech_pvt->skype_call_id)) {
		/* we are not inside an active call */
		ERRORA("We're NO MORE in a call now %s\n", SKYPIAX_P_LOG, (tech_pvt && tech_pvt->skype_call_id) ? tech_pvt->skype_call_id : "");
		switch_mutex_unlock(globals.mutex);

	} else {

		/* we're owned, we're in a call, let's try to transfer */
		/************************** TODO
		  Checking here if it is possible to transfer this call to Test2
		  -> GET CALL 288 CAN_TRANSFER Test2
		  <- CALL 288 CAN_TRANSFER test2 TRUE
		 **********************************/

		private_t *available_skypiax_interface = NULL;

		gettimeofday(&timenow, NULL);
		for (i = 0; !found && i < SKYPIAX_MAX_INTERFACES; i++) {
			if (strlen(globals.SKYPIAX_INTERFACES[i].name)) {

				giovatech = &globals.SKYPIAX_INTERFACES[i];
				//NOTICA("skype interface: %d, name: %s, state: %d, value=%s, giovatech->callid_number=%s, giovatech->skype_user=%s\n", SKYPIAX_P_LOG, i, giovatech->name, giovatech->interface_state, value, giovatech->callid_number, giovatech->skype_user);
				//FIXME check a timestamp here
				if (strlen(giovatech->skype_transfer_call_id) && (giovatech->interface_state != SKYPIAX_STATE_DOWN) && (!strcmp(giovatech->skype_user, tech_pvt->skype_user)) && (!strcmp(giovatech->transfer_callid_number, value)) && ((((timenow.tv_sec - giovatech->transfer_time.tv_sec) * 1000000) + (timenow.tv_usec - giovatech->transfer_time.tv_usec)) < 1000000)) {	//1.0 sec
					found = 1;
					DEBUGA_SKYPE
						("FOUND  (name=%s, giovatech->interface_state=%d != SKYPIAX_STATE_DOWN) && (giovatech->skype_user=%s == tech_pvt->skype_user=%s) && (giovatech->transfer_callid_number=%s == value=%s)\n",
						 SKYPIAX_P_LOG, giovatech->name, giovatech->interface_state,
						 giovatech->skype_user, tech_pvt->skype_user, giovatech->transfer_callid_number, value)
						break;
				}
			}
		}

		if (found) {
			//tech_pvt->callid_number[0]='\0';
			switch_mutex_unlock(globals.mutex);
			return 0;
		}
		DEBUGA_SKYPE("NOT FOUND\n", SKYPIAX_P_LOG);

		available_skypiax_interface = find_available_skypiax_interface(tech_pvt);
		if (available_skypiax_interface) {
			/* there is a skypiax interface idle, let's transfer the call to it */

			//FIXME write a timestamp here
			gettimeofday(&tech_pvt->transfer_time, NULL);
			switch_copy_string(tech_pvt->skype_transfer_call_id, id, sizeof(tech_pvt->skype_transfer_call_id) - 1);

			switch_copy_string(tech_pvt->transfer_callid_number, value, sizeof(tech_pvt->transfer_callid_number) - 1);

			DEBUGA_SKYPE
				("Let's transfer the skype_call %s to %s interface (with skype_user: %s), because we are already in a skypiax call(%s)\n",
				 SKYPIAX_P_LOG, tech_pvt->skype_call_id, available_skypiax_interface->name, available_skypiax_interface->skype_user, id);
			sprintf(msg_to_skype, "ALTER CALL %s TRANSFER %s", id, available_skypiax_interface->skype_user);
			skypiax_signaling_write(tech_pvt, msg_to_skype);
		} else {
			/* no skypiax interfaces idle, do nothing */
			DEBUGA_SKYPE
				("Not answering the skype_call %s, because we are already in a skypiax call(%s) and no other skypiax interfaces are available OR another interface is answering this call\n",
				 SKYPIAX_P_LOG, tech_pvt->skype_call_id, id);
			//sprintf(msg_to_skype, "ALTER CALL %s END HANGUP", id);
		}
		switch_sleep(10000);
		DEBUGA_SKYPE
			("We (%s) have NOT answered a Skype RING on skype_call %s, because we are already in a skypiax call\n",
			 SKYPIAX_P_LOG, tech_pvt->skype_call_id, id);

		switch_mutex_unlock(globals.mutex);
	}
	return 0;
}

/* For Emacs:
 * Local Variables:
 * mode:c
 * indent-tabs-mode:t
 * tab-width:4
 * c-basic-offset:4
 * End:
 * For VIM:
 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
 */
