/**********************************************************
							  
  MEX file for the tcpip toolbox.
                                                          */
#define VERSION "Version  2.0.5 + PTBMods by Mario Kleiner - 2012-09-10"

/*
%   This file(s) is part of the tcp_udp_ip toolbox (C) Peter Rydesater et al.
%   et al.  1998-2003 for running in MATLAB(R) as scripts and/or plug-ins.
%
%   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
%
%   In addition, as a SPECIAL EXCEPTION, Peter Rydesater, SWEDEN,
%   gives permission to link the code of this program with any library,
%   and distribute linked combinations of it. You must obey the GNU
%   General Public License in all respects for all of the code in the
%   tcp_udp_ip toolbox and any . If you modify any source file included,
%   you may extend this exception to your version of the file, but you are
%   not obligated to do so.  If you do not wish to do so, delete this exception
%   statement from your version. This exception makes it possible to use
%   pnet.c (.dll) as a plug-in as it is intended and let it be (dynamical)
%   linked to MATLAB(R) or any compiled stand alone application.

    
  Notes for Unix implementation
  Compile this with:
  
  mex -g -v -largeArrayDims pnet.c
  
  On Linux + gcc 4.6+ + Matlab use:
 
  mex -O -g -v CFLAGS='$CFLAGS -fPIC -fexceptions' -largeArrayDims pnet.c
 
  Notes for Windows implementation
 
  When using LCC, compile this with:
  mex -O pnet.c {MATLAB_INSTALL_DIR}\sys\lcc\lib\wsock32.lib -DWIN32

  When using Visual C++, compile this with:
  mex -O pnet.c ws2_32.lib winmm.lib -DWIN32 -largeArrayDims
  
  
  == Main Authour ==           == Windows support ==      == Earlie/Basic UDP support ==
  Peter Rydesater              Mario Bergeron             Mike Medeiros at 23-Jan-2001.
                               LYRtech
  Ostersund, Sweden            Quebec, Canada
  +46 70 560 68 16             
  Peter.Rydesater@mh.se        Mario.Bergeron@lyrtech.com


  SE MORE:  http://www.rydesater.com
  
  **********************************************************/
    
/******* GENERAL DEFINES *********/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>

/******* WINDOWS ONLY DEFINES *********/
#ifdef WIN32
#define IFWINDOWS(dothis) dothis 
#define IFUNIX(dothis)
//#include <windows.h>
#include <winsock2.h>
#define close(s) closesocket(s)
#define nonblockingsocket(s) {unsigned long ctl = 1;ioctlsocket( s, FIONBIO, &ctl );}
#define s_errno WSAGetLastError()
#define EWOULDBLOCK WSAEWOULDBLOCK
#define usleep(a) Sleep((a)/1000)
#define MSG_NOSIGNAL 0
#define DEFAULT_USLEEP        1000		/* MK: Changed from 10 msecs to 1 msec == 1000 microsecs. for lower latency. Can't go lower than 1 msec on Windoze.  :-( */

/******* NON WINDOWS DEFINES *********/
#else
#define IFWINDOWS(dothis) 
#define IFUNIX(dothis) dothis

#include <errno.h>
#define s_errno errno // ?? Is this OK ??
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> 
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <sys/time.h>

#define nonblockingsocket(s)  fcntl(s,F_SETFL,O_NONBLOCK)
#define DEFAULT_USLEEP        500		/* MK: Changed from 10 msecs to 0.5 msec == 500 microsecs. for lower latency. Should not be a problem on good OS/X and Linux :-) */
#endif

#ifndef INADDR_NONE
#define INADDR_NONE (-1)
#endif

// Do this hack cause SIGPIPE that kills matlab on any platform???
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif

/* Include header file for matlab mex file functionality */
#include "mex.h"

/********** DEFINES related to pnet own functionality *****************/
/*   Set debuging on/off   */
#define debug_view_con_status(X)   // __debug_view_con_status(X)

#define MAX_CON         100       /* Maximum number of simultanius tcpip connections.*/
#define NAMEBUFF_LEN    100
#define MINBUFFSIZE     1000      /* readbuffer will shrink to this size if datalength is smaller. */
#define CBNAMELEN       30

#define CON_FREE         -1       /* Find a new free struct in struct array */

#define BACKLOG           50       /* How many pending connections queue will hold */

#define double_inf            HUGE_VAL
#define DEFAULT_WRITETIMEOUT  double_inf
#define DEFAULT_READTIMEOUT   double_inf
#define DEFAULT_INPUT_SIZE    50000

/* Different status of a con_info struct handles a file descriptor    */
#define STATUS_FREE       -1
#define STATUS_NOCONNECT   0    // Disconnected pipe that is note closed 
#define STATUS_TCP_SOCKET  1 
#define STATUS_IO_OK       5    // Used for IS_... test
#define STATUS_UDP_CLIENT  6
#define STATUS_UDP_SERVER  8
#define STATUS_CONNECT     10   // Used for IS_... test
#define STATUS_TCP_CLIENT  11
#define STATUS_TCP_SERVER  12
#define STATUS_UDP_CLIENT_CONNECT 18
#define STATUS_UDP_SERVER_CONNECT 19

#define IS_STATUS_IO_OK(x) ((x)>=STATUS_IO_OK)
#define IS_STATUS_CONNECTED(x) ((x)>=STATUS_CONNECT)
#define IS_STATUS_UDP_NO_CON(x) ((x)==STATUS_UDP_CLIENT || (x)==STATUS_UDP_SERVER)
#define IS_STATUS_TCP_CONNECTED(x) ((x)==STATUS_TCP_CLIENT || (x)==STATUS_TCP_SERVER)

typedef struct
{
    char *ptr;       /* Pointer to buffert. */
    int len;         /* Length of allocated buffert. */
    int pos;         /* Length used of buffer for data storage.*/
} io_buff;

/* Structure that hold all information about a tcpip connection. */
typedef struct
{
    int fid;
    double writetimeout;
    double readtimeout;
    struct sockaddr_in remote_addr;
    io_buff write;
    io_buff read;
    int status;       /* STATUS_... FREE, NOCONNECT, SERVER, CLIENT ... */
    char callback[CBNAMELEN+1];
} con_info;


/////////////////////////////////////////////////////////////////////////////////////////////////
/* Some global variables */
int        gret_args=0;         /* Global variable that holds number of matlab return argumens returned */
int            gnlhs;           /* number of expected outputs */
mxArray       **gplhs;          /* array of pointers to output arguments */
int            gnrhs;           /* number of inputs */
const mxArray  **gprhs;         /* array of pointers to input arguments */

/* Global list with static length of connections structures holding info about current connection */
con_info con[MAX_CON];
int con_index=0;                   /* Current index possition for list of handlers */
unsigned long mex_call_counter=0;  /* Counter that counts how many calls that have been done to pnet */

