/**
 * Simple example of broadcast transmission and reception, through the default network interface.
 * http://atastypixel.com/blog/the-making-of-talkie-broadcasting/
 *
 *  Compile with:
 *      cc -o broadcast_sample broadcast_sample.c
 *
 *  Usage:
 *    To transmit:
 *      ./broadcast_sample "Message to send"
 *
 *    To receive, call with no arguments:
 *      ./broadcast_sample
 *
 * Michael Tyson, A Tasty Pixel <michael@atastypixel.com>
 * http://atastypixel.com
 */

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

#define kPortNumber 1234
#define kBufferSize 1024

/*!
 *  Transmit data
 *
 *  @param data
 *    Data to send
 *  @param length
 *    Length of data, in bytes
 *  @result 0 on failure, 1 on success
 */
int transmit(char * data, int length) {
  
  // Create socket
  int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
  if ( sock_fd == -1 ) {
    // Error occurred
    return 0;
  }
  
  // Enable broadcast
  int flag = 1;
  int result = setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag));
  if ( result != 0 ) {
    // Error occurred
    return 0;
  }
  
  // Create address
  struct sockaddr_in addr;
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = INADDR_BROADCAST;
  addr.sin_port = htons(kPortNumber);
  
  // Send
  result = sendto(sock_fd, data, length, 0, (struct sockaddr*)&addr, sizeof(addr));
  if ( result < 0 ) {
     // Error occurred
    return 0;
  }
  
  return 1;
}

/*!
 *  Receive loop
 *
 *  @description
 *    Loops forever, waiting for data, and printing whatever comes in.
 *  @result 0 on failure
 */
int receive() {
  
  // Create socket
  int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
  if ( sock_fd == -1 ) {
    // Error occurred
    return 0;
  }
  
  // Create address from which we want to receive, and bind it
  struct sockaddr_in addr;
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = INADDR_ANY;
  addr.sin_port = htons(kPortNumber);
  int result = bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr));
  if ( result < 0 ) {
    // Error occurred
    return 0;
  }
  
  char buffer[kBufferSize];
  int addr_len = sizeof(addr);
  
  while ( 1 ) {
    
    // Receive a message, waiting if there's nothing there yet
    int bytes_received = recvfrom(sock_fd, buffer, kBufferSize, 0, (struct sockaddr*)&addr, &addr_len);
    if ( bytes_received < 0 ) {
       // Error occurred
       return 0;
    }
    
    // Now we have bytes_received bytes of data in buffer. Print it!
    fwrite(buffer, sizeof(char), bytes_received, stdout);
    printf("\n");
  }  
}


/*!
 *   Main entry point
 */
int main(int argc, char** argv) {
  if ( argc < 2 ) {
    // No argument: Just listen
    printf("Listening...\n");
    if ( !receive() ) {
      printf("Error occurred: %s\n", strerror(errno));
      return 1;
    }
    return 0;
  }

  // Argument provided: Transmit this
  if ( transmit(argv[1], strlen(argv[1])) ) {
    printf("\"%s\" transmitted.\n", argv[1]);
  } else {
    printf("Error occurred: %s\n", strerror(errno));
    return 1;
  }
  
  return 0;
}
