#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <malloc.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>

#include "o1k_conf.h"
#include "o1k_network.h"
#include "packet.h"
#include "transmit.h"

extern "C" {
  extern int gethostname(char* name, int namelen);
}

TransmitState *
initialize_transmit(O1kNetwork * network, int channel_offset)
{
    TransmitState *transmitter;
    int tsock;
    int on = 1;
    char hostname[MAXHOSTNAMELEN + 1];
    struct hostent *host;


    /*
     * .... create transmitter state ....
     */
    transmitter = (TransmitState *) malloc(sizeof(TransmitState));
    if (transmitter == NULL)
	return (NULL);


    /*
     * .... open transmitter socket and set broadcast option ....
     */
    if ((tsock = socket(AF_INET, network->type, 0)) < 0) {
	perror("Transmit socket");
	free(transmitter);
	return (NULL);
    }

    if (setsockopt(tsock, SOL_SOCKET, SO_BROADCAST, (char *) &on, sizeof(on))) {
	perror("Transmit setsockopt");
	close(tsock);
	free(transmitter);
	return (NULL);
    }
    transmitter->socket = tsock;


    /*
     * .... If there is no specific transmission destination, specified by a
     * host.domain string, then broadcast on the local network ....
     */
    if (network->hostnameknt == 0) {

	/*
	 * .... use local hostname to identify local network ....
	 */
	if (gethostname(hostname, sizeof(hostname)) < 0) {
	    perror("Transmit gethostname");
	    close(tsock);
	    free(transmitter);
	    return (NULL);
          }
        
	if (verbose) printf("Broadcasting on local net from %s\n", hostname);

	if ((host = gethostbyname(hostname)) == NULL) {
	    perror("Transmit gethostbyname");
	    close(tsock);
	    free(transmitter);
	    return (NULL);
	}

	memcpy((char *) &transmitter->name[0].sin_addr,
	       (char *) host->h_addr, host->h_length);

	transmitter->name[0].sin_addr.s_addr &= IN_CLASSC_NET;
	transmitter->name[0].sin_family = AF_INET;
	transmitter->name[0].sin_port = CHANNEL_BASE + network->tchannel + channel_offset;
	transmitter->nameknt = 1;

    }
    else {
	int i;
	int subflag;
	char *tptr;

        transmitter->nameknt = 0;
	for (i = 0; i < network->hostnameknt; i++) {
	    tptr = network->hostname[i];
	    subflag = 0xffffffff;
	    if (tptr[0] == 'S') {
		subflag = 0xffffff00;
		tptr++;
	    }

	    if (tptr[0] >= '0' && tptr[0] <= '9') {
		transmitter->name[transmitter->nameknt].sin_addr.s_addr =
		    inet_addr(tptr);
		transmitter->name[transmitter->nameknt].sin_addr.s_addr &=
		    subflag;
		transmitter->name[transmitter->nameknt].sin_family =
		    AF_INET;
		transmitter->name[transmitter->nameknt].sin_port =
		    CHANNEL_BASE + network->tchannel + channel_offset;
		transmitter->nameknt++;
	    }
	    else {
              if ((host = gethostbyname(tptr)) != NULL) {
		    memcpy((char *) &transmitter->name[transmitter->nameknt].sin_addr,
			   (char *) host->h_addr, host->h_length);
		    transmitter->name[transmitter->nameknt].sin_addr.s_addr &=
			subflag;
		    transmitter->name[transmitter->nameknt].sin_family =
			AF_INET;
		    transmitter->name[transmitter->nameknt].sin_port =
			CHANNEL_BASE + network->tchannel + channel_offset;
		    transmitter->nameknt++;
		}
		else {
		    fprintf(stderr, "Transmit gethostbyname '%s'  ", tptr);
		    perror("");
		}
	    }
	}
    }

    transmitter->session = (unsigned int) getpid();
    if (verbose) {
	char *tname;
	int i;

	if (verbose) printf("Session number = %d\n", transmitter->session);
        
	for (i = 0; i < transmitter->nameknt; i++) {
	    tname = "SUBNET";
	    if (transmitter->name[i].sin_addr.s_addr & 0xff) {
		if ((host = gethostbyaddr((char *) &transmitter->name[i].sin_addr, 4, AF_INET)) == NULL) {
		    perror("gethostbyaddr");
		    tname = "CAN NOT FIND";
		}
		else {
		    tname = host->h_name;
		}
	    }

	    if (verbose) printf("Sending to %d.%d.%d.%d\t\t%s\n",
                                (transmitter->name[i].sin_addr.s_addr >> 24) & 0xff,
                                (transmitter->name[i].sin_addr.s_addr >> 16) & 0xff,
                                (transmitter->name[i].sin_addr.s_addr >> 8) & 0xff,
                                (transmitter->name[i].sin_addr.s_addr >> 0) & 0xff,
                                tname);
          }
    }

    return (transmitter);
}


int 
transmit_packet(TransmitState * transmitter, PacketType * packet)
{
    int i;
    int len;
    int size;

    size = sizeof(PacketHeader) + PACKETDATASIZE;	/* packet->header.size; */
    for (i = 0; i < transmitter->nameknt; i++) {
	len = sendto(transmitter->socket, (char *) packet, size, 0,
		     (struct sockaddr *) & transmitter->name[i], sizeof(struct sockaddr_in));
	if (len != size)
	    return 0;
    }
    return 1;
}


void 
close_transmit(TransmitState * transmitter)
{
    close(transmitter->socket);
    free(transmitter);
}

void
transmit_data(unsigned char * data, int nbytes, 
	      TransmitState * tstate, O1kNetwork * network,
              int type, int tstamp)
{
  PacketType packet;
  static int frame_num[NET_MAX_TYPES];
  static int init = 1;  
  int count = 0;

  
  /* TODO: fix for restart condition */
  if (init){
    int i;
    for (i=0; i<NET_MAX_TYPES; i++)
      frame_num[i] = 0;
    init = 0;
  }
  
#if 0
  if (verbose) printf("transmit_data[%d]: %d bytes\n",type,nbytes);
#endif
  
  packet.header.frame = frame_num[type]++;
  /* (1<<type) == type index */
  packet.header.session = tstate->session | ((1<<type) << 28);
  
  packet.header.tstamp = tstamp;

  if (nbytes > PACKETDATASIZE * MAXPACKETS) {
    if (verbose) fprintf(stderr, "Not sending frame %d due to size (%d) > maximum\n",
                         packet.header.frame, nbytes);
  } else {
    while (nbytes > 0) {
      int curbytes = 0;
      
      if (nbytes <= PACKETDATASIZE) {
        curbytes = nbytes;
        packet.header.flags = ((1 << count) - 1);
      }
      else {
        curbytes = PACKETDATASIZE;
        packet.header.flags = ~(1 << count);
      }
      
      packet.header.size = curbytes;
      count++;
      nbytes -= curbytes;
      memcpy(packet.data, data, curbytes);
      data += curbytes;
#if 0
      if (verbose) printf("transmitting: %d bytes left: %d\n",packet.header.size, nbytes);
#endif
      transmit_packet(tstate, &packet);
    }
  }
  
  if (network->polling)
    poll(0, 0, network->polling);  
}