/***********************************************************************/
void Print_Start_Message(){
    mexPrintf("\n===============================================================================\n"
	      "Loaded pnet MEX-file for the tcp/udp/ip-toolbox Compiled @ "
	      __DATE__ " " __TIME__  "\n"
	      VERSION "\n"
	      "Copyright (C) Peter Rydesater, Sweden, et al. , 1998 - 2003\n"
	      "GNU General Public License, se license.txt for full license notis.\n"
	      "You are allowed to (dynamicaly) link this file with non-free code. \n\n"
	      "   http://www.rydesater.com \n\n"
	      "===============================================================================\n\n"
	      );
}

/***********************************************************************/
int myoptstrcmp(const char *s1,const char *s2)
{
    int val;
    while( (val= toupper(*s1) - toupper(*s2))==0 ){
	if(*s1==0 || *s2==0) return 0;
	s1++;
	s2++;
	while(*s1=='_') s1++;
	while(*s2=='_') s2++;
    }
    return val;
}

/********************************************************************/
/* A "wrapper" function for memory allocation. Most for debuging /tracing purpose */
void *myrealloc(char *ptr,int newsize)
{
    ptr=realloc(ptr,newsize);
    if(ptr==NULL && newsize>0)
	mexErrMsgTxt("Internal out of memory!");
    return ptr;
}

/********************************************************************/
/* A "wrapper" function for memory allocation. Most for debuging /tracing purpose */
void newbuffsize(io_buff *buff,int newsize)
{
    //    fprintf(stderr,"NEWSIZE:%d\n",newsize);
    if(newsize==-1){
	free(buff->ptr);
	buff->ptr=NULL;
	return;
    }
    if(newsize<buff->pos)
	newsize=buff->pos;
    if(newsize<256)
	newsize=256;
    if(newsize>buff->len){   // Grow....
	//	fprintf(stderr,"NEWSIZE UP %d -> %d\n",buff->len,newsize*2);
	buff->ptr=myrealloc(buff->ptr,newsize*2);
	buff->len=newsize*2;
    }else if(newsize*4 < buff->len){ // Decrease...
	//	fprintf(stderr,"NEWSIZE DOWN %d -> %d\n",buff->len,newsize*2);
	buff->ptr=myrealloc(buff->ptr,newsize*2);
	buff->len=newsize*2;
    }
}

/********************************************************************/
mxClassID str2classid(const char *str)
{
    if(myoptstrcmp("CHAR",str)==0)	    return mxCHAR_CLASS;
    if(myoptstrcmp("DOUBLE",str)==0)	return mxDOUBLE_CLASS;
    if(myoptstrcmp("SINGLE",str)==0)	return mxSINGLE_CLASS;
    if(myoptstrcmp("INT8",str)==0)	    return mxINT8_CLASS;
    if(myoptstrcmp("UINT8",str)==0)	    return mxUINT8_CLASS;
    if(myoptstrcmp("INT16",str)==0)	    return mxINT16_CLASS;
    if(myoptstrcmp("UINT16",str)==0)	return mxUINT16_CLASS;
    if(myoptstrcmp("INT32",str)==0)	    return mxINT32_CLASS;
    if(myoptstrcmp("UINT32",str)==0)	return mxUINT32_CLASS;
    return mxCHAR_CLASS; // Default to char;
}

/********************************************************************/
mxClassID classid2size(const mxClassID id)
{
    if(id==mxCHAR_CLASS)	return 1;
    if(id==mxDOUBLE_CLASS)	return sizeof(double);
    if(id==mxSINGLE_CLASS)	return sizeof(float);
    if(id==mxINT8_CLASS)	return 1;
    if(id==mxUINT8_CLASS)	return 1;
    if(id==mxINT16_CLASS)	return 2;
    if(id==mxUINT16_CLASS)	return 2;
    if(id==mxINT32_CLASS)	return 4;
    if(id==mxUINT32_CLASS)	return 4;
    mexErrMsgTxt("Non supported datatype!");
    return 0;
}

/* Windows implementation of perror() function */
#ifdef WIN32
/********************************************************************/
/*
void perror(const char *context )
{
    int wsa_err;
    wsa_err = WSAGetLastError();
    mexPrintf( "[%s]: WSA error: ", context );
    switch ( wsa_err )
    {
    case WSANOTINITIALISED: mexPrintf( "WSANOTINITIALISED\n" ); break;
    case WSAENETDOWN:       mexPrintf( "WSAENETDOWN      \n" ); break;
    case WSAEADDRINUSE:     mexPrintf( "WSAEADDRINUSE    \n" ); break;
    case WSAEACCES:         mexPrintf( "WSAEACCES        \n" ); break;
    case WSAEINTR:          mexPrintf( "WSAEINTR         \n" ); break;
    case WSAEINPROGRESS:    mexPrintf( "WSAEINPROGRESS   \n" ); break;
    case WSAEALREADY:       mexPrintf( "WSAEALREADY      \n" ); break;
    case WSAEADDRNOTAVAIL:  mexPrintf( "WSAEADDRNOTAVAIL \n" ); break;
    case WSAEAFNOSUPPORT:   mexPrintf( "WSAEAFNOSUPPORT  \n" ); break;
    case WSAEFAULT:         mexPrintf( "WSAEFAULT        \n" ); break;
    case WSAENETRESET:      mexPrintf( "WSAENETRESET     \n" ); break;
    case WSAENOBUFS:        mexPrintf( "WSAENOBUFS       \n" ); break;
    case WSAENOTSOCK:       mexPrintf( "WSAENOTSOCK      \n" ); break;
    case WSAEOPNOTSUPP:     mexPrintf( "WSAEOPNOTSUPP    \n" ); break;
    case WSAESHUTDOWN:      mexPrintf( "WSAESHUTDOWN     \n" ); break;
    case WSAEWOULDBLOCK:    mexPrintf( "WSAEWOULDBLOCK   \n" ); break;
    case WSAEMSGSIZE:       mexPrintf( "WSAEMSGSIZE      \n" ); break;
    case WSAEHOSTUNREACH:   mexPrintf( "WSAEHOSTUNREACH  \n" ); break;
    case WSAEINVAL:         mexPrintf( "WSAEINVAL        \n" ); break;

    case WSAECONNREFUSED:   mexPrintf( "WSAECONNREFUSED  \n" ); break;
    case WSAECONNABORTED:   mexPrintf( "WSAECONNABORTED  \n" ); break;
    case WSAECONNRESET:     mexPrintf( "WSAECONNRESET    \n" ); break;
    case WSAEISCONN:        mexPrintf( "WSAEISCONN       \n" ); break;
    case WSAENOTCONN:       mexPrintf( "WSAENOTCONN      \n" ); break;
    case WSAETIMEDOUT:      mexPrintf( "WSAETIMEDOUT     \n" ); break;
    default:                mexPrintf( "Unknown(%d) error!\n", wsa_err ); break;
    }
    return;
}
 **/
#endif

