/*
  EHTERSHIELD_H library for Arduino etherShield
  Copyright (c) 2008 Xing Yu.  All right reserved.

  This library 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 library 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.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  
  -----------------------  History  ---------------------------------------------
  
  February 2008:
  Original Arduino style etherShield wrapper class by Xing Yu wrapped the 2.x 
  version of the Tuxgraphics TCP/IP stack from Guido Socher (using ENC28J60 driver 
  code from Pascal Stang). The enc28j60.c file was slightly Arduino-ized.
  
  June 2009:
  Andy modified the wrapper class for use with the 3.5 version of the Tuxgraphics 
  TCP/IP stack, offering new functionality (most notably routing through agateway). 
  Furthermore some of the initialization stuff was grouped together in 
  ES_enc28j60Init for convenience.
  
  May 2010:
  Andras Tucsni modified the wrapper class and enc28j60.c file for use with the 4.2 
  version of the Tuxgraphics TCP/IP stack. The following 4.2 functions are new in 
  this wrapper:
  * UDP server and UDP client methods
  * DNS resolving
  * added binary buffer fill method
  * generic TCP client (still with 1 TCP segment only limitation)
  Furthermore:
  * The library is made interrupt safe so that other (interrupt driven) SPI 
    peripherals can be used as well. Users wishing to use an alternate CS pin
    can change ENC28J60_CONTROL_CS in enc28j60.c However, beware that Arduino pin
    10 must always be high when initializing SPI, even when it isn't used as CS.
    When using other SPI peripherals, one may also wish to lower the SPI speed 
    in enc28j60.c when the other peripherals can't operate on Fosc/2 (and one
    doesn't want to modify all peripheral libraries to switch speeds dynamically).
  * Status LED flashing on initialization made quicker to save time.
  * Some explanation added to the declarations in this header file.
  * Some methods that aren't part of the client interface were removed (commented
    out in case you disagree).
  Note:
  This wrapper version is backwards compatible with the 2.x and 3.5 wrapper class, 
  except for the browser callback functions which now have an extra lenght argument 
  as of version 4.0.
  *
  * November 2010: Changes to allow client-conversation
  * Author: Peter Jaeger
  * Changes marked with comments "PJ change 2010-11-11"
  */

#ifndef ETHERSHIELD_H
#define ETHERSHIELD_H

#include <inttypes.h>
extern "C" {
#include "enc28j60.h"
#include "ip_config.h"
#include "ip_arp_udp_tcp.h"
#include "net.h"
#include "dnslkup.h"
}

class EtherShield
{
	public:

	EtherShield();

	//-- ENC28J60 driver --
	//The first ES* method to call in setup(). *macaddr points to a 6 byte MAC address
	void ES_enc28j60Init(uint8_t* macaddr);
	//Only relevant if the ENC28J60 clock output is used; normally not the case for Arduino
	void ES_enc28j60clkout(uint8_t clk);
	//Is the ethernet link up ?
	uint8_t ES_enc28j60linkup(void);
	//What is the ethernet revision number ?
	uint8_t ES_enc28j60getrev(void);
	//Low level method that can be used to blink your ethernet LEDs (used in ES_enc28j60Init)
	void ES_enc28j60PhyWrite(uint8_t address, uint16_t data);
	//Checks if any data (packet) was received. If so, the data is stored in *packet (len=MAX size)
	//Typically, this method is called just before calling the ES_packetloop method to proces the data.
	uint16_t ES_enc28j60PacketReceive(uint16_t len, uint8_t* packet);

	//-- General initialization --
	//Usually called in setup(). Initializes network settings and http server port
	void ES_init_ip_arp_udp_tcp(uint8_t *mymac,uint8_t *myip,uint16_t port);

	//-- UDP server --
	uint8_t ES_eth_type_is_ip_and_my_ip(uint8_t *buf,uint16_t len);
	void ES_make_udp_reply_from_request(uint8_t *buf,char *data,uint8_t datalen,uint16_t port);
	
	//These methods have been removed w.r.t. 2.x as they are not part of the client interface:
	uint8_t ES_eth_type_is_arp_and_my_ip(uint8_t *buf,uint16_t len);
	void ES_make_echo_reply_from_request(uint8_t *buf,uint16_t len);
	void ES_make_tcp_synack_from_syn(uint8_t *buf);
	void ES_make_arp_answer_from_request(uint8_t *buf);
	//void ES_init_len_info(uint8_t *buf);
	//uint16_t ES_get_tcp_data_pointer(void);

