/* This file is part of cardwords
   (c) 1999 Tobias Peters
   
   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

*/

// cardwords_tcpclient.cc

// The purpose of class CardWords_TCPClient is:
// - establishing a TCP connection to the cardwords_server
// - act as a base class for CardWords_BaseClient, which also handles
//   messages and the protocol overhead

#include "cardwords_tcpclient.hh"
#include "cardwords_stringconverters.hh"

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <netdb.h>
#include <stdlib.h>
#include <iostream.h>
#include <stdio.h>
#include <unistd.h>
#include <fstream.h>

CardWords_TCPClient::CardWords_TCPClient()
{
  /* clear the socket address prior to use */
  memset(&sn, 0, sizeof(sn));

  port      =  0;
  connected =  false;
  socket_fd = -1;
}

CardWords_TCPClient::~CardWords_TCPClient()
{}

// returns true if a TCP connection exists
bool
CardWords_TCPClient::is_connected(void) const
{
  return connected;
}

// Call this only while not connected. While connected,
// this call will fail and return -1.
// If the server_name can not be resolved, returns -2.
// returns 0 on success.
int
CardWords_TCPClient::set_remote_hostname(const string & hostname)
{
  if (is_connected() == true) {
    return -1;
  }

  host = gethostbyname(hostname.c_str());
  if (host == 0) {
    herror(hostname.c_str());
    return -2;
  }
  
  sn.sin_family = host->h_addrtype;
  if (host->h_length > (int)sizeof(sn.sin_addr)) {
    host->h_length = sizeof(sn.sin_addr);
  }

  memcpy((caddr_t)&sn.sin_addr, host->h_addr, host->h_length);
  return 0;
}

// Returns 0 if no name is set
string
CardWords_TCPClient::get_remote_hostname(void) const
{
  return host->h_name;
}

// Call this only while not connected. While connected to a cardwords_server,
// this call will fail and return -1.
// returns 0 on success.
int
CardWords_TCPClient::set_remote_port_number(const string & portp)
{
  if (is_connected() == true) {
    return -1;
  }
  port = string2unsigned(portp);
  sn.sin_port = htons(port);
  return 0;
}

int 
CardWords_TCPClient::set_remote_port_number(unsigned short port)
{
  if (is_connected() == true) {
    return -1;
  }
  this->port = port;
  sn.sin_port = htons(port);
  return 0;
}

unsigned short
CardWords_TCPClient::get_remote_port_number(void) const
{
  return port;
}

int
CardWords_TCPClient::get_fd(void) const
{
  return socket_fd;
}

// Disconnect closes a connection. If not connected, it does nothing.
void
CardWords_TCPClient::disconnect(void)
{
  if (is_connected() == true) {
    close(get_fd());
  }
  connected = false;
}

// connect is virtual because a child class might also do some logging
// after the TCP connection is established.
// Returns 0 on success.
// Returns -1 if the tcp connection failed.
int
CardWords_TCPClient::connect(void)
{
  if (is_connected() == true) {
    cerr << "CardWords_TCPClient::connect(): already connected." << endl;
    return -1;
  }
  cout << "Trying " << inet_ntoa(sn.sin_addr) << "..." << endl;
  
  socket_fd = socket (AF_INET, SOCK_STREAM, 0);
  
  if (::connect(socket_fd, (struct sockaddr *)&sn, sizeof(sn)) < 0) {
    perror("Unable to connect to remote host");
    return -1;
  }
  connected = true;
  return 0;
}