/********************************************************************/
/*Makes byte swapping, or not depending on the mode argument        */
void byteswapdata(char *ptr,const int elements,const int elementsize,int mode)
{
    // MODE=0 Do nothing, MODE=1 Swap, MODE=2 network byte order, MODE=3 Intel byte order.
#ifndef SWAPDATA
#define SWAPDATA(a,b) { a^=b; b^=a; a^=b; }
#endif
    // A little smart ckeck of byte the machine byte order.
    const int ordertest=1;
    const char *is_intel_order=(const char *)(&ordertest);
    //    fprintf(stderr,"SWAP FUNCTION...E:%d SI:%d\n",elements,elementsize);
    if(elementsize<2) return;
    if(is_intel_order[0]==1 && mode==2)   mode=1;
    if(is_intel_order[0]==0 && mode==3)   mode=1;
    if(mode==1){
	int e;
	//	fprintf(stderr,"SWAP DATA\n");
	switch(elementsize){
	case 2: for(e=0;e<elements*elementsize;e+=elementsize)
	    SWAPDATA(ptr[e],ptr[e+1]) break;
	case 3: for(e=0;e<elements*elementsize;e+=elementsize)
	    SWAPDATA(ptr[e],ptr[e+2])  break;
	case 4: for(e=0;e<elements*elementsize;e+=elementsize)
	    SWAPDATA(ptr[e],ptr[e+3]) SWAPDATA(ptr[e+1],ptr[e+2])  break;
	case 5: for(e=0;e<elements*elementsize;e+=elementsize)
	    SWAPDATA(ptr[e],ptr[e+4]) SWAPDATA(ptr[e+1],ptr[e+3])  break;
	case 6: for(e=0;e<elements*elementsize;e+=elementsize)
	    SWAPDATA(ptr[e],ptr[e+5]) SWAPDATA(ptr[e+1],ptr[e+4]) SWAPDATA(ptr[e+2],ptr[e+3])  break;
	case 7: for(e=0;e<elements*elementsize;e+=elementsize)
	    SWAPDATA(ptr[e],ptr[e+6]) SWAPDATA(ptr[e+1],ptr[e+5]) SWAPDATA(ptr[e+2],ptr[e+4])  break;
	case 8: for(e=0;e<elements*elementsize;e+=elementsize)
	    SWAPDATA(ptr[e],ptr[e+7]) SWAPDATA(ptr[e+1],ptr[e+6]) SWAPDATA(ptr[e+2],ptr[e+5]) SWAPDATA(ptr[e+3],ptr[e+4])  break;
	}
    }
}

/********************************************************************/
/*Makes byte swapping, or not depending on the mode argument        */
void byteswapcopy(char *dest,char *src,const int elements,const int elementsize,int mode)
{
    // MODE=0 Do nothing, MODE=1 Swap, MODE=2 network byte order, MODE=2 Intel byte order.
    // A little smart ckeck of byte the machine byte order.
    const int ordertest=1;
    const char *is_intel_order=(const char *)(&ordertest);
    if(is_intel_order[0]==1 && mode==2)   mode=1;    
    if(is_intel_order[0]==0 && mode==3)   mode=1;
    //    fprintf(stderr,"SWAP COPY E:%d SI:%d SWAP:%d\n",elements,elementsize,mode);
    if(mode==1){
	int e,n;
	//	fprintf(stderr,"SWAP COPY\n");
	for(e=0;e<elements;e++){
	    char *dp=&dest[e*elementsize];
	    char *sp=&src[e*elementsize];
	    for(n=0;n<elementsize;n++){
		dp[n]=sp[elementsize-1-n];
		//		fprintf(stderr,"E:%d/%d N:%d/%d\n",e,elements,n,elementsize);
	    }
	}
	//	fprintf(stderr,"SWAP COPY END\n");
    }
    else
	    memmove(dest,src,elements*elementsize);
}

/********************************************************************/
/* DEBUGING FUNCTION */
void __debug_view_con_status(char *str)
{
    int a;
    mexPrintf("%s\n",str);
    for(a=0;a<5;a++)
    {
	mexPrintf("[%02d] FID:%02d STATUS:%02d WRT.POS:%d RD.POS:%d ",a,con[a].fid,con[a].status,con[a].write.pos,con[a].read.pos);
	if(con[a].read.ptr)
	    mexPrintf("RD: %02x %02x %02x %02x %02x %02x %02x %02x ",
		      (int)con[a].read.ptr[0],(int)con[a].read.ptr[1],(int)con[a].read.ptr[2],(int)con[a].read.ptr[3],
		      (int)con[a].read.ptr[4],(int)con[a].read.ptr[5],(int)con[a].read.ptr[6],(int)con[a].read.ptr[7]);
	if(con[a].write.ptr)
	    mexPrintf("WR: %02x %02x %02x %02x %02x %02x %02x %02x ",
		      (int)con[a].write.ptr[0],(int)con[a].write.ptr[1],(int)con[a].write.ptr[2],(int)con[a].write.ptr[3],
		      (int)con[a].write.ptr[4],(int)con[a].write.ptr[5],(int)con[a].write.ptr[6],(int)con[a].write.ptr[7]);
	if(a==con_index)
	    mexPrintf("<--\n");
	else
	    mexPrintf("\n");
    }
    mexPrintf("--------------------\n");
}

#ifdef WIN32
/********************************************************************/
/* Portable time function using matlabs NOW                         */
double my_now(){
    double sec;
    
    sec = ((double) timeGetTime()) / 1000.0;
    return(sec);
    
  /* 
    double dotimenow;
    static double lastdotime;
    mxArray *plhs[1]={NULL};
    mxArray *prhs[1]={NULL};
    mexCallMATLAB(1,plhs,0,prhs,"now");
    sec=mxGetScalar(plhs[0])*60*60*24; //Return time as sec from 1970
    dotimenow=floor(sec*10)/10; // Do calls every 1/10 Sec
    mxDestroyArray(plhs[0]);
    if(lastdotime!=dotimenow){  //Call drawnow once evry X second
	    int ret=mexCallMATLAB(0,plhs,0,prhs,"drawnow");
	    //mexPrintf("wait... drawnow returns: %d\n",ret);
	    lastdotime= dotimenow;
    }
    return sec;   
  */
}
#else
double my_now()
{
    struct timeval tv;
    double sec;
    gettimeofday(&tv, NULL);
    sec = (double) tv.tv_sec + ((double) tv.tv_usec) / (double) 1e6;
    return(sec);
}
#endif

