//---------------------------------------------------------------------------

#ifndef Unit2H
#define Unit2H
//---------------------------------------------------------------------------
#include <vcl.h>
#include <winsock2.h>
#include <cassert>
#include <map>
#include <list>
#include "Datastruct.h"
using namespace std;

#pragma comment(lib,"ws2_32.lib")

namespace Async
{
  #ifndef __BORLANDC__
  typedef void  (*receive_handler)(Data *pd);
  typedef void  (*error_handler)(int errcode);
  typedef void  (*close_handler)(SOCKET s);
  typedef void  (*connect_handler)(SOCKET s);
  #else
  typedef void __fastcall (__closure* receive_handler)(Data *pd);
  typedef void __fastcall (__closure *error_handler)(int errcode);
  typedef void __fastcall (__closure *close_handler)(SOCKET s);
  typedef void __fastcall (__closure *connect_handler)(SOCKET s);
  #endif
  
  class AsyncSocket
  {
     static const int  WM_SOCKET=WM_USER+109;
     static map<SOCKET,list<Data*> > sMapOfComm;
     static WNDPROC sOldProc;
     static bool bWrite;//可写
     
     static receive_handler sReceviceProc;
     static error_handler sErrorProc;
     static close_handler sCloseProc;
     static connect_handler sConnectProc;

     static int Send(SOCKET s)
     {
        map<SOCKET,list<Data*> >::iterator it=sMapOfComm.find(s);
        if(it!=sMapOfComm.end())
        {
           for(list<Data*>::iterator itlist=it->second.begin();itlist!=it->second.end();
              )
           {
              auto_str str;
              (*itlist)->getstr(str);
              int ret=::send(s,str.cstr(),str.length(),0);
              if(ret==SOCKET_ERROR)//
              {
                 if((ret=WSAGetLastError())!=WSAEWOULDBLOCK)
                 return 0;
                 else
                 {
                   bWrite=false;
                   return WSAEWOULDBLOCK;//缓冲区满了或对方接收缓冲区满了
                 }
              }
              else
              {
                delete (*itlist);
                itlist=it->second.erase(itlist);
              }
           }
           return 1;
        }
        return 0;
     }
     static LRESULT CALLBACK NewWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
     {
        if(msg==WM_SOCKET)
        {
           SOCKET s=(SOCKET)wParam;
           if(WSAGETSELECTERROR(lParam))
           {
             sErrorProc(WSAGetLastError());
             ::closesocket(s);
             return 0;
           }
           switch(WSAGETSELECTEVENT(lParam))
           {
             case FD_ACCEPT:
             {
                 SOCKET sClient=::accept(s,NULL,NULL);
                 ::WSAAsyncSelect(sClient,hwnd,WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE);
             }
             break;
             case FD_CONNECT:
             {
                sConnectProc(s);
             }
             break;
             case FD_READ:
             {
                 DataHead dh;
                 dh.s=s;
                 int len=::recv(s,(char*)&dh,sizeof(DataHead),0);
                 if(len==sizeof(DataHead))
                 {
                     if(dh.datalen>0)
                     {
                         auto_str astr(dh.datalen);
                         len=::recv(s,astr.str(),astr.length(),0);
                         if(len==dh.datalen)
                         {
                            Data data(dh.cmd,astr.str(),astr.length(),s);
                            sReceviceProc(&data);
                         }
                     }
                     else if(!dh.datalen)
                     {
                         Data data(dh.cmd,NULL,0,s);
                         sReceviceProc(&data);
                     }
                 }
             }
             break;
             case FD_WRITE:
             {
                 bWrite=True;
                 //Send(s);
             }
             break;
             case FD_CLOSE:
             {
                sCloseProc(s);
                ::closesocket(s);
             }
             break;
           }
           return 1;
        }
        else
        {
          return  ::CallWindowProc(sOldProc,hwnd,msg,wParam,lParam);
        }
     }
     HWND s_hwnd;
     SOCKET s_Sock;
     bool bMode;
  public:
     AsyncSocket(HWND hwnd):s_hwnd(hwnd),s_Sock(INVALID_SOCKET),bMode(true)
     {
       sOldProc=(WNDPROC)SetWindowLong(s_hwnd,GWL_WNDPROC,(LONG)NewWndProc);
       assert(sOldProc);
     }
     ~AsyncSocket()
     {
        Close();
     }

