/* Autor: Leandro Bernsmuller
Controla comunicacao com sockets.

A comunicacao eh baseada em mensagens tipo texto trocadas
com um servidor.
Funciona de forma independente ao DirectX (nao usa DirectPlay).

Baseado no exemplo WSock do Windows SDK.
Usa as primitivas de sockets do UNIX. */



#include <windows.h>
#include <winsock.h>
#include "..\princip\defs.h"
#include "..\interf\dlgsopor.h"
#include "..\interf\dlgsosrv.h"



// TRUE - deve haver comunicacao com sockets via Windows
// FALSE - nao deve
BOOL bUSANDO_SOCKETS = FALSE;

// deferencia para o socket usado na comunicacao
SOCKET stSOCKET;



/* Tenta fazer uma conexao com um servidor via sockets.
Pede os dados do servidor ao usuario por dialogos.
Se obtiver sucesso, altera a variavel bUSANDO_SOCKETS para TRUE,
senao para FALSE.
**********************************************************/
void TentaConectarSockets ( HINSTANCE hInstancia )
{
   // dbug
   //bUSANDO_SOCKETS = TRUE;
   //return;

   bUSANDO_SOCKETS = FALSE;

   // inicia sockets

   // inicia com a versao desejada - soh deve rodar uma vez
   // (codigo original de msdev/mfc/src/sockcore.cpp, AfxSocketInit)
   WSADATA
      stData;
   WORD
      wVersao = MAKEWORD ( 1, 1 );
   if ( WSAStartup ( wVersao, &stData ) != 0 )
      return;
   if ( LOBYTE ( stData.wVersion ) != 1 ||
        HIBYTE ( stData.wVersion) != 1 ) {
      // termina: erro
      WSACleanup();
      return;
   }
      
   // cria o socket
   stSOCKET = socket ( AF_INET, SOCK_STREAM, 0 );
   // se houve erro   
   if ( stSOCKET == INVALID_SOCKET )
      return;

   // endereco do servidor no formato para internet

   // string para guardar nome do servidor
   char
      szNomeServidor [ 300 ];

   // tenta obter endereco do servidor, se falhar sai
   if ( ! FazDialogo_EnderecoServidorSock ( hInstancia, szNomeServidor ) )
      return;
   
   SOCKADDR_IN
      stDestSIn;        // DESTination Socket INternet 
      
   stDestSIn.sin_family = AF_INET;
   
   // endereco   
   PHOSTENT
      pstServ = gethostbyname ( szNomeServidor );
   if ( pstServ == NULL ) {
      closesocket ( stSOCKET );
      return;
   }
   memcpy( (char FAR*) &( stDestSIn.sin_addr ), pstServ->h_addr,
           pstServ->h_length );
           
   // porta

   // string para guardar porta do servidor
   char
      szPortaServidor [ 300 ];

   // tenta obter porta do servidor, se falhar sai
   if ( ! FazDialogo_PortaServidorSock ( hInstancia, szPortaServidor ) )
      return;

   // define porta
   stDestSIn.sin_port = htons ( (unsigned short) atoi ( szPortaServidor ) );
   
   // tenta conectar
   
   if ( connect ( stSOCKET, (PSOCKADDR) &stDestSIn, sizeof ( stDestSIn ) ) < 0 ) {
      closesocket ( stSOCKET );
      return;
   }
   
   bUSANDO_SOCKETS = TRUE;
}



/* Termina a conexao. Deve ser usado apenas apohs a conexao
ter sido estabelecida (nao durante a tentativa de conectar).
Altera bUSANDO_SOCKETS para FALSE.
**********************************************************/
void DesconectaSockets ()
{
   // dbug
   //return;

   if ( ! bUSANDO_SOCKETS )
      return;
      
   closesocket ( stSOCKET );
   WSACleanup();
   bUSANDO_SOCKETS = FALSE;
}



/* Envia uma mensagem, tipo string, pelo socket
pszMsg - mensagem que deve ser enviada. Deve ser um string
         terminado com zero.
**********************************************************/
void EnviaMensagemSockets ( char* pszMsg )
{
   // dbug
   //return;

   // verifica se nao existe conexao
   if ( ! bUSANDO_SOCKETS )
      // aborta envio da mensagem      
      return;
   
   // tenta enviar dados, se nao conseguir termina conexao      
   if ( send ( stSOCKET, pszMsg, strlen ( pszMsg ), 0 ) == SOCKET_ERROR )
      DesconectaSockets ();
}



/* Recebe uma mensagem pelo canal.

Verifica se existe uma mensagem para receber. Se existir,
le a mensagem, caso contrario termina execucao da funcao, sem
esperar (bloqueado) que uma mensagem chegue.

pchMem - aponta onde escrever os dados recebidos
dwTamMem - tamanho da memoria onde os dados podem ser escritos
retorna:
nCod - codigo de retorno: SOCKET_ERROR - se houve erro ou fim de conexao
                          ou
                          numero de bytes recebidos
return TRUE - recebeu dados / FALSE - nao recebeu
                              (nao havia oque receber, ou erro)
**********************************************************/
BOOL RecebeMensagemSockets ( char* pchMem, DWORD dwTamMem, int& nCod )
{
   // dbug
   //return TRUE;

   // tempo de espera
   struct timeval
      stWait;
   stWait.tv_sec = 0;
   stWait.tv_usec = 0;

   // conjunto de descritores para testar
   fd_set
      stTest;
   FD_ZERO ( &stTest );
   FD_SET ( stSOCKET, &stTest );

   // verifica se existe alguma mensagem para receber
   int
      nSelectCod = select ( stSOCKET + 1,
                            &stTest,
                            (fd_set*) 0,
                            (fd_set*) 0,
                            &stWait );
      
   // verifica se codigo de retorno indica erro
   if ( nSelectCod == SOCKET_ERROR ) {
      DesconectaSockets ();
      return FALSE;
   }
   else
      // verifica se codigo de retorno eh zero, nada para receber
      if ( nSelectCod == 0 )
         return FALSE;

   // recebe dados
   nCod = recv ( stSOCKET, pchMem, dwTamMem, 0 );

   // se recebeu informacao de conecxao terminada ou erro
   if ( nCod == SOCKET_ERROR ) {
      // termina conexao
      DesconectaSockets ();
      return FALSE;
   }

   return TRUE;
}