/*******************************************************************************/
/* Checks that given index is valid index and set current index, "con_index"   */
/* to that point. If index is CON_FREE (-1) then is search done for a free one */
/* Returns 1 On success. 0 on error                                            */
int move_con(int idx)
{
    if(idx>=MAX_CON)
	mexErrMsgTxt("Unvalid value of handler!\n");
    if(idx>=0)
    {
	con_index=idx;
	if(con[con_index].status==STATUS_FREE)
	    mexErrMsgTxt("No valid handler! already closed?");
	return 1;
    }
    debug_view_con_status("START MOVE");
    if(idx==CON_FREE)    /* Move con_index until it find a free non used struct */
    {
	for(con_index=0;con_index<MAX_CON;con_index++)
	{
	    debug_view_con_status("STEP MOVE");
	    if(con[con_index].status==STATUS_FREE)
		return 1;
	}
	mexErrMsgTxt("To many open connection! Forgot to close old connections?");
    }
    if(idx<0)
	mexErrMsgTxt("Unvalid value of handler!");
    return 0;
}

/**********************************************************************************************/
/* Returns true if specified argument exist                                                  */
int my_mexIsInputArgOK(const int argno)
{
    //    fprintf(stderr,"IS_INPUT_ARG_OK NO:%d of %d\n",argno,gnrhs);        // DEBUG
    if(gnrhs>argno)
	return 1;
    return 0;
}

/******************************************************************************************************/
/* Returns specified input argument as scalar. Global and error tolerant replacement for mxGetScalar */
const mxArray *my_mexInputArg(const int argno)
{
    //    fprintf(stderr,"GET_INPUT_ARG NO:%d\n",argno);                      // DEBUG
    if(!my_mexIsInputArgOK(argno))
	mexErrMsgTxt("Missing input argument.");
    return gprhs[argno];
}

/****************************************************************************************************************/
/* Returns pointer to static buffer (max 80 chars) holding a string in argument argno Global and error tolerant */
const char *my_mexInputOptionString(const int argno)
{
    static char buff[80+1];
    buff[0]=0;
    if(my_mexIsInputArgOK(argno))
	if(mxIsChar(my_mexInputArg(argno)))
	   mxGetString(my_mexInputArg(argno),buff,80);
    return buff;
}

/****************************************************************************************************************/
/* Returns pointer to static buffer (max 80 chars) holding the first string with non numeric,nonempty contents  */
const char *my_mexFindInputString(int argno)
{
    const char *str;
    static char buff[1];
    buff[0]=0;
    while(my_mexIsInputArgOK(argno)){
	str=my_mexInputOptionString(argno);
	if(!isdigit(str[0]) && str[0]!=0 )
	    return str;
	argno++;
    }
    return buff;
}

/*********************************************************************/
/* Returns true if if finds option opt at argument argno or later    */
int my_mexFindInputOption(int argno,const char *opt)
{
    char buff[80+1];
    while(my_mexIsInputArgOK(argno))
    {
	buff[0]=0;
	mxGetString(my_mexInputArg(argno),buff,80);
	if(myoptstrcmp(buff,opt)==0)
	    return 1;
	argno++;
    }
    return 0;
}

/***********************************************************************************************************/
/* Returns specified input arguments cell as scalar. Global and error tolerant replacement for mxGetScalar */
double my_mexInputCell(const int argno,int cell_no)
{
    if(!mxIsDouble(my_mexInputArg(argno)))
	mexErrMsgTxt("Argument has wrong datatype.");
    if(cell_no>=mxGetNumberOfElements(my_mexInputArg(argno)))
	mexErrMsgTxt("Argument has wrong number of cells");
    return mxGetPr(my_mexInputArg(argno))[cell_no];
}

/******************************************************************************************************/
/* Returns specified input argument as scalar or a scalar product of its elements.                    */
/* Global and error tolerant replacement for mxGetScalar                                              */
int my_mexInputSize(const int argno)
{
    if(!my_mexIsInputArgOK(argno))
	return DEFAULT_INPUT_SIZE;
    if(mxIsChar(my_mexInputArg(argno))){
	const int ch=(my_mexInputOptionString(argno)[0]);
	if(!isdigit(ch))
	    return DEFAULT_INPUT_SIZE;
	else
	    return atoi(my_mexInputOptionString(argno));
    }
    if(mxGetNumberOfElements(my_mexInputArg(argno))>1){
	int val=1,n=0,el=mxGetNumberOfElements(my_mexInputArg(argno));
	for(n=0;n<el;n++)
	    val*=(int)my_mexInputCell(argno,n);
	return val;
    }else
	return (int)mxGetScalar(my_mexInputArg(argno));
}

/******************************************************************************************************/
/* Returns specified input argument as scalar. Global and error tolerant replacement for mxGetScalar */
double my_mexInputScalar(const int argno)
{
    if(mxIsChar(my_mexInputArg(argno)))
	return atof(my_mexInputOptionString(argno));
    else
	return mxGetScalar(my_mexInputArg(argno));
}

/********************************************************************/
/* Copys a matlab Char array to a char * string buffer              */
int my_mexInputArray2Buff(const int argno,io_buff *buff)
{
    mxClassID id=mxGetClassID(my_mexInputArg(argno));
    int si=classid2size(id);
    int len=mxGetNumberOfElements(my_mexInputArg(argno));
    int swap=2;
    if(my_mexFindInputOption(argno+1,"NATIVE"))  swap=0;
    if(my_mexFindInputOption(argno+1,"SWAP"))    swap=1;
    if(my_mexFindInputOption(argno+1,"NETWORK")) swap=2;
    if(my_mexFindInputOption(argno+1,"INTEL"))   swap=3;
    
    newbuffsize(buff,buff->pos+len*si);

    if(id==mxCHAR_CLASS){
	mxChar *ptr = (mxChar *)mxGetData(gprhs[argno]);
	int a;
	for(a=0;a<len;a++)
	    buff->ptr[buff->pos++]=(char)(unsigned char)ptr[a];
    }else{
	char *ptr = (char *)mxGetData(gprhs[argno]);
	byteswapcopy(&buff->ptr[buff->pos],ptr,len,si,swap);
	buff->pos+=(len*si);
    }
    return len;
}

/*******************************************************************************/
/* Puts double scalar in matlab variable in the array of return argument for mex*/
void my_mexReturnValue(double val)
{
    if((gret_args>gnlhs) && (gret_args>1))
	return;
    gplhs[gret_args]=mxCreateDoubleMatrix(1,1,mxREAL);
    if(gplhs[gret_args]==NULL)
	mexErrMsgTxt("Matrix creation error! Lack of memory?");
    else
    {
	*mxGetPr(gplhs[gret_args])=val;
	gret_args++;
    }
//    mexPrintf("DEBUG MEX RETURN VALUE:%g\n",val);  // DEBUG
}

/*******************************************************************************/
/* Puts double matrix in matlab variable in the array of return argument for mex*/
void my_mexReturnMatrix(int rows, int cols, double *vals)
{
    double *pr;
    if((gret_args>gnlhs) && (gret_args>1))
	return;
    gplhs[gret_args] = mxCreateDoubleMatrix( rows, cols, mxREAL);
    if( gplhs[gret_args] == NULL)
    	mexErrMsgTxt("Matrix creation error");
    pr = (double *)mxGetPr(gplhs[gret_args]);
    memcpy(pr,vals,rows*cols*sizeof(double));
    gret_args++;
}

