//
// Copyright (C) 1999-2004 Toshikaz Hirabayashi
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// TOSHIKAZ HIRABAYASHI BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
// OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Except as contained in this notice, the name of Toshikaz Hirabayashi shall
// not be used in advertising or otherwise to promote the sale, use or other
// dealings in this Software without prior written authorization from
// Toshikaz Hirabayashi.

#include <X11/Xlib.h>
#include <WScom.h>
#include <x11/WSDxappDev.h>
#include <x11/WSDxsocket.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <fcntl.h>
#ifndef MacOS
#include <sys/poll.h>
#endif //MacOS
#include <errno.h>
#include <sys/file.h>
#include <string.h>
#include <x11/WSxcom.h>

#ifdef SUN
#define SOCKLEN_T  int
#else //SUN
#ifdef MacOS
#define SOCKLEN_T  int
#else //MacOS
#define SOCKLEN_T  socklen_t
#endif //MacOS
#endif //SUN

WSMFclassInit(WSDxsocket,WSDxsocket);

WSDsocket* _xsocket_create_handler(){
  WSDxsocket* sock = new WSDxsocket();
  return sock;
};


class  _xsocket_init {
  public:
  _xsocket_init(){
    WSDsocket::setCreateHandler(_xsocket_create_handler);
//printf("WSDxsocket init\n");
  };
};
_xsocket_init   WSGIappXsocketInit;

//typedef struct pthread_sem_struct{
//  int locked;
//  pthread_mutex_t mutex;
//  pthread_cond_t lock_free;
//} pthread_sem_t;

// A new semaphore function for initializing.
int pthread_sem_init_np(pthread_sem_t* semp){
  semp->locked = 0;
  pthread_mutex_init(&(semp->mutex),NULL);
  pthread_cond_init(&(semp->lock_free),NULL);
  return 0;
}

// A new semaphore function for locking.
int pthread_sem_lock_np(pthread_sem_t* semp){
  pthread_mutex_lock(&(semp->mutex));
  while(semp->locked){
    pthread_cond_wait(&(semp->lock_free),&(semp->mutex));
  }
  semp->locked++;
  pthread_mutex_unlock(&(semp->mutex));
  return 0;
}

// A new semaphore function for unlocking.
int pthread_sem_unlock_np(pthread_sem_t* semp){
  pthread_mutex_lock(&(semp->mutex));
  if (semp->locked < 1){
    semp->locked = 0;
    pthread_mutex_unlock(&(semp->mutex));
    return -1;
  }
  semp->locked = 0;
  pthread_cond_broadcast(&(semp->lock_free));
  pthread_mutex_unlock(&(semp->mutex));
  return 0;
}

// A new semaphore function for unlocking.
int pthread_sem_destroy_np(pthread_sem_t* semp){
  pthread_cond_destroy(&(semp->lock_free));
  pthread_mutex_destroy(&(semp->mutex));
  return 0;
}


WSDxsocket::WSDxsocket(){
  _socket = 0; 
  _listened = False;
  _sem = NULL;
}

WSDxsocket::~WSDxsocket(){
  if (_sem != NULL){
    pthread_sem_unlock_np((pthread_sem_t*)_sem);
    _sem = NULL;
  }
  destroy();
}

long WSDxsocket::destroy(){
//printf("WSDxsocket::destroy _socket=0x%x\n",_socket);
  if (_socket != 0){
    _listened = False;
    _started_accept = False;
    ::shutdown(_socket,0);
    ::close(_socket);
//printf("WSDxsocket::destroy _socket=0x%x close done.\n",_socket);
    seterr();
    _socket = 0;
  }
  _listened = False;
  _started_accept = False;
  return WSDsocket::destroy();
}

