#include "stdafx.h"
#include "VIBBox.h"

VIBBox::VIBBox(unsigned int IP, const char LongText[64])
{
	//init
	m_IpAddr =IP;
	m_Feature = 0;
	m_bGotFeature = false;
	strcpy_s(m_Description, sizeof(m_Description), LongText);
	memset(m_MAC, 0, 6);
}

VIBBox::~VIBBox(){}

//!setzt eine neue IP
bool VIBBox::ChangeIP(DWORD IPAddr, DWORD IpSubnet)
{
	BYTE Ip0, Ip1, Ip2, Ip3;
	struct sockaddr_in sa;
	char Packet[64];
	char Text[64];
	bool result = true;
	int packetSize;

	Ip0 = (BYTE)((IPAddr & 0xFF000000)>>24);
	Ip1 = (BYTE)((IPAddr & 0x00FF0000)>>16);
	Ip2 = (BYTE)((IPAddr & 0x0000FF00)>>8);
	Ip3 = (BYTE) (IPAddr & 0x000000FF);
	
	//Paket bauen
	sprintf_s(Packet, 64, "CIP!%s", m_Description);
	Packet[36] = Ip0;
	Packet[37] = Ip1;
	Packet[38] = Ip2;
	Packet[39] = Ip3;

	if (m_Feature & 8)
	{	// also set subnet mask
		*(unsigned int *)(Packet+40) = IpSubnet;
		packetSize = 44;
	}
	else
		packetSize = 64;

	//socket
	SOCKET sSocketUdp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sSocketUdp == INVALID_SOCKET){
		sprintf_s(Text, "Failed socket() (%d)", WSAGetLastError());;
		AfxMessageBox(Text);
		return false;
	}

	//Broadcast <-> Ja
	int Broadcast = 1;
	setsockopt(sSocketUdp, SOL_SOCKET, SO_BROADCAST, (char *)&Broadcast, sizeof(int));

	memset(&sa, 0, sizeof(sa));
	sa.sin_addr.s_addr = inet_addr("255.255.255.255");
	sa.sin_port        = htons(UDP_PORT_OUT);
	sa.sin_family      = AF_INET;

	if (sendto(sSocketUdp, Packet, packetSize, 0, (SOCKADDR*) &sa, sizeof(sa)) < 0){
		AfxMessageBox("Failed sendto()");
		result = false;
	}

	closesocket(sSocketUdp);

	return result;
}


//!löst ein Reset der Box aus
void VIBBox::ResetDevice()
{
	struct sockaddr_in sa;
	char Packet[64];
	char Text[64];

	sprintf_s(Packet, 64, "Res!%s", m_Description);

	//socket
	SOCKET sSocketUdp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sSocketUdp == INVALID_SOCKET){
		sprintf_s(Text, "Failed socket() (%d)", WSAGetLastError());;
		AfxMessageBox(Text);
		return;
	}

	//Broadcast <-> Ja
	int Broadcast = 1;
	setsockopt(sSocketUdp, SOL_SOCKET, SO_BROADCAST, (char *)&Broadcast, sizeof(int));

	memset(&sa, 0, sizeof(sa));
	sa.sin_addr.s_addr = inet_addr("255.255.255.255");
	sa.sin_port        = htons(UDP_PORT_OUT);
	sa.sin_family      = AF_INET;

	if (sendto(sSocketUdp, Packet, 64, 0, (SOCKADDR*) &sa, sizeof(sa)) < 0)
		AfxMessageBox("Failed sendto()");

	closesocket(sSocketUdp);
}