/********************************************************************************/
/* Puts string as matlab char char variable in array of return argument for mex */
void my_mexReturnString(const char *str)
{
    if(gret_args>gnlhs && gret_args>1)
	return;
    gplhs[gret_args]=mxCreateString(str);
    if(gplhs[gret_args]==NULL)
	mexErrMsgTxt("String creation error! Lack of memory?");
    gret_args++;
}

/******************************************************************************/
/* Puts data from buffer into next MATLAB return Array                        */
void my_mexReturnArrayFromBuff(const int argno,io_buff *buff,const int line)
{
    const int maxelements=my_mexInputSize(argno);
    const mxClassID id=str2classid(my_mexInputOptionString(argno+1));
    mwSize dims[20]={0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0 };
    const int si=classid2size(id);
    int returnelements= ( (buff->pos/si)< maxelements )?(buff->pos/si):maxelements;
    int deleteelements=returnelements;
    int swap=2;
    int return_no_dims= my_mexIsInputArgOK(argno) && !mxIsChar(my_mexInputArg(argno))?mxGetNumberOfElements(my_mexInputArg(argno)):1;

    if(my_mexFindInputOption(argno+1,"NATIVE"))  swap=0;
    if(my_mexFindInputOption(argno+1,"SWAP"))    swap=1;
    if(my_mexFindInputOption(argno+1,"NETWORK")) swap=2;
    if(my_mexFindInputOption(argno+1,"INTEL"))   swap=3;
    if(return_no_dims>20)
	mexErrMsgTxt("To many dimentions to return.");
    debug_view_con_status("GET_ARRAY");
    if(line){
	int n=-7;
	if(id!=mxCHAR_CLASS && return_no_dims)
	    mexErrMsgTxt("'READLINE' works only with datatype char and non fixed blocksize");
	for(n=0;n<returnelements;n++)
	    if(buff->ptr[n]=='\n')
		break;
	if(n==maxelements)                             // If no new-line found inside limit...
	    deleteelements=returnelements=maxelements; // ...return first part of splited line.
	else if(n==returnelements)                     // If new-line not recived inside limit...
	    deleteelements=returnelements=0;           // ...return empty string, and delete nothing.
	else if(n>0 && buff->ptr[n-1]=='\r')           // If(*3) new-line, return line of char but not nl chars.
	   deleteelements=2+(returnelements=n-1);
	else
	   deleteelements=1+(returnelements=n);
	return_no_dims=1;
    }
    if(return_no_dims>1){                 // If shape of return argument is specified.....
	if(returnelements==maxelements){        // ...then only accept correct shape.
	    int n;
	    for(n=0;n<return_no_dims;n++)
		dims[n]=(mwSize) my_mexInputCell(argno,n);
	}
    }else if(returnelements>0){           // else... Just return row of available elements
	dims[0] = 1;
	dims[1] = (mwSize) returnelements;
	return_no_dims=2;
    }
    if(! (gret_args>gnlhs && gret_args>1) ){
	/* Create a 2-Dimensional character mxArray.*/
	if( dims[0]==0)
	    gplhs[gret_args] = mxCreateNumericArray(0,dims,id,mxREAL);
	else
	    gplhs[gret_args] = mxCreateNumericArray(return_no_dims,dims,id,mxREAL);
	if(gplhs[gret_args] == NULL)
	    mexErrMsgTxt("Could not create return array.");
	if(dims[0]!=0){
	    if(id==mxCHAR_CLASS){
		int i;
		mxChar *p=(mxChar *)mxGetData(gplhs[gret_args]);
		for(i=0;i<returnelements;i++)
		    p[i]=buff->ptr[i];
	    }else{
		char *p=(char *)mxGetData(gplhs[gret_args]);
		byteswapcopy(p,buff->ptr,returnelements,si,swap);
	    }
	}
	gret_args++;
    }
       //    debug_view_con_status("GET_ARRAY NSTAN KLAR");
    // Delete from read buffer if not "VIEW" option and dims filled
    if(my_mexFindInputOption(argno+1,"VIEW")==0 && deleteelements>0 ){
	buff->pos-=deleteelements*si;
	memmove(buff->ptr,&buff->ptr[deleteelements*si],buff->pos);
	newbuffsize(buff,buff->pos);
    }
    // mexPrintf("DEBUG MEX RETURN ARRAY OF:%d\n",returnelements);  // DEBUG

    //    fprintf(stderr,"DIMS:[%d %d] DEL:%d RET:%d SI:%d POS:%d LEN:%d PTR:%08X\n",
    //	    dims[0],dims[1],deleteelements,returnelements,si,buff->pos,buff->len,buff->ptr);
}


/**********************************************************************/
int ipv4_lookup(const char *hostname,int port)
{
    struct in_addr addr;    /* my address information */
    /* Try IP address */
    addr.s_addr=inet_addr(hostname);
    if (addr.s_addr==INADDR_NONE){
	/*Can't resolve host string as dot notation IP number...
	  try lookup IP from hostname */
	struct hostent *he;
	//	fprintf(stderr,"Trying nameserverlookup:%s\n",hostname);
	he=gethostbyname(hostname);
	if(he==NULL){
	    mexPrintf("\nUNKNOWN HOST:%s\n",hostname);
	    return -1;  /* Can not lookup hostname */
	}
	addr = *((struct in_addr *)he->h_addr);
    }
    con[con_index].remote_addr.sin_family=AF_INET;
    con[con_index].remote_addr.sin_port=htons(port);
    con[con_index].remote_addr.sin_addr=addr;
    memset(&con[con_index].remote_addr.sin_zero, 0,8);
    return 0;
}


/**********************************************************************/
/* Writes from specified position (pointer) in buffer of spec. length */
int writedata()
{
    const double timeoutat=my_now()+con[con_index].writetimeout;
    int   len=con[con_index].write.pos;
    const char *ptr=con[con_index].write.ptr;
    const int  fid=con[con_index].fid;
    int sentlen=0;
    int retval=0;
    int lastsize=1000000;
    if(con[con_index].status<STATUS_IO_OK)
	return 0;
    //    if( !IS_STATUS_TCP_CONNECTED(con[con_index].status)  && len>65534 )   // TODO: Ta bort!
    //	len=65534;
    while(sentlen<len)
    {
	if(lastsize<1000)
	    usleep(DEFAULT_USLEEP);
	if(IS_STATUS_UDP_NO_CON(con[con_index].status))
	    retval = sendto(fid,&ptr[sentlen],len-sentlen,MSG_NOSIGNAL,
			    (struct sockaddr *)&con[con_index].remote_addr,sizeof(struct sockaddr));
	else
	    retval=send(fid,&ptr[sentlen],len-sentlen,MSG_NOSIGNAL);
	lastsize=retval>0?retval:0;
	sentlen+=lastsize;
	/*	if( retval==0){
		mexPrintf("\nREMOTE HOST DISCONNECTED\n");
		con[con_index].status=STATUS_NOCONNECT;
		break;
		}*/
	if(retval<0 && s_errno!=EWOULDBLOCK
	   //	   IFWINDOWS( && s_errno!=WSAECONNRESET  )           // DEBUG: REMOVE THIS LINE?
	   ){
	    con[con_index].status=STATUS_NOCONNECT;
	    perror( "sendto() / send()" );
	    mexPrintf("\nREMOTE HOST DISCONNECTED\n");
	    break;
	}
	if( !IS_STATUS_TCP_CONNECTED(con[con_index].status) && sentlen>0 )
	    break;
	if(timeoutat<=my_now())
	    break;
    }
    con[con_index].write.pos-=sentlen;
    memmove(con[con_index].write.ptr, &con[con_index].write.ptr[sentlen], con[con_index].write.pos);
    newbuffsize(&con[con_index].write,con[con_index].write.pos);
    return sentlen;
}

