// Berkeley Open Infrastructure for Network Computing
// http://boinc.berkeley.edu
// Copyright (C) 2005 University of California
//
// This is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation;
// either version 2.1 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 Lesser General Public License for more details.
//
// To view the GNU Lesser General Public License visit
// http://www.gnu.org/copyleft/lesser.html
// or write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#ifndef _NET_XFER_
#define _NET_XFER_

#ifndef _WIN32
#include <stdio.h>
#include <time.h>
#include <vector>
#endif
#include <curl/curl.h>


#include "network.h"

#ifndef _MAX_PATH
#define _MAX_PATH 256
#endif 

extern CURLM* g_curlMulti; // the global libcurl multi handle 

// The following classes implement polling (non-blocking) I/O
// between a disk file (or memory block) and a socket

#define MAX_BLOCKSIZE   16384

// global functions for starting & stopping libcurl
extern int curl_init();
extern int curl_cleanup();

// represents a network connection, either being accessed directly
// or being transferred to/from a file
//

/*
net_xfer object status codes (using libcurl):

nxf->response          maps to HTTP_STATUS_* (from http_curl.h)
nxf->http_op_state     maps to HTTP_STATE_*  (from http_curl.h)
nxf->CurlError is a curl specific code  (maps to the CURLE_* enums in curl/curl.h)
nxf->strCurlError is a human-readable msg (i.e. "couldn't connect to server")
*/

class NET_XFER {
public:
    FILE* fileIn;
	FILE* fileOut;  // CMC need an output file for POST responses
	CURL* curlEasy; // the "easy curl" handle for this net_xfer request
	struct curl_slist *pcurlList; // curl slist for http headers
	struct curl_httppost *pcurlFormStart; // a pointer to a form item for POST
	struct curl_httppost *pcurlFormEnd; // a pointer to a form item for POST
	unsigned char* pByte;  // pointer to bytes for reading via libcurl_read function
	long lSeek;  // this is a pointer within the file we're reading
    char infile[_MAX_PATH];
    char outfile[_MAX_PATH];
	bool bTempOutfile; // CMC -- flag that outfile is really a tempfile we should delete
    char* req1;
	bool bSentHeader;  // CMC -- a flag that I already sent the header
	CURLcode CurlResult;   // CMC -- send up curl result code
	char strCurlResult[_MAX_PATH];  // CMC -- 'human-readable' string with result info

    // int socket;  // CMC -- deprecated, net_xfer's via curlEasy handle above
    char hostname[256];     // The host we're connecting to (possibly a proxy)
    bool is_connected;
    bool want_download;     // at most one should be true
    bool want_upload;
    bool do_file_io;
        // If true: poll() should transfer data to/from file
        // (in which case "file" and blocksize are relevant)
        // If false: set io_ready (higher layers will do I/O)
    bool io_done;
        // set to true when the current transfer is over:
        // - the transfer timed out (not activity for a long time)
        // - network connect failed
        // - got EOF on socket read (0 bytes, select indicated I/O ready)
        // - error on disk write (e.g. volume full)
        // - reached end of disk file on upload
        // - got file read error on upload
        // - write to socket failed on upload
    bool io_ready;
        // Signals higher layers that they can read or write socket now
        // (used if !do_file_io)
    long error;
	long response;
    int port;
    int blocksize;
    double start_time;
    double xfer_speed;
    double bytes_xferred;   // bytes transferred in this session
	double content_length;
    char file_read_buf[MAX_BLOCKSIZE];
    int file_read_buf_offset, file_read_buf_len;
    int seconds_until_timeout;

	// CMC - moved from http_op
	int http_op_state;     // values below
    int http_op_type;
    int http_op_retval;

    NET_XFER();
    ~NET_XFER();
    void reset();
    void init(char* host, int port, int blocksize);
    int get_ip_addr(int &ip_addr);
    //int open_server();
    void close_socket();
    void close_file();
    // int do_xfer(int&);  // CMC not needed for libcurl
    void update_speed();
    void got_error();
    char* get_hostname();
    bool check_timeout(bool);
    void reset_timeout();
};

// bandwidth limitation is implemented at this level, as follows:
// There are limits max_bytes_sec_up and max_bytes_sec_down.
// We keep track of the last time and bytes_left_up and bytes_left_down;
// Each second we reset these to zero.

class NET_XFER_SET {
    std::vector<NET_XFER*> net_xfers;
public:
    NET_XFER_SET();
    double max_bytes_sec_up, max_bytes_sec_down;
        // user-specified limits on throughput
    double bytes_left_up, bytes_left_down;
        // bytes left to transfer in the current second
    double bytes_up, bytes_down;
        // total bytes transferred
    bool up_active, down_active;
        // has there been transfer activity since last call to check_active()?
    int last_time;
    int insert(NET_XFER*);
    int remove(NET_XFER*);

    void get_fdset(FDSET_GROUP&);
    void got_select(FDSET_GROUP&, double);
#if 0
    bool poll();
    int net_sleep(double);
    int do_select(double& bytes_transferred, double timeout);
#endif
    NET_XFER* lookup_curl(CURL* pcurl);   // lookup by easycurl handle
    void check_active(bool&, bool&);
};

#endif  // _H
