#pragma once
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include<string>;

#include "iphdr.h"

#ifdef STARFLEETTOOLBOX_EXPORTS
#define ETHDEVICEPINGER __declspec(dllexport)
#else
#define ETHDEVICEPINGER __declspec(dllimport)
#endif

#define DEFAULT_DATA_SIZE		32       // default data size
#define DEFAULT_SEND_COUNT		4        // number of ICMP requests to send
#define DEFAULT_RECV_TIMEOUT	1000     // one second
#define DEFAULT_TTL				128
#define MAX_RECV_BUF_LEN		0xFFFF   // Max incoming packet size.
#define PING_SLEEP				250		// sleep between ping

using namespace std;

/// <summary>
/// The ETHERNET device pinger: a helper to allow sending ICMP packets to a host
/// </summary>
class ETHDEVICEPINGER DevicePinger
{
public:
	/// <summary>
	/// Default constructor
	/// </summary>
	DevicePinger();

	/// <summary>
	/// A copy constructor accepting the IPV4 host address to ping
	/// </summary>
	/// <param name="address">The host IPV4 adress</param>
	DevicePinger(string address);

	/// <summary>
	/// A copy constructor accepting the IPV4 host address to ping
	/// </summary>
	/// <param name="address">The host IPV4 adress</param>
	/// <param name="timeout">The allowed ping timeout in milliseconds</param>
	DevicePinger(string address, int timeout);

	/// <summary>
	/// Sets the host IPV4 Address to ping
	/// </summary>
	/// <param name="address">The host IPV4 adress</param>
	void SetAddress(string address);
		

	/// <summary>
	/// Pings the host
	/// </summary>
	/// <returns>An encapsulation of ping returns</returns>
	PING_RESULT Ping();


	/// <summary>
	/// Pings the host
	/// </summary>
	/// <param name="packetAmount">How much packet to be sent</param>
	/// <returns>An encapsulation of ping returns</returns>
	PING_RESULT Ping(int packetAmount);

private:
	SOCKET	_s = INVALID_SOCKET;
	WSAOVERLAPPED _recvol;
	char* _icmpbuf = NULL;
	int _packetlen = 0;
	int _receiveTimeOut = DEFAULT_RECV_TIMEOUT;
	struct addrinfo* _dest = NULL;


	int		_addressFamily = AF_INET,				/// Address family to use
			_protocol = IPPROTO_ICMP,				/// Protocol value
			_ttl = DEFAULT_TTL;						/// Default TTL value
	int		_dataSize = DEFAULT_DATA_SIZE;			/// Amount of data to send
	BOOL	_recordRoute = FALSE;					/// Use IPv4 record route?
	char*	_destination = NULL,					/// Destination
			_recvbuf[MAX_RECV_BUF_LEN];				/// For received packets
	int		_recvbuflen = MAX_RECV_BUF_LEN;			/// Length of received packets.
	char _lastKnownIp[100];							/// The last known IP
	/// <summary>
	/// Initializes the ICMP Raw socket
	/// </summary>
	bool Initialize();

	/// <summary>
	/// Inits the ICMP IPV4 Header
	/// </summary>
	/// <param name="buf">A buffer</param>
	/// <param name="datasize">the datasize to be set</param>
	void InitIcmpHeader(char* buf, int datasize);

	/// <summary>
	/// This function calculates the 16 - bit one's complement sum of the supplied buffer (ICMP) header.
	/// </summary>
	/// <param name="buffer"></param>
	/// <param name="size"></param>
	///	<returns></returns>
	USHORT Checksum(USHORT* buffer, int size);

	/// <summary>
	/// Sets the sequence number of the ICMP request packet.
	/// </summary>
	void  SetIcmpSequence(char* buf);

	/// <summary>
	///This routine computes the checksum for the ICMP request.For IPv4 its
	///    easy, just compute the checksum for the ICMP packet and data. For IPv6,
	///    its more complicated. The pseudo checksum has to be computed for IPv6
	///    which includes the ICMP6 packet and data plus portions of the IPv6
	///    header which is difficult since we aren't building our own IPv6
	///    header. 
	/// </summary>
	/// <param name="s"></param>
	/// <param name="buf"></param>
	/// <param name="packetlen"></param>
	/// <param name="dest"></param>
	void ComputeIcmpChecksum(SOCKET s, char* buf, int packetlen, struct addrinfo* dest);

	/// <summary>
	/// Posts an overlapped WSARecvFrom on the raw socket.
	/// </summary>
	/// <param name="s"></param>
	/// <param name="buf"></param>
	/// <param name="buflen"></param>
	/// <param name="from"></param>
	/// <param name="fromlen"></param>
	/// <param name="ol"></param>
	/// <returns></returns>
	int PostRecvfrom(SOCKET s, char* buf, int buflen, SOCKADDR* from, int* fromlen, WSAOVERLAPPED* ol);

	/// <summary>
	/// Sets the TTL on the socket.
	/// </summary>
	/// <param name="s"></param>
	/// <param name="ttl"></param>
	/// <returns></returns>
	int SetTtl(SOCKET s, int ttl);

	/// <summary>
	/// Resolves the specified address and returns a list of addrinfo
	///    structure containing SOCKADDR structures representing the resolved addresses.
	///    Note that if 'addr' is non-NULL, then getaddrinfo will resolve it whether
	///    it is a string listeral address or a hostname.
	/// </summary>
	/// <param name="addr"></param>
	/// <param name="port"></param>
	/// <param name="af"></param>
	/// <param name="type"></param>
	/// <param name="proto"></param>
	/// <returns></returns>
	struct addrinfo* ResolveAddress(char* addr, char* port, int af, int type, int proto);
};