/********************************************************************/
/* Init current record with values                                  */
void init_con(int fid,int status)
{
    memset(&con[con_index],0,sizeof(con_info));
    con[con_index].fid=fid;
    con[con_index].status=status;
    con[con_index].writetimeout=DEFAULT_WRITETIMEOUT;
    con[con_index].readtimeout=DEFAULT_READTIMEOUT;
}

/********************************************************************/
/* Close con struct                                                 */
void close_con()
{
    if(con[con_index].fid>=0)
	close(con[con_index].fid);
    else
	mexWarnMsgTxt("Closing already closed connection!");
    newbuffsize(&con[con_index].write,-1);
    newbuffsize(&con[con_index].read,-1);
    init_con(-1,STATUS_FREE);
}

/*******************************************************************      */
/* Function to close all still open tcpip connections */
int closeall(void)
{
    int flag=0;
    for(con_index=0;con_index<MAX_CON;con_index++)
	if(con[con_index].fid>=0) {  /* Already closed?? */
	    close_con();
	    flag=1;
	}
    return flag;
}

/****************************************************************************/
/* This function is called on unloading of mex-file                         */
void CleanUpMex(void)
{
    if(closeall()) /* close all still open connections...*/
	mexWarnMsgTxt("Unloading mex file. Unclosed tcp/udp/ip connections will be closed!");
    IFWINDOWS(   WSACleanup();  );
}

/********************************************************************/
/* Read to fill input buffer with specified length from UDP or TCP network*/
int read2buff(const int len,int newline,int noblock)
{
    const double timeoutat=my_now()+con[con_index].readtimeout;
    int retval=-1;

    if(len<con[con_index].read.pos)              /* If enouth in buffer then return */
	return len;
    if(0==IS_STATUS_IO_OK(con[con_index].status))/* If not read/write fid (broken pipe) then exit.*/
	if(len<con[con_index].read.pos)
	    return len;
	else
	    return con[con_index].read.pos;

    /* Resize readbuffer to needed size */
    if(con[con_index].read.len<len)
	newbuffsize(&con[con_index].read,len);

    while(1){
	int readlen=len-con[con_index].read.pos;
//	mexPrintf("DEBUG: READLINE: readlen:%d\n",readlen);
	if(readlen>0){
	    if(IS_STATUS_CONNECTED(con[con_index].status))
		retval=recv(con[con_index].fid,&con[con_index].read.ptr[con[con_index].read.pos],readlen ,MSG_NOSIGNAL);
	    else{
		struct sockaddr_in my_addr;
		int fromlen=sizeof(my_addr); 

		// Copy 0.0.0.0 adress and 0 port to remote_addr as init-value.
		memset(&my_addr,0,sizeof(my_addr));
		con[con_index].remote_addr.sin_addr = my_addr.sin_addr;
		con[con_index].remote_addr.sin_port = my_addr.sin_port;
		retval=recvfrom(con[con_index].fid,&con[con_index].read.ptr[con[con_index].read.pos],
				readlen,MSG_NOSIGNAL,(struct sockaddr *)&my_addr, &fromlen);
		if (retval>0){
		    con[con_index].remote_addr.sin_addr = my_addr.sin_addr;
		    con[con_index].remote_addr.sin_port = htons((unsigned short int)ntohs(my_addr.sin_port));
		}
	    }
	    if( retval==0){
		mexPrintf("\nREMOTE HOST DISCONNECTED\n");
		con[con_index].status=STATUS_NOCONNECT;
		break;
	    }
	    if(retval<0 && s_errno!=EWOULDBLOCK
	       //	       IFWINDOWS( && s_errno!=WSAECONNRESET )// DEBUG: REMOVE THIS LINE?
	       ) {
		con[con_index].status=STATUS_NOCONNECT;
		perror( "recvfrom() or recv()" );
		break;
	    }
	}
	//	fprintf(stderr,"RET:%d/%d ",retval,s_errno);
	readlen=retval>0?retval:0;
	con[con_index].read.pos+=readlen;
	if( !IS_STATUS_TCP_CONNECTED(con[con_index].status) && con[con_index].read.pos>0 )
	    break;
	if( con[con_index].read.pos>=len )
	    break;
	if(noblock || timeoutat<=my_now())
	    break;
	if(newline){
	    int n;
	    for(n=0;n<con[con_index].read.pos;n++)
		if(con[con_index].read.ptr[n]=='\n')
		    return con[con_index].read.pos;
	}
	if(readlen<1000)
	    usleep(DEFAULT_USLEEP);
    }
    return con[con_index].read.pos;
}

/***************************************************************************/   // BORT???
/* Read specified length & type from UDP or TCP network to input buffer    */
int readtype2buff(int len,mxClassID datatype,int newline,int noblock)
{
    const int si=classid2size(datatype);
    return read2buff(len*si,newline,noblock)/si;
}

/********************************************************************/
/* Function Creating a tcpip connection and returns handler number  */
int tcp_connect(const char *hostname,const int port)
{
    if(ipv4_lookup(hostname,port)==-1)
	return -1;
    con[con_index].fid=socket(AF_INET, SOCK_STREAM, 0);
    if(con[con_index].fid== CON_FREE){
	/*Can't open socket */
	close_con();
	return -1;
    }
    if(connect(con[con_index].fid,(struct sockaddr *)&con[con_index].remote_addr,sizeof(struct sockaddr)) == -1){
	/*Can't connect to remote host. */
	close_con();
	return -1;
    }
    con[con_index].status=STATUS_TCP_CLIENT;
    nonblockingsocket(con[con_index].fid); /* Non blocking read! */
    return con_index;
}