     void reg_receive_handler(receive_handler handler)
     {
       sReceviceProc=handler;
     }
     void reg_error_handler(error_handler handler)
     {
       sErrorProc=handler;
     }
     void reg_close_handler(close_handler handler)
     {
       sCloseProc=handler;
     }
     void reg_connect_handler(connect_handler handler)
     {
       sConnectProc=handler;
     }
     void Close()
     {
       if(s_Sock!=INVALID_SOCKET)
       {
         ::closesocket(s_Sock);
         s_Sock=INVALID_SOCKET;
       }
       for(map<SOCKET,list<Data*> >::iterator it=sMapOfComm.begin();
           it!=sMapOfComm.end();++it
          )
       {
          for(list<Data*>::iterator itlist=it->second.begin();
              itlist!=it->second.end();
              ++itlist
             )
          {
             delete (*itlist);
          }
          it->second.clear();
       }
       sMapOfComm.clear();
     }
     bool Listen(int Port)
     {
       Close();
       bMode=true;

       s_Sock=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
       if(s_Sock==INVALID_SOCKET)return false;

       sockaddr_in addr;
       addr.sin_family=AF_INET;
       addr.sin_addr.S_un.S_addr=INADDR_ANY;
       addr.sin_port=::htons(Port);

       if(::bind(s_Sock,(PSOCKADDR)&addr,sizeof(addr))==SOCKET_ERROR)
       return false;

       ::WSAAsyncSelect(s_Sock,s_hwnd,WM_SOCKET,FD_ACCEPT|FD_READ|FD_WRITE|FD_CLOSE);

       ::listen(s_Sock,5);
       return true;
     }
     bool Connect(const char* lpszIp, int Port)
     {
         Close();
         bMode=false;

         s_Sock=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
         if(s_Sock==INVALID_SOCKET)return false;

         sockaddr_in addr;
         addr.sin_family=AF_INET;
         addr.sin_addr.S_un.S_addr=::inet_addr(lpszIp);
         addr.sin_port=::htons(Port);

         if(addr.sin_addr.S_un.S_addr==0xffffffff)
         return false;

         ::WSAAsyncSelect(s_Sock,s_hwnd,WM_SOCKET,FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE);
         int ret=::connect(s_Sock,(PSOCKADDR)&addr,sizeof(addr));
         if(ret!=SOCKET_ERROR)
         return true;
         else
         {
            return ::WSAGetLastError()==WSAEWOULDBLOCK;
         }
     }
     int PostSend(const char *cmd,const char *data,int datalen,SOCKET s=INVALID_SOCKET)
     {
       DataHead dh={0};
       memcpy(dh.cmd,cmd,2);
       dh.datalen=datalen;

       if(!bMode)  //客户端模式
       dh.s=s_Sock;
       else
       dh.s=s;

       Data *pd(new Data(dh,data));
       assert(pd);
       map<SOCKET,list<Data*> >::iterator it=sMapOfComm.find(pd->getdatahead().s);
       if(it==sMapOfComm.end())
       sMapOfComm[pd->getdatahead().s].push_back(pd);
       else
       {
         it->second.push_back(pd);
       }
       return Send(pd->getdatahead().s);
     }
     bool GetWriteStatus()const
     {
         return bWrite;
     }
  };
  map<SOCKET,list<Data*> >AsyncSocket::sMapOfComm;
  WNDPROC AsyncSocket::sOldProc;
  bool AsyncSocket::bWrite;
  
  receive_handler AsyncSocket::sReceviceProc;
  error_handler  AsyncSocket::sErrorProc;
  close_handler AsyncSocket::sCloseProc;
  connect_handler AsyncSocket::sConnectProc;

  class InitSocket
  {
  public:
    InitSocket()
    {
      WSAData wsaData={0};
      assert(::WSAStartup(MAKEWORD(2,2),&wsaData)==0);
    }
    ~InitSocket()
    {
       ::WSACleanup();
    }
  };
  static InitSocket init;
}
#endif
//---------------------------------------------------------------------------

#ifndef DatastructH
#define DatastructH
#include <windows.h>
#include <cassert.h>
//---------------------------------------------------------------------------
  class auto_str
  {
     char* s_buf;
     int s_buflen;

     friend class Data;

     void* operator new(size_t);
     void* operator new[](size_t);

     void set(const char *buf,const int len)
     {
       s_buf=const_cast<char*>(buf);
       s_buflen=len;
     }
   public:
     auto_str():s_buf(NULL),s_buflen(0)
     {}
     auto_str(size_t size):s_buf(NULL),s_buflen(size)
     {
       if(size>0)
       {
         s_buf=new char[size];
         assert(s_buf);
         memset(s_buf,0,size);
       }
     }
     ~auto_str()
     {
       if(s_buf)
       {
         delete [] s_buf;
       }
     }
     inline char* str()const
     {
       return s_buf;
     }
     inline const char* cstr()const
     {
       return s_buf;
     }
     inline const int length()const
     {
       return s_buflen;
     }
  };
  struct DataHead
  {
    char cmd[3];
    int datalen;
    SOCKET s;
  };
  class Data
  {
       //struct data
       DataHead s_dh;
       char *s_data;

       Data& operator=(const Data&);
       Data(const Data&);
    public:
       Data(const DataHead &dh,const char *data=NULL):s_data(NULL)
       {
         memcpy(&s_dh,&dh,sizeof(DataHead));
         if(data)
         {
           if(dh.datalen>0)
           {
              s_data=new char[dh.datalen];
              assert(s_data);
              memcpy(s_data,data,dh.datalen);
           }
         }
       }
       Data(const char* cmd,const char* data,int datalen,SOCKET s=INVALID_SOCKET):s_data(NULL)
       {
         memset(&s_dh,0,sizeof(s_dh));
         s_dh.s=s;
         memcpy(s_dh.cmd,cmd,2);
         s_dh.datalen=datalen;
         if(datalen>0)
         {
           s_data=new char[datalen];
           assert(s_data);
           memcpy(s_data,data,datalen);
         }
       }
       ~Data()
       {
         if(s_data)
         {
           delete [] s_data;
         }
       }
       inline const char* getdata()const
       {
          return s_data;
       }
       inline const int getdatalen()const
       {
          return s_dh.datalen;
       }
       inline const DataHead& getdatahead()const
       {
          return s_dh;
       }
       void getstr(auto_str& str)const
       {
          char *pBuf=new char[s_dh.datalen+sizeof(DataHead)];
          assert(pBuf);
          memcpy(pBuf,&s_dh,sizeof(DataHead));
          memcpy(pBuf+sizeof(DataHead),s_data,s_dh.datalen);
          str.set(pBuf,s_dh.datalen+sizeof(DataHead));
       }
  };
  
#endif