#include "mp3.h" /* mp3 header defs */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/file.h>
#include <netinet/in.h> /* sockaddr_in structure */
#include <netdb.h> /* /etc/hosts table entries */
#define BUFFERSIZE 2000
#define PACKETSIZE 1300

/* global variables */
   struct sockaddr_in myClientSockAddr; /* socket structure */
   int mySocket;
   char sendBuffer[2*PACKETSIZE];
   char dataBuffer[10*BUFFERSIZE];
   char headerBuffer[2*BUFFERSIZE];
   int bitRate;
   int samplingFrequency;
   int padding;
   int frameSize;
   long timeInterval;
   long fileOffset, uselessHeaderLength;
   FILE *mp3file;
   mp3header frameHeader;
   RTPheader packetHeader;
   MSheader specificHeader;
   int currentFrame=0;
   int currentPacket=0;
   int numFrames;
   int dataBufferOffset = 0;
   int frameCount = 0;

void myExit(int code)
  {
  close(mySocket);
  fclose(mp3file);
  printf("\nExit called, code %d.\n",code);
  exit(code);
  }

void initPacketHeader(RTPheader packetHeader)
  {
  packetHeader.V=2; /* version */
  packetHeader.X=0; /* extended header */
  packetHeader.CC=0; /* contributing sources count */
  packetHeader.M=0; /* marker */
  packetHeader.PT=40; /* payload type */
  packetHeader.SN=0; /* sequence number */
  packetHeader.TS=rand(); /* timestamp */
  packetHeader.SSRC=1234; /* source id */
  }

int myfread(void *b, int c, int o, FILE *f)
  {
  int r;
  if ((r=fread(b, c, o, f))<=0) /* exits on error and eof */
    {
    printf("\nRead error.\n");
    myExit(1);
    }
  return r;
  }

/* set client name, verify it and copy it to structure */
void setClient(char * name)
   {
   struct hostent *hp; /* For lookup in /etc/hosts file. */
   hp = gethostbyname(name); /* try to lookup host */
   if (hp == NULL)
      {
      printf("Could not find host, error code %d\n", errno);
      perror("Error message"); /* print the error mesage */
      myExit(1); /* myExit program */
      }
     else
      {/* get the first address in the server's address list */
      bcopy(hp->h_addr_list[0], &myClientSockAddr.sin_addr.s_addr, hp->h_length);
      printf("Host valid. Official name: %s\n", hp->h_name);
      }
   }

/* set server port and copy it to structure */
void setPort(char * number)
   {
   int myPort = atoi(number);
   myClientSockAddr.sin_port = htons(myPort); /* Server port #, converted locally */
   }

/* connect to the client */
int connectToClient()
   {
   int mySocket;
   mySocket = socket(AF_INET, SOCK_DGRAM, 0);
   if (mySocket==-1)
      {
      printf("Server connection socket create failure, error code %d\n", errno);
      perror("Error message"); /* print error message */
      close (mySocket); /* preventive */
      myExit(1);
      }
   myClientSockAddr.sin_family = AF_INET;    /* Internet domain */
   if ((connect(mySocket, (struct sockaddr*)&myClientSockAddr, sizeof(myClientSockAddr)))<0)
      {
      printf("Server connection socket connect failure, error code %d\n", errno);
      perror("Error message"); /* print error message */
      close (mySocket); /* preventive */
      myExit(1);
      }
   printf("Connected...\n");
   return mySocket;
   }

/* read the header from file and set some global vars */
void readHeader()  
  {
  int bytesToSend;
  int maxFrames;
  myfread(&frameHeader, sizeof(frameHeader), 1, mp3file);
  if (frameHeader.sync != 0xfff) printf("Sync error!");
  bitRate = bitRates[frameHeader.BRI];
  samplingFrequency = samplingFrequencies[frameHeader.SF];
  padding = frameHeader.PaB;
  frameSize = (int)(((1152l*(long)bitRate/8l)/(long)samplingFrequency)+(long)padding);
  maxFrames = 1300 / frameSize;
  if (maxFrames == 0)
    {
    printf("\nCould not fit even one frame per packet, framesize=%d!\n", frameSize);
    myExit(1);
    }
  numFrames = (int)(samplingFrequency * 2 / 11520); /* frames in 200ms */
  if (maxFrames < numFrames) numFrames = maxFrames; /* fit in a packet */
  bytesToSend = numFrames * frameSize;
  timeInterval = (bytesToSend/(bitRate/8.)*1000); /* adjust actual interval, ms */
  }