/*******************************************************************     
 Function Creating a TCP server socket                                 
 or a connectionless UDP client socket.
*/
int tcp_udp_socket(int port,int dgram_f)
{
    int sockfd;
    struct sockaddr_in my_addr;    /* my address information */
    const int on=1;

    #ifndef WIN32
        #ifndef IPTOS_LOWDELAY
        #define IPTOS_LOWDELAY          0x10
        #endif
        int tos = IPTOS_LOWDELAY;
    #endif

    if(dgram_f)
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    else
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd==-1)
	return -1;
    my_addr.sin_family = AF_INET;         /* host byte order */
    my_addr.sin_port = htons(port);       /* short, network byte order */
    my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */
    memset(&(my_addr.sin_zero),0, 8);        /* zero the rest of the struct */
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(const char *)&on,sizeof(on));
    if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1)
    {
	close(sockfd);
	return -1;
    }
    listen(sockfd,BACKLOG);
    nonblockingsocket(sockfd);

    /* Try to enable low-latency send/receive operations on socket: */
    #ifndef WIN32
    if (-1 == setsockopt(sockfd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
        mexPrintf("pnet: Warning: Could not enable low-latency mode on socket! [%s]\n", strerror(errno));
    }
    #endif

    return sockfd;
}

/*****************************************************************/
/* Listen to socket and returns connection if there is one...
   else it returns -1 */
int tcpiplisten(int noblock)
{
    const double timeoutat=my_now()+con[con_index].readtimeout;
    int new_fd;
    const int sock_fd= con[con_index].fid;
    int sin_size = sizeof(struct sockaddr_in);
    move_con(CON_FREE);        /* Find a new free con struct for the new connection.... */
    while(1){
	    if ((new_fd = accept(sock_fd, (struct sockaddr *)&con[con_index].remote_addr,&sin_size)) > -1)
            break;
        if(noblock || timeoutat<=my_now())
	        return -1;
	    usleep(DEFAULT_USLEEP);
    }
    nonblockingsocket(new_fd); /* Non blocking read! */
    setsockopt(new_fd,SOL_SOCKET,SO_KEEPALIVE,(void *)1,0); /* realy needed? */
    con[con_index].fid=new_fd;
    con[con_index].status=STATUS_TCP_SERVER;
    return con_index;
}

/*****************************************************************/
/* Make a UDP socket be a "connected" UDP socket                 */
int udp_connect(const char *hostname,int port)
{	
    if(con[con_index].status < STATUS_UDP_CLIENT)
	mexErrMsgTxt("Must pass UDP client or server handler!");
    if(ipv4_lookup(hostname,port)==-1)
	return -1;
    if(connect(con[con_index].fid,(struct sockaddr *)&con[con_index].remote_addr,sizeof(struct sockaddr)) == -1)
	return -1; /*Can't connect to remote host. */
    if(con[con_index].status == STATUS_UDP_CLIENT)
	con[con_index].status = STATUS_UDP_CLIENT_CONNECT;
    else
	con[con_index].status = STATUS_UDP_SERVER_CONNECT;
    nonblockingsocket(con[con_index].fid); /* Non blocking read! */
    return con[con_index].status;
}

