  /*
 *  Copyright (C) 1997, 1998 Olivetti & Oracle Research Laboratory
 *
 *  Copyright (C) 1998 International Computer Science Institute (ICSI)
 *
 *  This 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 software 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 software; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 *  USA.
 */

/*
 * sockets.c - functions to deal with sockets.
 */

#include <pspkernel.h>
#include <string.h>
#include "vnc.h"
#include "../libnet/pspnet.h"
#include "../dialog.h"
#include "../vncdbg.h"

typedef int Err;
Err h_errno;

static unsigned long recvBytes = 0;
static unsigned long sentBytes = 0;
//static unsigned long start;
//static unsigned long sentInterval, recvInterval;

Bool errorMessageFromReadExact = True;

/******************************************************************
 * PrintSentRecvStat
 *
 * prints information about send/receive spped on the console
 *****************************************************************/
void PrintSentRecvStat ()
{
  if (sentBytes || recvBytes) {
    LogPs0 ("Network performance statistic:\n");
    LogPn2 ("\tsent: ", sentBytes, "bytes\n\trecv: ", recvBytes/1000, " Kbytes\n");
  }
  else
    return;

//  if (sentInterval != 0)
//    LogPn1 ("\tsend speed ",
//	sentBytes * sysTicksPerSecond / sentInterval, " bytes/sec\n");

//  if (recvInterval != 0)
//    LpgPn1 ("\treceive speed ",
//	recvBytes * sysTicksPerSecond / recvInterval, " bytes/sec\n");

  recvBytes = sentBytes = 0;
//  start = sentInterval = recvInterval = 0;
}


/******************************************************************
 * ReadExact
 * 
 * Read an exact number of bytes, and don't return until you've got them.
 *****************************************************************/

Bool ReadExact (int sock, unsigned char *buf, int n)
{
  	int i = 0;
  	int j;

//  	start = TimGetTicks ();

  	while (i < n)
  	{
    	j = sceNetInetRecv (sock, buf + i, (n - i), 0);
    	if (j <= 0)
    	{
      		if (j < 0)
      		{
      		        int errno = sceNetInetGetErrno();
//      			if ((errno == netErrTimeout) && ((TimGetTicks() - start) < ReadNetTimeout))
//      				continue;
				LogPn1 ("read failed (Err=", errno, ")\n");
      		}
      		else
      		{
				if (errorMessageFromReadExact)
				{
	  				LogPs0 ("read nothing\n");
				}
      		}
      		return False;
    	}
    	i += j;
  	}

//  	recvInterval += TimGetTicks () - start;
  	recvBytes += n;

  	return True;
}


/******************************************************************
 * WriteExact
 * 
 * Write an exact number of bytes, and don't return until you've sent them.
 *****************************************************************/

Bool WriteExact (int sock, unsigned char *buf, int n)
{
  int i = 0;
  int j;

//  start = TimGetTicks ();

  while (i < n) {
    j = sceNetInetSend(sock,buf + i,(n - i),0);
    if (j <= 0) {
      if (j < 0) {
	LogPs0 ("write failed\n");
      } else {
        LogPs0 ("write nothing\n");
      }
      return False;
    }
    i += j;
  }

//  sentInterval += TimGetTicks () - start;
  sentBytes += n;

  return True;
}

extern u8 ConnectTimeout;
int ConnectWithTimeout (SOCKET sock, struct sockaddr_in *addr, int size) {
  int err, one = 1, zero = 0, ct = (ConnectTimeout+1)*20;
  sceNetInetSetsockopt(sock, SOL_SOCKET, SO_NOBLOCK, (char *)&one, sizeof(one));

  err = sceNetInetConnect(sock, &addr, sizeof(addr));
  if (err == 0)
    return 0;

  if (err == 0xFFFFFFFF && sceNetInetGetErrno() == 0x77) {
    int ticks;
    for (ticks = 0; ticks < ct; ticks++) {
      err = sceNetInetConnect(sock, &addr, sizeof(addr));
      if (err == 0 || (err == 0xFFFFFFFF && sceNetInetGetErrno() == 0x7F)) {
        sceNetInetSetsockopt(sock, SOL_SOCKET, SO_NOBLOCK, (char *)&zero, sizeof(zero));
        return 0;
      }
      pgWaitVn(15);
      PostProgressMessage("Connecting", ticks, ct);
    }
  }
  
  sceNetInetClose(sock);

  return err;
}

/******************************************************************
 * ConnectToTcpAddr
 * 
 * Connects to the given TCP port.
 *****************************************************************/
int ConnectToTcpAddr (unsigned long host, int port)
{
    int sock;
    struct sockaddr_in addr;
    int one = 1;
    int err;

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    *(u32*)addr.sin_addr = htonl(host);

    sock = sceNetInetSocket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
	MsgPn1 (0, "\n\tConnectToTcpAddr:\n\n   cannot open socket\n\n\nError: ", sceNetInetGetErrno(), "\n");
	LogPn1 ("ConnectToTcpAddr: cannot open socket\nError: ", sceNetInetGetErrno(), "\n");
	return -1;
    }
    LogPn1 ("ConnectToTcpAddr: socket\nnr: ", sock, "\n");
//    MsgPrintf(2,"Got Socket %i.", sock);

//    err = ConnectWithTimeout(sock, &addr, sizeof(addr));
    err = sceNetInetConnect(sock, &addr, sizeof(addr));
    if (err) {
        if(err == 111) MsgPrintf (0, "\n\tConnection Refused by Server");
        else MsgPn2 (0, "\n\tConnectToTcpAddr:\n\n   cannot connect\n\n\nError: ", err, " / ", sceNetInetGetErrno(), "\n");
	LogPn2 ("ConnectToTcpAddr: cannot connect\nError: ", err, " / ", sceNetInetGetErrno(), "\n");
	sceNetInetClose(sock);
	return -1;
    }
    LogPn1 ("ConnectToTcpAddr: connect ok\n", err, "\n");
//    MsgPrintf(2,"Connected.");

    err = sceNetInetSetsockopt(sock, IPPROTO_TCP, TCP_NODELAY,(char *)&one, sizeof(one));
    if (err < 0) {
	MsgPn2 (0, "\n\tConnectToTcpAddr:\n\n   cannot set socket options\n\n\nError: ", err, " / ", sceNetInetGetErrno(), "\n");
	LogPn2 ("ConnectToTcpAddr: cannot set socket options\nError: ", err, " / ", sceNetInetGetErrno(), "\n");
	sceNetInetClose(sock);
	return -1;
    }
    LogPn1 ("ConnectToTcpAddr: setsockopt ok\n", err, "\n");
//    MsgPrintf(2,"Socket Options Set.");

    return sock;
}


#if 0
/******************************************************************
 * StringToIPAddr
 * 
 * Converts a host string to an IP address.
*****************************************************************/
int StringToIPAddr (char *str, unsigned long *addr)
{
    struct hostent *hp = NULL;
    NetHostInfoBufType AppHostInfo;

    if ((hp = ((struct hostent*)NetLibGetHostByName(str, &AppHostInfo, ConnectNetTimeout, &errno))) == NULL) {
      if ((long)(*addr = NetLibAddrAToIN(str)) == -1)
        return 0;
    }
    else
      memcpy ((char *)(hp->h_addr), (char *)addr, hp->h_length);

    return 1;
}
#endif