/* this does the actual work, hence the name */
void doWork()
  {
  int packetSize;
  char padding;
  fpos_t last;
  int totalDataSize;
  int frameStartOffset; /* marker in data buffer for the last frame's start byte */
  int i;
  packetHeader.SN = currentPacket++; /* set sequence number */
  packetHeader.TS += timeInterval * samplingFrequency / 1000; /* set timestamp */
  specificHeader.FFN = currentFrame; /* set first frame number */
  memcpy(&frameHeader, headerBuffer, sizeof(frameHeader)); /* copy header */
  packetSize = sizeof(packetHeader) + sizeof(specificHeader) + frameCount * sizeof(frameHeader);
  do
    {
    readHeader(); /* and set some global vars */
    memcpy(headerBuffer + frameCount * sizeof(frameHeader), &frameHeader, sizeof(frameHeader)); /* copy header */
    myfread(dataBuffer + dataBufferOffset, frameSize - sizeof(frameHeader), 1, mp3file); /* read data */
    frameStartOffset = dataBufferOffset; /* the frame's offset is relative to this marker */
    dataBufferOffset += (frameSize - sizeof(frameHeader)); /* offset for the next data */
    packetSize += sizeof(frameHeader); /* one more header in */
    frameCount++; /* and one more frame */
    }
  while ((frameCount <= numFrames + 1)&&(packetSize + dataBufferOffset < PACKETSIZE));
  specificHeader.lastFrameOffset = frameHeader.offset; /* offset of first frame in next packet */
  packetSize -= sizeof(frameHeader); /* take it back, not sending the last one */
  frameCount--;
  totalDataSize = frameStartOffset - frameHeader.offset; /* subtract the last frame data */
  packetSize += totalDataSize; /* add the data size too */
  specificHeader.FFN = currentFrame; /* first frame # */
  currentFrame += frameCount;
  specificHeader.numFrames = frameCount; /* number of frames in packet */
  bzero(sendBuffer,PACKETSIZE); /* clear buffer */
  memcpy(sendBuffer, &packetHeader, sizeof(packetHeader)); /* assemble packet */
  memcpy(sendBuffer + sizeof(packetHeader), &specificHeader, sizeof(specificHeader));
  memcpy(sendBuffer + sizeof(packetHeader) + sizeof(specificHeader), headerBuffer, sizeof(frameHeader) * frameCount); 
  memcpy(sendBuffer + sizeof(packetHeader) + sizeof(specificHeader) + sizeof(frameHeader) * frameCount, dataBuffer, totalDataSize);
  padding = packetSize % 4; /* need padding? */
  packetSize += padding;
  packetHeader.P = 0;
  if (padding != 0)
    {
    packetHeader.P = 1;
    sendBuffer[packetSize - 1] = padding;
    }
  memcpy(sendBuffer, &packetHeader, sizeof(packetHeader)); /* put the last piece in */
/*  if (packetHeader.SN%7!=5) /* used this to simulate loss */
    send(mySocket, sendBuffer, packetSize, 0); /* send the packet */
  memcpy(dataBuffer, dataBuffer + totalDataSize, dataBufferOffset - totalDataSize); /* copy data at beginning of buffer */
  dataBufferOffset = dataBufferOffset - totalDataSize; /* start position next time */
  memcpy(headerBuffer, headerBuffer + frameCount * sizeof(frameHeader), sizeof(frameHeader)); /* copy header too */
  frameCount = 1; /* next time already have one frame header and some data */
  }

/* main program loop */
int main(int argc, char *argv[])
  {
  struct timeval last, now;
  struct timezone tz;
  char fileName[256];
  long mseconds;
  signal(SIGINT, myExit);
  signal(SIGPIPE, myExit);
  printf("mp3 Server\n");
  if (argc == 7)
    {
    setClient(argv[2]);
    setPort(argv[4]);
    strcpy(fileName,argv[6]);
    if (!(mp3file=fopen(fileName,"r")))
      {
      printf("\nFile %s could not be opened!\n",fileName);
      myExit(1);
      }
    mySocket = connectToClient();
    gettimeofday(&last, &tz);
    timeInterval = 200;
    initPacketHeader(packetHeader);
    do
      {
      gettimeofday(&now, &tz);
      mseconds = (long)(((double)now.tv_sec - (double)last.tv_sec) * 1000 + ((double)now.tv_usec - (double)last.tv_usec) / 1000);
      if (mseconds >= timeInterval)
        {
        printf("#"); /* some sign that it works */
        doWork();
        printf("*");
        fflush(stdout);
        memcpy(&last, &now, sizeof(now));
        }
      }
    while (1); /* infinite loop, will get out via exit on eof */
    }
   else printf("Usage: mp3_server -dest <client_address> -dataport <client_port> -file <mp3_file>\n");
  }
