#ifdef WIN32
#define STL_USING_ALL
#include "STL.h"
#endif

#include <errno.h>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

#include "CException.h"
#include "Network.h"
#include "CNetMessage.h"
#include "CNetStream.h"


void CNetStream::Write(char * buffer, int size)
{
  int sent = 0;
  int justsent;

  while (sent < size)
    {
      justsent = NetWrite(sock, buffer+sent, size-sent);
      if (justsent <= 0)
        {
          throw CNetworkException(std::string("Unable to send correctly: ") +
                                  std::string(strerror(errno)));
        }

      sent += justsent;
    }
}

void CNetStream::Read(char * buffer, int size)
{
  int read = 0;
  int justread;

  while (read < size)
    {
      justread = NetRead(sock, buffer+read, size-read);
      if (justread < 0)
        {
          throw CNetworkException(std::string("Unable to receive correctly: ")+
                                  std::string(strerror(errno)));
        }
      if (justread == 0)
        {
          throw CNetworkException(std::string("Connection lost."));
        }

      read += justread;
    }
}



bool CServerStream::Connect()
{
  sockaddr_in address;
#ifdef WIN32
  int addresslenght = sizeof(sockaddr_in);
#elif defined(LINUX)
  unsigned int addresslenght = sizeof(sockaddr_in);
#else
#error OS not supported
#endif
  memset(&address, 0, sizeof(sockaddr_in));
  address.sin_port = htons(port);
  address.sin_family = AF_INET;

  listensock = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);

  if (listensock < 0)
    {
      throw CNetworkException(std::string("Unable to create a socket: ") +
                              std::string(strerror(errno)));
    }

  if (bind(listensock, (sockaddr *) &address, addresslenght) < 0)
    {
      std::ostringstream o;
      o << "Unable to bind to port " << port << ": " << strerror(errno);
      throw CNetworkException(o.str());
    }

  if (listen(listensock, 1) < 0)
    {
      throw CNetworkException(std::string("Unable to listen: ") +
                              std::string(strerror(errno)));
    }

  sock = accept(listensock, (sockaddr *) &address, &addresslenght);

  if (sock < 0)
    {
      throw CNetworkException(std::string("Unable to accept connection: ") +
                              std::string(strerror(errno)));
    }

  dottedIPother = inet_ntoa(address.sin_addr);

  return true;
}


bool CClientStream::Connect()
{
  hostent* host = gethostbyname(dottedIPother.c_str());

  if (host == 0)
    {
      throw CNetworkException(std::string("Unable to resolve ") +
                              dottedIPother + ".");
    }

  if (host->h_addrtype != AF_INET)
    {
      throw CNetworkException(dottedIPother + 
                              std::string("doesn't seem to be "
                                          "an IPv4 address."));
    }

  dottedIPother = inet_ntoa(*(in_addr *)*host->h_addr_list);

  sock = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);

  if (sock < 0)
    {
      throw CNetworkException(std::string("Unable to create a socket: ") +
                              std::string(strerror(errno)));
    }


  sockaddr_in address;
  memset(&address, 0, sizeof(sockaddr_in));
  address.sin_port = htons(port);
  address.sin_family = AF_INET;

  memcpy(&address.sin_addr, host->h_addr_list[0], host->h_length);

  if (connect(sock, (sockaddr *) &address, sizeof(sockaddr_in)))
    {
      throw CNetworkException(std::string("Unable to connect :") +
                              std::string(strerror(errno)));
    }

  return true;
}



CNetStream& CNetStream::operator << (const CNetMessage& message)
{
  Write((char *) &message.GetTypeLong(), sizeof(long));
  Write((char *) &message.GetSizeLong(), sizeof(long));
  Write((char *) message.GetData(), message.GetSize());

  return *this;
}


CNetStream& CNetStream::operator >> (CNetMessage& message)
{
  Read((char *) &message.GetTypeLong(), sizeof(long));

  Read((char *) &message.GetSizeLong(), sizeof(long));

  void * newdata = new unsigned char[message.GetSize()];
  if (newdata == 0)
    {
      std::cerr << MEMORY_ERROR.GetErrorString();
      exit(1);
    }

  Read((char *) newdata, message.GetSize());

  message.SetData(newdata);

  return *this;
}