/*****************************************************************/
/*                                                               */
/*    ----Main function that is called from matlab--------       */
/*                                                               */
void mexFunction(
    int           nlhs,           /* number of expected outputs */
    mxArray       *plhs[],        /* array of pointers to output arguments */
    int           nrhs,           /* number of inputs */
    const mxArray *prhs[]         /* array of pointers to input arguments */
    )
{
    char fun[80+1];
    /* Initialization on first call to the mex file */
    if(mex_call_counter==0)
    {
#ifdef WIN32
	WORD wVersionRequested;
	WSADATA wsaData;
	int wsa_err;    
	wVersionRequested = MAKEWORD( 2, 0 );
	wsa_err = WSAStartup( wVersionRequested, &wsaData );
	if (wsa_err)
	    mexErrMsgTxt("Error starting WINSOCK32.");
#endif
    Print_Start_Message();

	mexAtExit(CleanUpMex);
	/* Init all connecttions to to free */
	for(con_index=0;con_index<MAX_CON;con_index++)
	    init_con(-1,STATUS_FREE);
        con_index=0;
    }
    mex_call_counter++;
    debug_view_con_status("ENTER_MEX");

    /* GLOBAL IN-OUT ARGUMENTS */
    gnlhs=nlhs;       /* number of expected outputs */
    gplhs=plhs;       /* array of pointers to output arguments */
    gnrhs=nrhs;       /* number of inputs */
    gprhs=prhs;       /* array of pointers to input arguments */
    gret_args=0;      /* No return argumens returned */


    if(mxIsChar(my_mexInputArg(0))){
	/* GET FIRST ARGUMENT -- The "function" name */
	strncpy(fun,my_mexInputOptionString(0),80);
//	mexPrintf("DEBUG MEX(1):[%d] %s\n",con_index,fun);   // DEBUG
	
	/* Find of the function name corresponds to a non connection associated function */
	if(myoptstrcmp(fun,"CLOSEALL")==0){
	    closeall();
	    return;
	}
	if(myoptstrcmp(fun,"TCPCONNECT")==0){
	    const int port=(int)my_mexInputScalar(2);
	    const char *hostname=my_mexInputOptionString(1);
	    move_con(STATUS_FREE);
	    my_mexReturnValue(tcp_connect(hostname,port));
	    return;
	}
	if(myoptstrcmp(fun,"TCPSOCKET")==0){
	    const int fd=tcp_udp_socket((int)my_mexInputScalar(1), 0);
	    if(fd>=0){
		move_con(STATUS_FREE);
		init_con(fd,STATUS_TCP_SOCKET);
		my_mexReturnValue(con_index);
	    }else
		my_mexReturnValue(-1);
	    return;
	}
	if(myoptstrcmp(fun,"UDPSOCKET")==0){
	    const int fd=tcp_udp_socket((int)my_mexInputScalar(1), 1);
	    if(fd>=0){
		move_con(STATUS_FREE);
		init_con(fd,STATUS_UDP_CLIENT);
		my_mexReturnValue(con_index);
	    }else
		my_mexReturnValue(-1);
	    return;
	}
	if(myoptstrcmp(fun,"DOCALLBACKS")==0){
	    return;
	}
    }
    /* Get connection handler and suppose that it is a connection assosiated function */
    /* Find given handel */
    //       if(strncasecmp(fun,"DEF",3)!=0)
    if(move_con((int)my_mexInputScalar(0))==0) 
	mexErrMsgTxt("Unknown connection handler");
    strncpy(fun,my_mexInputOptionString(1),80);
 //   mexPrintf("DEBUG MEX(2):[%d] %s\n",con_index,fun);   // DEBUG
    debug_view_con_status("CON_MOVED!!");
       
       if(myoptstrcmp(fun,"CLOSE")==0){
	   close_con();
	return;
    }
    if(myoptstrcmp(fun,"TCPLISTEN")==0){
	if(con[con_index].status!=STATUS_TCP_SOCKET)
	    mexErrMsgTxt("Invalid socket for LISTEN, Already open, or UDP?...");
	my_mexReturnValue(tcpiplisten(my_mexFindInputOption(2,"noblock")));
	return;
    }
    /* MATLAB called with status = UDP_CONNECT(fid, ip, port) */
    if(myoptstrcmp(fun,"UDPCONNECT")==0){
	const int port=(int)my_mexInputScalar(3);
	const char *hostname=my_mexInputOptionString(2);
	my_mexReturnValue(udp_connect(hostname,port));
	return;
    }
    if(myoptstrcmp(fun,"WRITE")==0){
	my_mexInputArray2Buff(2,&con[con_index].write);
	if(IS_STATUS_TCP_CONNECTED(con[con_index].status))
	    writedata();
	return;
    }
    if(myoptstrcmp(fun,"PRINTF")==0){
	mxArray *plhs[1]={NULL};
	if(gnrhs<3) return;
	mexCallMATLAB(1,plhs, gnrhs-2, (mxArray **)&(gprhs[2]),"sprintf");
	gprhs=(const mxArray **)plhs; gnrhs=1; // HACK: Move return arg from sprintf to input arg of this mex.
	my_mexInputArray2Buff(0,&con[con_index].write);
	if(IS_STATUS_TCP_CONNECTED(con[con_index].status))
	    writedata();
	    return;
    }
    if(myoptstrcmp(fun,"READ")==0){
	    if(IS_STATUS_TCP_CONNECTED(con[con_index].status))
	        readtype2buff( (int)my_mexInputSize(2),str2classid(my_mexFindInputString(2)),0,my_mexFindInputOption(2,"noblock"));
	    my_mexReturnArrayFromBuff(2,&con[con_index].read,0);
	    return;
    }
    if(myoptstrcmp(fun,"READLINE")==0){
    //	mexPrintf("DEBUG: READLINE....\n");
	    if(IS_STATUS_TCP_CONNECTED(con[con_index].status))
	        read2buff(my_mexInputSize(2),1,my_mexFindInputOption(2,"noblock"));
	    my_mexReturnArrayFromBuff(2,&con[con_index].read,1);
    //	mexPrintf("DEBUG: READLINE END\n");
	return;
    }
    if(myoptstrcmp(fun,"READTOFILE")==0){
	FILE *f=NULL;
	const int blocklen=my_mexInputSize(2+1);
	int readlen=blocklen;
	int writelen=0;
	if(IS_STATUS_TCP_CONNECTED(con[con_index].status))
	    readlen= read2buff(blocklen,0,(int)my_mexFindInputOption(2+1,"noblock"));
	if(readlen>0)
	    f=fopen(my_mexInputOptionString(2),(int)my_mexFindInputOption(2+1,"append")?"ab":"wb");
	if(f){
	    writelen=fwrite(con[con_index].read.ptr,1,readlen,f);
	    fclose(f);
	}
	// Delete from read buffer if not "VIEW" option and dims filled
	if(my_mexFindInputOption(2+1,"VIEW")==0 ){
	    con[con_index].read.pos-=writelen;
	    memmove(con[con_index].read.ptr,&con[con_index].read.ptr[writelen],con[con_index].read.pos);
	    newbuffsize(&con[con_index].read,con[con_index].read.pos);
	}
	my_mexReturnValue(writelen);
	return;
    }
    if(myoptstrcmp(fun,"WRITEFROMFILE")==0){
	int len=1024*1024*1024;int start=0;
	FILE *f=fopen(my_mexInputOptionString(2),"rb");
	if(f==NULL)
	    my_mexReturnValue(-1);
	if(my_mexIsInputArgOK(2+1))
	    start=(int)my_mexInputScalar(2+1);
	if(my_mexIsInputArgOK(2+2))
	    len=(int)my_mexInputScalar(2+2);
	else{
	    fseek(f,0,SEEK_END);
	    len=ftell(f)-start;
	}
	fseek(f,start,SEEK_SET);
	newbuffsize(&con[con_index].write,con[con_index].write.pos+len);
	len=fread(&con[con_index].write.ptr[con[con_index].write.pos],1,len,f);
	con[con_index].write.pos+=len;
	fclose(f);
	if(IS_STATUS_TCP_CONNECTED(con[con_index].status))
	    writedata();
	if(len<0)
	    my_mexReturnValue(-1);
	else
	    my_mexReturnValue(len);
	return;
    }
    if(myoptstrcmp(fun,"READPACKET")==0){
	    con[con_index].read.pos=0;
	    my_mexReturnValue(readtype2buff(my_mexInputSize(2),str2classid(my_mexInputOptionString(2+1)),0,
	        my_mexFindInputOption(2+1,"noblock")));
	    return;
    }
    if(myoptstrcmp(fun,"WRITEPACKET")==0){
	if(IS_STATUS_UDP_NO_CON(con[con_index].status))
	    ipv4_lookup(my_mexInputOptionString(2),my_mexInputScalar(3));
	my_mexReturnValue(writedata());
	con[con_index].write.pos=0;
	return;
    }
    if(myoptstrcmp(fun,"STATUS")==0){
	my_mexReturnValue(con[con_index].status);
	return;
    }
    if(myoptstrcmp(fun,"GETHOST")==0){
	int n;
	double ip_bytes[4] = {0, 0, 0, 0};
	const unsigned char *ipnr=(const unsigned char *)&con[con_index].remote_addr.sin_addr;
	for(n=0;n<4;n++)
	    ip_bytes[n] = (double)ipnr[n];
	my_mexReturnMatrix(1,4,ip_bytes);
	my_mexReturnValue((int)ntohs(con[con_index].remote_addr.sin_port));
	return;
    }
    if(myoptstrcmp(fun,"SETCALLBACK")==0){
	strncpy(con[con_index].callback, my_mexInputOptionString(2), CBNAMELEN);
	return;
    }
    if(myoptstrcmp(fun,"GETCALLBACK")==0){
	my_mexReturnString(con[con_index].callback);
	return;
    }
    if(myoptstrcmp(fun,"SETWRITETIMEOUT")==0){
	con[con_index].writetimeout=my_mexInputScalar(2);
	return;
    }
    if(myoptstrcmp(fun,"SETREADTIMEOUT")==0){
	con[con_index].readtimeout=my_mexInputScalar(2);
	return;
    }
    if(myoptstrcmp(fun,"debug")==0){
	mexPrintf("     FID:%d\n",con[con_index].fid);
	mexPrintf("  STATUS:%d\n",con[con_index].status);
	mexPrintf("WRITE  TO:%g\n",con[con_index].writetimeout);
	mexPrintf("WRITE PTR:%p\n",con[con_index].write.ptr);
	mexPrintf("WRITE POS:%d\n",con[con_index].write.pos);
	mexPrintf("WRITE LEN:%d\n",con[con_index].write.len);
	mexPrintf("READ  TO:%g\n",con[con_index].readtimeout);
	mexPrintf("READ PTR:%p\n",con[con_index].read.ptr);
	mexPrintf("READ POS:%d\n",con[con_index].read.pos);
	mexPrintf("READ LEN:%d\n",con[con_index].read.len);
	return;
    }
    mexErrMsgTxt("Unknown 'function name' in argument.");
}