long WSDxsocket::connect(){
  if (_socket == 0){
    initialize();
    if (_socket == 0){
      return WS_ERR;
    }
  }
  if (_udp != False){
    return WS_ERR;
  }
  struct sockaddr_in server;
  struct hostent  *hp;
  if (!strcmp(_dest_naddr,"")){
    hp = gethostbyname("localhost");
  }else{
    hp = gethostbyname((char*)_dest_naddr);
  }
  server.sin_family = AF_INET;
  if (hp == NULL){
    server.sin_addr.s_addr = (unsigned long)inet_addr((char*)_dest_naddr);
    if ( server.sin_addr.s_addr == (unsigned long)-1 ) {
fprintf(stderr,"WSDxsocket::connect invarid addr=%s\n",(char*)_dest_naddr);
      return WS_ERR;
    }
  }else{
    server.sin_addr.s_addr = *(unsigned long *)hp->h_addr;
  }
  server.sin_port = htons( _dest_port );

  if( ::connect(_socket, (struct sockaddr *)&server,sizeof server) < 0 ){
    seterr();
    close();
fprintf(stderr,"WSDxsocket::connect failed addr=%s port=%d\n",(char*)_dest_naddr,_dest_port);
    return WS_ERR;
  }
  return WS_NO_ERR;
}

long WSDxsocket::listen(){
  if (_socket == 0){
    initialize();
    if (_socket == 0){
      return WS_NO_ERR;
    }
  }
  if (_udp != False){
    return WS_ERR;
  }
  struct sockaddr_in server;

  server.sin_family = AF_INET;
  if (!strcmp((char*)_naddr,"")){
    server.sin_addr.s_addr = INADDR_ANY;
  }else{
    server.sin_addr.s_addr = (unsigned long)inet_addr((char*)_naddr);
//printf("WSDxsocket::listen addr=%s %x\n",(char*)_naddr, server.sin_addr.s_addr);
  }  
  server.sin_port = htons( _port );
  int val;
  val = 1;
  setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof val);
  if( bind(_socket, (struct sockaddr *)&server, sizeof server) < 0){
    seterr();
    return WS_ERR;
  }
  SOCKLEN_T len;
#if 0
  if(getsockname(_socket, (struct sockaddr *)&server, &len) < 0){
    seterr();
    return WS_ERR;
  }
#endif
  if ( ::listen(_socket, 5) != 0){
    seterr();
    return WS_ERR;
  }
  _listened = True;
  seterr();
  return WS_NO_ERR;
}
void* WSDxsocket::accept(){
  if (_socket == 0){
    initialize();
    if (_socket == 0){
      return NULL;
    }
  }
  if (_udp != False){
    return 0;
  }
  if (_listened == False){
    if (listen() != WS_NO_ERR){
      return NULL;
    }
  }
  struct sockaddr_in sock_addr;
  SOCKLEN_T hlen = sizeof(sock_addr);

  int sock = ::accept(_socket,(sockaddr*)&sock_addr,&hlen);
  if (sock < 0){
    seterr();
    return NULL;
  }
  _set_dest_addr_(htonl(sock_addr.sin_addr.s_addr));
  return (void*)sock;
}

struct tmp_data{
  WSDxsocket* obj;
  void(*hd)(WSDsocket*,void*,WSCulong);
};

