#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <SFML/Audio.hpp>
#include <vector>
#include <Windows.h>


#define BUFSIZE     1024
#define MAXADDRSTR  16

#define TIMECAST_ADDR "234.5.6.7"
#define TIMECAST_PORT 8910

// custom audio stream that plays a loaded buffer
class MyStream : public sf::SoundStream
{
public:

	
    std::vector<sf::Int16> m_samples;
    std::size_t m_currentSample;
	HANDLE sem;

    void load(sf::Int16* buffer, size_t sampleCount, size_t sampleRate, size_t channelCount)
    {
		sem = CreateSemaphore(NULL, 1, 1, NULL);

        // extract the audio samples from the sound buffer to our own container
        m_samples.assign(buffer, buffer + sampleCount);

        // reset the current playing position 
        m_currentSample = 0;

        // initialize the base class
        initialize(channelCount, sampleRate);
    }

	void add(sf::Int16* buffer, size_t size)
	{
		// check semaphore
		WaitForSingleObject(sem, INFINITE);

		for (int i = 0; i < size; i++)
		{
			m_samples.push_back(buffer[i]);
		}
		ReleaseSemaphore(sem, 1, NULL);
	}

private:

    virtual bool onGetData(Chunk& data)
	{
		// number of samples to stream every time the function is called;
		// in a more robust implementation, it would rather be a fixed
		// amount of time rather than an arbitrary number of samples
		const int samplesToStream = 50000;

		//check semaphore
		WaitForSingleObject(sem, INFINITE);

		// set the pointer to the next audio samples to be played
		data.samples = &m_samples[m_currentSample];

		// have we reached the end of the sound?
		if (m_currentSample + samplesToStream <= m_samples.size())
		{
			// end not reached: stream the samples and continue
			data.sampleCount = samplesToStream;
			m_currentSample += samplesToStream;
			ReleaseSemaphore(sem, 1, NULL);
			return true;
		}
		else
		{
			// end of stream reached: stream the remaining samples and stop playback
			data.sampleCount = m_samples.size() - m_currentSample;
			m_currentSample = m_samples.size();
			ReleaseSemaphore(sem, 1, NULL);
			return false;
		}

	}

    virtual void onSeek(sf::Time timeOffset)
    {
        // compute the corresponding sample index according to the sample rate and channel count
        m_currentSample = static_cast<std::size_t>(timeOffset.asSeconds() * getSampleRate() * getChannelCount());
    }

};

char achMCAddr[MAXADDRSTR] = TIMECAST_ADDR;
u_long lMCAddr;
u_short nPort              = TIMECAST_PORT;
SYSTEMTIME *lpstSysTime, stSysTime;

int main(int argc, char *argv[]) {
  int nRet;
  int nIP_TTL = 2;
  BOOL  fFlag;
  SOCKADDR_IN stLclAddr, stSrcAddr;
  struct ip_mreq stMreq;         /* Multicast interface structure */
  SOCKET hSocket;
  WSADATA stWSAData;

  	int count = 0;
    // load an audio buffer from a sound file
    sf::SoundBuffer buffer;
	const sf::Int16* pSample;
	sf::Int16 * pSound;
	sf::Int16 * temp;
	sf::Int16 * test;
	size_t sampleCount, sampleRate, channelCount;
    buffer.loadFromFile("test.wav");

	sampleRate   = buffer.getSampleRate();
	channelCount = buffer.getChannelCount();
	pSample      = buffer.getSamples();
	sampleCount  = buffer.getSampleCount();


	pSound = const_cast<sf::Int16*>(pSample);
	temp   = const_cast<sf::Int16*>(pSample);
	test = temp;
	char achInBuf [sizeof(sampleRate)];
    // initialize and play our custom stream
	MyStream stream;
  /* Init WinSock */
  nRet = WSAStartup(0x0202, &stWSAData);
  if (nRet) {
      printf ("WSAStartup failed: %d\r\n", nRet);
      exit(1);
  }


  /* Get a datagram socket */
  hSocket = socket(AF_INET, 
     SOCK_DGRAM, 
     0);
  if (hSocket == INVALID_SOCKET) {
    printf ("socket() failed, Err: %d\n", WSAGetLastError());
    WSACleanup();
    exit(1);
  }

  /* Avoid (WSA)EADDRINUSE error on bind() (last one to bind should 
   *  receive dgrams sent to our port, but there are no guarantees of 
   *  this, unfortunately). */ 
  fFlag = TRUE;
  nRet = setsockopt(hSocket, 
     SOL_SOCKET, 
     SO_REUSEADDR, 
     (char *)&fFlag, 
     sizeof(fFlag));
  if (nRet == SOCKET_ERROR) {
    printf ("setsockopt() SO_REUSEADDR failed, Err: %d\n",
      WSAGetLastError());
  }

  /* Name the socket (assign the local port number to receive on) */
  stLclAddr.sin_family      = AF_INET;
  stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  stLclAddr.sin_port        = htons(nPort);
  nRet = bind(hSocket, 
      (struct sockaddr*) &stLclAddr, 
      sizeof(stLclAddr));
  if (nRet == SOCKET_ERROR) {
      printf ("bind() port: %d failed, Err: %d\n", nPort, 
      WSAGetLastError());
  }

  /* Join the multicast group so we can receive from it */
  stMreq.imr_multiaddr.s_addr = inet_addr(achMCAddr);
  stMreq.imr_interface.s_addr = INADDR_ANY;
  nRet = setsockopt(hSocket, 
     IPPROTO_IP, 
     IP_ADD_MEMBERSHIP, 
     (char *)&stMreq, 
     sizeof(stMreq));
  if (nRet == SOCKET_ERROR) {
    printf(
      "setsockopt() IP_ADD_MEMBERSHIP address %s failed, Err: %d\n",
      achMCAddr, WSAGetLastError());
  } 

  int addr_size = sizeof(struct sockaddr_in);
    
    stream.load(pSound, 250000, sampleRate, channelCount);
    stream.play();
	stream.setVolume(10);
	char * derp = (char *)pSound;
  for (;;) {
	  
    int addr_size = sizeof(struct sockaddr_in);

    nRet = recvfrom(hSocket, 
      derp, 
      sizeof(sampleRate), 
      0,
      (struct sockaddr*)&stSrcAddr, 
      &addr_size);

    if (nRet < 0) {
      printf ("recvfrom() failed, Error: %d\n", WSAGetLastError());
      WSACleanup();
      exit(1);
    }

	temp = reinterpret_cast<sf::Int16*>(derp);
	stream.add(temp, sampleRate);

  } /* end for(;;) */

  /* Leave the multicast group: With IGMP v1 this is a noop, but 
   *  with IGMP v2, it may send notification to multicast router.
   *  Even if it's a noop, it's sanitary to cleanup after one's self.
   */
  stMreq.imr_multiaddr.s_addr = inet_addr(achMCAddr);
  stMreq.imr_interface.s_addr = INADDR_ANY;
  nRet = setsockopt(hSocket, 
     IPPROTO_IP, 
     IP_DROP_MEMBERSHIP, 
     (char *)&stMreq, 
     sizeof(stMreq));
  if (nRet == SOCKET_ERROR) {
    printf (
      "setsockopt() IP_DROP_MEMBERSHIP address %s failed, Err: %d\n",
      achMCAddr, WSAGetLastError());
  } 

  /* Close the socket */
  closesocket(hSocket);

  /* Tell WinSock we're leaving */
  WSACleanup();

  return (0);
} /* end main() */