//!sucht nache Devices
int VIBBox::Discover(vector<iVIBBox*>* pTargetList)
{
	if(pTargetList == NULL)
		return -1;
	
	SOCKET sSocketUdp;	//!< UDP socket
	struct sockaddr_in sa,sa_out;
	struct sockaddr_in SenderAddr;
	int SenderAddrSize = sizeof(SenderAddr);
	int Broadcast = 1;
	int ret;
	unsigned int i;
	bool bActive = true;
	bool bIsFirstLoop = true;
	char Packet[64];
	char OutPacket[64];
	char Text[64];

	/* Socket aufmachen/vorbereiten */
	//socket erzeugen
	sSocketUdp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sSocketUdp == INVALID_SOCKET)
	{
		sprintf_s(Text, "Failed socket() (%d)", WSAGetLastError());;
		AfxMessageBox(Text);
		return -1;
	}
	
	//Broadcast <-> Ja
	setsockopt(sSocketUdp, SOL_SOCKET, SO_BROADCAST, (char *)&Broadcast, sizeof(int));

	// Increase receive buffer size
	int bufsize = 65536;
	setsockopt(sSocketUdp, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(int));

	//Bind an Port und alle Netzwerkkarten 
	memset(&sa, 0, sizeof(sa));
	sa.sin_addr.s_addr = INADDR_ANY;
	sa.sin_port        = htons(UDP_PORT_IN);
	sa.sin_family      = AF_INET;
	if (bind(sSocketUdp, (SOCKADDR*) &sa, sizeof(sa)) < 0)
	{
		sprintf_s(Text, "Failed bind() (%d)", WSAGetLastError());
		closesocket(sSocketUdp);
		AfxMessageBox(Text);
		return -1;
	}

	//Absender(PC) IPEndPoint
	memset(&sa_out, 0, sizeof(sa_out));
	sa_out.sin_addr.s_addr = inet_addr("255.255.255.255");
	sa_out.sin_port        = htons(UDP_PORT_OUT);
	sa_out.sin_family      = AF_INET;


	//==> Suchen <==
	/** Anfrage senden **/
	memset(OutPacket, 0, sizeof(OutPacket));
	strcpy_s(OutPacket, 64, "Who?");
	if (sendto(sSocketUdp, OutPacket, 64, 0, (SOCKADDR*) &sa_out, sizeof(sa_out)) < 0)
		AfxMessageBox("Failed sendto()\n");


	// Use select() for timeout
	struct timeval timeout;
	timeout.tv_sec = 0;
	timeout.tv_usec = 250000; // 250 ms

	fd_set readSet;
	FD_ZERO(&readSet);

	while (bActive)
	{
		// Wait for reply
		FD_SET(sSocketUdp, &readSet);
		if (select(0, &readSet, NULL, NULL, &timeout) <= 0)
		{
			//keine neues Paket	
			//war es der erste durchlauf dann nach den Probs fragen
			if(bIsFirstLoop)
			{
				//==> GetFeatures <==
				/** Anfrage senden **/
				sprintf_s(OutPacket, 64, "Get?");		
				if (sendto(sSocketUdp, OutPacket, 64, 0, (SOCKADDR*) &sa_out, sizeof(sa_out)) < 0)
					AfxMessageBox("Failed sendto()\n");

				bIsFirstLoop = false;
			}
			else
				bActive = false;
		}
		else
		{
			// Read message
			ret = recvfrom(sSocketUdp, Packet, 64, 0, (SOCKADDR*) &SenderAddr, &SenderAddrSize);

			//gibt es die IP schonn? -> Get features
			//ein Paket kommt so oft an wie es Netzwerkkarten gibt 
			for (i=0; i<pTargetList->size(); i++)
			{
				if (SenderAddr.sin_addr.S_un.S_addr == pTargetList->at(i)->Get_IpAddr())
				{
					if (Packet[0] == '#') // Get features
					{
						//TCPDisplay immer true
						pTargetList->at(i)->Set_Feature(Packet[1]);
						pTargetList->at(i)->Set_bGotFeature(true);
						if ((Packet[1] & 8) && ret >= 8)
						{	// Subnet mask
							pTargetList->at(i)->Set_IpSubnet(*(unsigned int *)(Packet+4));
						}
						if ((Packet[1] & 0x20) && ret >= 10)
						{
							pTargetList->at(i)->Set_UpdatePort(*(unsigned short *)(Packet+8));
						}
						if ((Packet[1] & 0x40) && ret >= 16)
						{
							pTargetList->at(i)->Set_MacAddress((unsigned char *)Packet+10);
						}
					}
					break;
				}
			}

			//eine neue IP
			if (i == pTargetList->size())
			{
				// Serial number always starts with letter
				if (Packet[0] != '#')
				{
					//Infos für neues Obj sammeln
					sprintf_s(Text, 64, "%s:%s", inet_ntoa(SenderAddr.sin_addr), Packet);
					VIBBox* obj = new VIBBox(SenderAddr.sin_addr.S_un.S_addr, Packet);

					//in liste einfügen
					pTargetList->push_back(obj);
				}
			}
		}
	}

	closesocket(sSocketUdp);
	return 0;
}