void* WSDxsocket::_accept_thread_(void* ptr){
  tmp_data* dt = (tmp_data*)ptr;
  WSDxsocket* _this = dt->obj;
  void(*hd)(WSDsocket*,void*,WSCulong) = dt->hd;
  delete dt;
//printf("accept start... \n");

  Display* disp = XOpenDisplay((char*)NULL);
  if (disp == NULL){
//printf("return1\n");
    return 0;
  }
  while(1){
    struct sockaddr_in sock_addr;
    SOCKLEN_T hlen = sizeof(sock_addr);
//printf("accept start... sock=0x%x\n",_this->_socket);
    int sock = ::accept(_this->_socket,(sockaddr*)&sock_addr,&hlen);
//printf("accept done...\n");
    if (sock < 0){
      XCloseDisplay(disp);
//printf("return2\n");
      return 0;
    }
    if (_this->_stop_accept != False){
      XCloseDisplay(disp);
//printf("return3\n");
      _this->_stop_accept = False;
      return 0;
    }
    _accept_send_type data;
    data.sock = _this;
    data.socket = sock;
    data.addr = htonl(sock_addr.sin_addr.s_addr);
    data.hd = hd;

    Atom atom = XInternAtom(disp,"SOCK-ACCEPT",False);
    Window win = XtWindow(WSGIxwinAppDev()->appWidget());
    XClientMessageEvent  cm;
    cm.type = ClientMessage;
    cm.display = disp;
    cm.window = win;
    cm.message_type = atom;
    cm.format = 32;


    memcpy(cm.data.l,&data,sizeof(_accept_send_type));
    XSendEvent(disp,win,False,NoEventMask,(_XEvent *)&cm);
//printf("send message... win=0x%x\n",win);
    Window  win2  = XCreateSimpleWindow(disp,DefaultRootWindow(disp),400,400,
                               200,200,2, 0, 1);
    XEvent event;
    XSendEvent(disp,win2,False,NoEventMask,(_XEvent *)&cm);
    XNextEvent(disp, &event);
    XDestroyWindow(disp,win2);

  }
}
long WSDxsocket::_continue_udp_read_accept(){
//printf("WSDxsocket::_continue_udp_read_accept.. _sem=0x%x\n",_sem);
  if (_sem != NULL){
//printf("WSDxsocket::_continue_udp_read_accept unlock\n");
    pthread_sem_unlock_np((pthread_sem_t*)_sem);
    _sem = NULL;
    return WS_NO_ERR;
  }
  return WS_ERR;
}
void* WSDxsocket::_udp_read_thread_(void* ptr){
  tmp_data* dt = (tmp_data*)ptr;
  WSDxsocket* _this = dt->obj;
  void(*hd)(WSDsocket*,void*,WSCulong) = dt->hd;
  delete dt;

  Display* disp = XOpenDisplay((char*)NULL);
  if (disp == NULL){
//printf("return1\n");
    return NULL;
  }
  if (_this->_udp_binded == False){
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    if (!strcmp((char*)_this->_naddr,"")){
      server.sin_addr.s_addr = INADDR_ANY;
    }else{
      server.sin_addr.s_addr = (unsigned long)inet_addr((char*)_this->_naddr);
//printf("WSDxsocket::_udp_read_thread addr=%s %x\n",(char*)_this->_naddr, server.sin_addr.s_addr);
    }  
    server.sin_port = htons( _this->_port );
//printf("WSDxsocket::_udp_read_thread bind port=%d server.sin_port=%d\n",_this->_port,server.sin_port);
    if( bind(_this->_socket, (struct sockaddr *)&server, sizeof(server)) < 0){
      XCloseDisplay(disp);
      return NULL;
    }
    _this->_udp_binded = True;
  }

  while(1){
    WSCulong timeout = 0xffffffff;
//printf("WSDxsocket::_udp_read_thread start poll sock=0x%x\n",_this->_socket);
#ifndef MacOS
    struct pollfd fds;
    fds.fd = _this->_socket;
    fds.events = POLLIN;
    fds.revents = 0;
    long ret = poll( &fds, 1, timeout );
    if (ret < 1){
      XCloseDisplay(disp);
      return NULL;
    }
    if (fds.revents & POLLIN){
#else //MacOS
    struct timeval tm;
    tm.tv_sec = 0xffff;
    tm.tv_usec = 0;
    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(_this->_socket,&read_fds);
    long ret = select(FD_SETSIZE,&read_fds,NULL,NULL,&tm);
    if (ret < 0){
      continue;
    }
    if (ret == 0){
      continue;
    }
    if (1){
#endif //MacOS
//printf("WSDxsocket::_udp_read_thread wait done\n");
      if (_this->_stop_accept != False){
        XCloseDisplay(disp);
        _this->_stop_accept = False;
        return NULL;
      }
      _accept_send_type data;
      data.sock = _this;
      data.socket = _this->_socket;
      data.addr = 0;
      data.hd = hd;

      Atom atom = XInternAtom(disp,"SOCK-ACCEPT",False);
      Window win = XtWindow(WSGIxwinAppDev()->appWidget());
      XClientMessageEvent  cm;
      cm.type = ClientMessage;
      cm.display = disp;
      cm.window = win;
      cm.message_type = atom;
      cm.format = 32;

      pthread_sem_t* sem = new pthread_sem_t;
      _this->_sem = (void*)sem;
      pthread_sem_init_np(sem);
      pthread_sem_lock_np(sem);    
  
      memcpy(cm.data.l,&data,sizeof(_accept_send_type));
      XSendEvent(disp,win,False,NoEventMask,(_XEvent *)&cm);
//printf("send message... win=0x%x\n",win);
      Window  win2  = XCreateSimpleWindow(disp,DefaultRootWindow(disp),400,400,
                                 200,200,2, 0, 1);
      XEvent event;
      XSendEvent(disp,win2,False,NoEventMask,(_XEvent *)&cm);
      XNextEvent(disp, &event);
      XDestroyWindow(disp,win2);

//printf("WSDxsocket::_udp_read_thread before lock\n");
      pthread_sem_lock_np(sem);    
//printf("WSDxsocket::_udp_read_thread unlocked...\n");
      pthread_sem_unlock_np(sem);    
      pthread_sem_destroy_np(sem);    
      delete sem;
    }else{
      break;
    }
  }
  XCloseDisplay(disp);
  return 0;
}

long WSDxsocket::acceptEx(void(*hd)(WSDsocket*,void*,WSCulong)){
  if (_started_accept != False){
    return WS_ERR;
  }
  if (hd == NULL){
    return WS_ERR;
  }
  if (_socket == 0){
    initialize();
    if (_socket == 0){
      return WS_ERR;
    }
  }
  if (_udp != False){
    pthread_t thr;
    tmp_data* dt = new tmp_data;
    dt->obj = this;
    dt->hd = hd;

    int ret = pthread_create(&thr,NULL,_udp_read_thread_,dt);
    if (ret != 0){
      return WS_ERR;
    }
    _started_accept = True;
    pthread_detach(thr);
    return WS_NO_ERR;
  }
  if (_listened == False){
    if (listen() != WS_NO_ERR){
      return WS_ERR;
    }
  }
  pthread_t thr;
  tmp_data* dt = new tmp_data;
  dt->obj = this;
  dt->hd = hd;

  int ret = pthread_create(&thr,NULL,_accept_thread_,dt);
  if (ret != 0){
    return WS_ERR;
  }
  _started_accept = True;
  pthread_detach(thr);
  return WS_NO_ERR;
}


long WSDxsocket::close(void* sock){
  if (sock != 0){
    ::shutdown((int)sock,0);
    ::close((int)sock);
    seterr();
    return WS_NO_ERR;
  }else{
    destroy();
    return WS_ERR;
  }
}
long WSDxsocket::initialize(){
//printf("WSDxsocket::initialize .. _udp=%d\n",_udp);
  if (_socket == 0){
    if (_udp == False){
      _socket = socket(AF_INET,SOCK_STREAM,0);
//printf("WSDxsocket::initialize .. tcp fd=%d\n",_socket);
    }else{
      _socket = socket(AF_INET,SOCK_DGRAM,0);
//printf("WSDxsocket::initialize .. udp fd=%d\n",_socket);
      _udp_binded = False;
    }
  }
  if (_socket < 0){
    _socket = 0;
    seterr();
    return WS_ERR;
  }
  return WSDsocket::initialize();
}
long WSDxsocket::read(void* handle,WSCuchar* buf,long size){
  if (_udp != False){
    if (_udp_binded == False){
      struct sockaddr_in server;
      server.sin_family = AF_INET;
      if (!strcmp((char*)_naddr,"")){
        server.sin_addr.s_addr = INADDR_ANY;
//printf("WSDxsocket::read addr=%s %x\n",(char*)_naddr, server.sin_addr.s_addr);
      }else{
        server.sin_addr.s_addr = (unsigned long)inet_addr((char*)_naddr);
//printf("WSDxsocket::read addr2=%s %x\n",(char*)_naddr, server.sin_addr.s_addr);
      }  
      server.sin_port = htons( _port );
      if( bind(_socket, (struct sockaddr *)&server, sizeof server) < 0){
        seterr();
        return WS_ERR;
      }
      _udp_binded = True;
    }
  }
  WSCulong timeout = 0;
  if (_socket == (int)handle){
    timeout = _timeout *1000;
  }else{
    timeout = _cl_timeout *1000;
  }
  if (timeout > 0){
    int fl = fcntl((int)handle,F_GETFL,0);
    if (fl == -1){
      seterr();
      return 0;
    }
    int ret = fcntl((int)handle,F_SETFL,fl | FNDELAY);
    if (ret == -1){
      seterr();
      return 0;
    }
  }else{
    int fl = fcntl((int)handle,F_GETFL,0);
    if (fl == -1){
      seterr();
      return 0;
    }
    int ret = fcntl((int)handle,F_SETFL,fl & ~FNDELAY);
    if (ret == -1){
      seterr();
      return 0;
    }
  }

  long read_len = 0;
  long len_to_read = size; 
  long read_first = True;
  while(1){
    long ret = 0;
    if (_udp == False){
      ret = recv((int)handle,(char*)&buf[read_len],len_to_read,0);
//printf("WSDxsocket::read ret=%d len=%d eno=%d buf=0x%x handle=0x%x\n",ret,len_to_read,errno,buf,handle);
    }else{
      struct sockaddr_in fromaddr;
      fromaddr.sin_family = AF_INET;
      fromaddr.sin_port = 0;
      fromaddr.sin_addr.s_addr = 0;
      SOCKLEN_T len = sizeof(fromaddr);
      ret = recvfrom((int)handle,(char*)&buf[read_len],len_to_read,0,(sockaddr*)&fromaddr,&len);
      _set_dest_addr_(htonl(fromaddr.sin_addr.s_addr));
      _dest_port = htons(fromaddr.sin_port);
//printf("WSDxsocket::read udp ret=%d len=%d eno=%d buf=0x%x handle=0x%x\n",ret,len_to_read,errno,buf,handle);
    }
//printf("eno=%d\n",errno);
    if (timeout > 0){
#ifndef MacOS
      struct pollfd fds;
      fds.fd = (int)handle;
      fds.events = POLLIN;
      fds.revents = 0;
#else //MacOS
      struct timeval tm;
      tm.tv_sec = timeout;
      tm.tv_usec = 0;
      fd_set read_fds;
      FD_ZERO(&read_fds);
      FD_SET((int)handle,&read_fds);
#endif //MacOS
      if (ret > 0){
        read_len +=  ret;
        len_to_read -= ret;
        if (len_to_read == 0){
//printf("WSDxsocket::read... here7 read_len=%d\n",read_len);
          seterr();
          return read_len;
        }
      }else if (ret == 0){        
        if (read_first == False){
//printf("WSDxsocket::read... here6 read_len=%d\n",read_len);
          seterr();
          return read_len;
        }
      }else if (ret < 0){        
        if (errno != EAGAIN){
//printf("WSDxsocket::read... here5 read_len=%d\n",read_len);
          seterr();
          return read_len;
        }
      }

      read_first = False;
#ifndef MacOS
      ret = poll( &fds, 1, timeout );
      if (ret < 1){
        seterr(ETIMEDOUT);
//printf("WSDxsocket::read... timed out...\n");
        return read_len;
      }
      if (fds.revents & POLLIN){
        if (size == read_len){
//printf("WSDxsocket::read... here1\n");
          seterr(0);
          return read_len;
        }
        continue;
      }else{
//printf("WSDxsocket::read... here2\n");
        seterr();
        return read_len;
      }
#else //MacOS
      ret = select( FD_SETSIZE,&read_fds,NULL,NULL,&tm );
      long err = errno;
      if (ret < 0 && err != EALREADY){
        seterr();
        return read_len;
      }
      if (ret == 0){
        seterr(ETIMEDOUT);
        return read_len;
      }
      continue;
#endif //MacOS
    }
//printf("WSDxsocket::read... here3\n");
    seterr();
    return ret;
  }
//printf("WSDxsocket::read... here4\n");
  seterr();
  return read_len;
}

long WSDxsocket::read(WSCuchar* buf,long size){
  if (_socket == 0 && _udp != False){
    initialize();
    if (_socket == 0){
      return -1;
    }
  }
  if (_socket != 0){
    return read((void*)_socket,buf,size);
  }
  return -1;
}
long WSDxsocket::write(void* handle,WSCuchar* buf,long size){
//printf("WSDxsocket::write _udp=%d size=%d\n",_udp,size);
  if (_udp != False){
    int val;
    val = 1;
    int ret = setsockopt((int)handle, SOL_SOCKET, SO_BROADCAST,
                         (char *)&val, sizeof(val));
    if (ret < 0){
      seterr();
      return 0;
    }
//printf("WSDxsocket::write setsockopt... _socket=%d handle=%d ret=%d\n",_socket,handle,ret);
  }
  WSCulong timeout = 0;
  if (_socket == (int)handle){
    timeout = _timeout *1000;
  }else{
    timeout = _cl_timeout *1000;
  }

  if (timeout > 0){
    int fl = fcntl((int)handle,F_GETFL,0);
    if (fl == -1){
      seterr();
      return 0;
    }
    int ret = fcntl((int)handle,F_SETFL,fl | FNDELAY);
    if (ret == -1){
      seterr();
      return 0;
    }
  }else{
    int fl = fcntl((int)handle,F_GETFL,0);
    if (fl == -1){
      seterr();
      return 0;
    }
    int ret = fcntl((int)handle,F_SETFL,fl & ~FNDELAY);
    if (ret == -1){
      seterr();
      return 0;
    }
  }

  long write_len = 0;
  long len_to_write = size; 
  long write_first = True;
  while(1){
    long ret = 0;
    if (_udp == False){
      ret = send((int)handle,(char*)&buf[write_len],len_to_write,0);
    }else if (_udp_binded == False){
      struct sockaddr_in toaddr;
      toaddr.sin_family = AF_INET;
      toaddr.sin_port = htons( _dest_port );
//printf("WSDxsocket::write addr=%s port=%d\n",_dest_naddr.getString(),_dest_port);
      if (!strcmp(_dest_naddr.getString(),"")){
        toaddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
//printf("here..... addr=0x%x port=%d\n",toaddr.sin_addr.s_addr,toaddr.sin_port);
      }else{
        toaddr.sin_addr.s_addr =(unsigned long)inet_addr((char*)_dest_naddr);
//printf("here2..... addr=0x%x port=%d\n",toaddr.sin_addr.s_addr, toaddr.sin_port);
      }

      ret = sendto((int)handle,(char*)&buf[write_len],len_to_write,0,
                   (sockaddr*)&toaddr,sizeof(toaddr));
//printf("sendto.. size=%d\n",sizeof(toaddr));
//printf("sendto.. handle=%d ret=%d errno=%d\n",handle,ret,errno);
    }
    if (_timeout > 0){
#ifndef MacOS
      struct pollfd fds;
      fds.fd = (int)handle;
      fds.events = POLLOUT;
      fds.revents = 0;
#else //MacOS
      struct timeval tm;
      tm.tv_sec = _timeout;
      tm.tv_usec = 0;
      fd_set write_fds;
      FD_ZERO(&write_fds);
      FD_SET((int)handle,&write_fds);
#endif //MacOS
      if (ret > 0){
        write_len +=  ret;
        len_to_write -= ret;
        if (_udp != False && size == write_len){
          return write_len;
        }
      }else if (ret == 0){        
        if (write_first == False){
          seterr();
          return write_len;
        }
      }else if (ret < 0){        
        seterr();
        return write_len;
      }
      if (timeout < 1){
//        seterr();
        return write_len;
      }
      write_first = False;
#ifndef MacOS
      ret = poll( &fds, 1, timeout );
      if (ret < 1){
        seterr();
        return write_len;
      }
      if ((fds.revents & POLLOUT) == POLLOUT){
        if (size == write_len){
          seterr();
          return write_len;
        }
        continue;
      }else{
        seterr();
        return write_len;
      }
#else //MacOS
      ret = select(FD_SETSIZE,NULL,&write_fds,NULL,&tm);
      long err = errno;
      if (ret < 0 && err != EALREADY){
        seterr();
        return write_len;
      }
      if (write_len == size){
        seterr(0);
        return write_len;
      }
      continue;
#endif //MacOS
    }
    seterr();
    return write_len;
  }
  seterr();
  return write_len;
}
long WSDxsocket::write(WSCuchar* buf,long size){
  if (_socket == 0 && _udp != False){
    initialize();
    if (_socket == 0){
      return -1;
    }
  }
//printf("WSDxsocket::write.. _socket=0x%x\n",_socket);
  if (_socket != 0){
//printf("WSDxsocket::write.. call\n");
    return write((void*)_socket,buf,size);
  }
  return -1;
}

void WSDxsocket::seterr(int err){
  if (err < 0){
    if (errno > 0){
      _error_str = strerror(errno);
    }else{
      _error_str = "";
    }
  }else if (err ==0){
    _error_str = "";
  }else{
    _error_str = strerror(err);
  }
}

WSCstring WSDxsocket::getLastError(){
  return _error_str;
}