	// -- Web server --
	//Loop runner for icmp and TCP. Returns the position in *buf of the tcp payload if valid tcp
	//data was received (0=nodata). plen is the received packet length as returned by ES_enc28j60PacketReceive.
	uint16_t ES_packetloop_icmp_tcp(uint8_t *buf,uint16_t plen);
	//Methods to fill *buf with web page data. pos=0 on first call and the return value
	//of the previous call on subsequent calls.
	uint16_t ES_fill_tcp_data_p(uint8_t *buf,uint16_t pos, const prog_char *progmem_s);
	uint16_t ES_fill_tcp_data(uint8_t *buf,uint16_t pos, const char *s);
	uint16_t ES_fill_tcp_data_len(uint8_t *buf,uint16_t pos, const uint8_t *s, uint8_t len);
	//Actually send the data that has been filled in buf from the web server to the client
	//afther having used the fill methods previously.
	void ES_www_server_reply(uint8_t *buf,uint16_t dlen);
	
	// -- client functions --
#if defined (WWW_client) || defined (NTP_client) || defined (UDP_client) || defined (TCP_client) || defined (PING_client)
	//Removed because it's not part of the client interface:
	//uint8_t ES_client_store_gw_mac(uint8_t *buf, uint8_t *gwipaddr);
	//void ES_client_arp_whohas(uint8_t *buf,uint8_t *ip_we_search);
	//Not included: it's not used in the sample code and seems have only internal relevance
	//void client_gw_arp_refresh(void); //do a continues refresh until found:
	//void client_arp_whohas(uint8_t *buf,uint8_t *gwipaddr); //do an arp request once (call this function only if enc28j60PacketReceive returned zero:
	
	//Usually called in setup() to set router gateway IP address. If destination host is on 
	//the local LAN, then gwipaddr=hostipaddr. Initialize before using ES_client methods.
	void ES_client_set_gwip(uint8_t *gwipaddr);	
	//void ES_client_set_wwwip(uint8_t *wwwipaddr); //is defined as macro below
	//Check if we got an arp response yet for gw mac: 1 no GW mac yet, 0 have a gw mac
	uint8_t ES_client_waiting_gw(void);
#endif

#if defined (WWW_client) || defined (TCP_client) 
	//Set the destination host IP address of the next www/tcp client session.
#define ES_client_set_wwwip ES_client_tcp_set_serverip
	void ES_client_tcp_set_serverip(uint8_t *ipaddr);
#endif

#ifdef TCP_client
	// To use the tcp client you need to:
	//
	// Declare a callback function to get the result (tcp data from the server):
	//
	// uint8_t your_client_tcp_result_callback(uint8_t fd, uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data){...your code;return(close_tcp_session);}
	//
	// statuscode=0 means the buffer has valid data, otherwise len and pos_in_buf
	// are invalid. That is: do to use data_start_pos_in_buf and len_of_data
	// if statuscode!=0.
	//
	// This callback gives you access to the TCP data of the first
	// packet returned from the server. You should aim to minimize the server
	// output such that this will be the only packet.
	//
	// close_tcp_session=1 means close the session now. close_tcp_session=0
	// read all data and leave it to the other side to close it. 
	// If you connect to a web server then you want close_tcp_session=0.
	// If you connect to a modbus/tcp equipment then you want close_tcp_session=1
	//
	// Declare a callback function to be called in order to fill in the 
	//
	// request (tcp data sent to the server):
	// uint16_t your_client_tcp_datafill_callback(uint8_t fd){...your code;return(len_of_data_filled_in);}
	//
	// Now call: 
	// fd=client_tcp_req(&your_client_tcp_result_callback,&your_client_tcp_datafill_callback,portnumber);
	//
	// fd is a file descriptor like number that you get back in the fill and result
	// function so you know to which call of client_tcp_req this callback belongs.
	//
	// You can not start different clients (e.g modbus and web) at the
	// same time but you can start them one after each other. That is
	// when the request has timed out or when the result_callback was
	// executed then you can start a new one. The fd makes it still possible to
	// distinguish in the callback code the different types you started.
	//
	// Note that you might never get called back if the other side does
	// not answer. A timer would be needed to recongnize such a condition.
	//
	// We use callback functions because that is the best implementation
	// given the fact that we have very little RAM memory.
	//
	uint8_t ES_client_tcp_req(uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data),uint16_t (*datafill_callback)(uint8_t fd),uint16_t port);
// PJ change 2010-11-11a
	uint8_t ES_client_tcp_add(uint8_t *buf, uint8_t (*result_callback)(uint8_t fd,uint8_t statuscode,uint16_t data_start_pos_in_buf, uint16_t len_of_data),uint16_t (*datafill_callback)(uint8_t fd));
// PJ end change 2010-11-11a
#endif

//Removed: not part of client interface
//#define HTTP_HEADER_START ((uint16_t)TCP_SRC_PORT_H_P+(buf[TCP_HEADER_LEN_P]>>4)*4)

#ifdef WWW_client
	// ----- http get
	void ES_client_browse_url(prog_char *urlbuf, char *urlbuf_varpart, prog_char *hoststr,void (*callback)(uint8_t,uint16_t,uint16_t));
	// The callback is a reference to a function which must look like this:
	// void browserresult_callback(uint8_t statuscode,uint16_t datapos,uint16_t len)
	// statuscode=0 means a good webpage was received, with http code 200 OK
	// statuscode=1 an http error was received
	// any other statuscode means the other side in not a web server and in this case datapos is also zero
	// ----- http post
	// client web browser using http POST operation:
	// additionalheaderline must be set to NULL if not used.
	// postval is a string buffer which can only be de-allocated by the caller 
	// when the post operation was really done (e.g when callback was executed).
	// postval must be urlencoded.
	void ES_client_http_post(prog_char *urlbuf, prog_char *hoststr, prog_char *additionalheaderline, char *postval,void (*callback)(uint8_t,uint16_t,uint16_t));
	// The callback is a reference to a function which must look like this:
	// void browserresult_callback(uint8_t statuscode,uint16_t datapos)
	// statuscode=0 means a good webpage was received, with http code 200 OK
	// statuscode=1 an http error was received
	// statuscode=2 means the other side in not a web server and in this case datapos is also zero
#endif

#ifdef NTP_client
	void ES_client_ntp_request(uint8_t *buf,uint8_t *ntpip,uint8_t srcport);
	uint8_t ES_client_ntp_process_answer(uint8_t *buf,uint32_t *time,uint8_t dstport_l);
#endif

#ifdef UDP_client
	// There are two ways of using this UDP client:
	// 1) you call send_udp_prepare, you fill the data yourself into buf starting at buf[UDP_DATA_P], 
	// you send the packet by calling send_udp_transmit
	// 2) You just allocate a large enough buffer for you data and you call send_udp and nothing else
	// needs to be done.
	void ES_send_udp_prepare(uint8_t *buf,uint16_t sport, uint8_t *dip, uint16_t dport);
	void ES_send_udp_transmit(uint8_t *buf,uint8_t datalen);
	// send_udp sends via gwip, you must call client_set_gwip at startup, datalen must be less than 220 bytes
	void ES_send_udp(uint8_t *buf,char *data,uint8_t datalen,uint16_t sport, uint8_t *dip, uint16_t dport);

	// --- DNS lookup --- (uses UDP)
	// look-up a hostname (you should check client_waiting_gw() before calling this function):
	void ES_dnslkup_request(uint8_t *buf,const prog_char *progmem_hostname);
	// returns 1 if we have an answer from an DNS server and an IP
	uint8_t ES_dnslkup_haveanswer(void);
	// get information about any error (zero means no error, otherwise see dnslkup.c)
	uint8_t ES_dnslkup_get_error_info(void);
	// loop over this function to search for the answer of the DNS server.
	// You call this function when enc28j60PacketReceive returned non
	// zero and packetloop_icmp_tcp did return zero.
	uint8_t ES_udp_client_check_for_dns_answer(uint8_t *buf,uint16_t plen);
	// returns the host IP of the name that we looked up if dnslkup_haveanswer did return 1
	uint8_t *ES_dnslkup_getip(void);
#endif

	// you can find out who ping-ed you if you want:
	void ES_register_ping_rec_callback(void (*callback)(uint8_t *srcip));

#ifdef PING_client
	void ES_client_icmp_request(uint8_t *buf,uint8_t *destip);
	// you must loop over this function to check if there was a ping reply:
	uint8_t ES_packetloop_icmp_checkreply(uint8_t *buf,uint8_t *ip_monitoredhost);
#endif // PING_client

#ifdef WOL_client
	void ES_send_wol(uint8_t *buf,uint8_t *wolmac);
#endif // WOL_client

#ifdef FROMDECODE_websrv_help
	uint8_t ES_find_key_val(char *str,char *strbuf, uint8_t maxlen,char *key);
	void ES_urldecode(char *urlbuf);
#endif	// FROMDECODE_websrv_help

#ifdef URLENCODE_websrv_help
	void ES_urlencode(char *str,char *urlbuf);
#endif	// URLENCODE_websrv_help

	uint8_t ES_parse_ip(uint8_t *bytestr,char *str);
	void ES_mk_net_str(char *resultstr,uint8_t *bytestr,uint8_t len,char separator,uint8_t base);

};

#endif

