/*
    compressloop-slave, compression server for compressloop-distributed
    Copyright (C) 2004  James Cameron <quozl@us.netrek.org>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


    Derived from compressloop.c (c) 1999 by Paul `Rusty' Russell, GPL,
    with contributions in 2000 and 2001 by Klaus Knopper, and in 2002
    by Valentijn Sessink.

*/

#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <zlib.h>
#include <assert.h>
#include <errno.h>

#define BLOCKSIZE 65536
#define UPSIZE BLOCKSIZE + (BLOCKSIZE/1000) + 12
#define PORT 8522

ssize_t readp(int fd, void *buf, size_t count) {
  ssize_t stat, bytes = 0;

  while (bytes < count) {
    stat = read(fd, buf+bytes, count-bytes);
    if (stat <= 0) { return stat; }
    bytes += stat;
  }
  return bytes;
}

ssize_t writep(int fd, void *buf, size_t count) {
  ssize_t stat, bytes = 0;

  while (bytes < count) {
    stat = write(fd, buf+bytes, count-bytes);
    if (stat <= 0) { return stat; }
    bytes += stat;
  }
  return bytes;
}

int main(int argc, char **argv) {
  int optionchar, lsock, ov, stat, asock, version, eof, port=PORT, verbose=0;
  struct sockaddr_in laddr;
  
  while ((optionchar = getopt(argc, argv, "l:v")) != -1) {
    switch (optionchar) {
    case 'l':
      port = atoi(optarg);
      break;
    case 'v':
      verbose++;
      break;
    default:
      fprintf(stderr, "usage: slave [-l port] [-v]\n");
      exit(1);
    }
  }

  lsock = socket(AF_INET, SOCK_STREAM, 0);
  if (lsock == -1) { perror("socket"); exit(1); }
  if (verbose) fprintf(stderr, "socket\n");

  ov = 1;
  stat = setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &ov, sizeof(ov));
  if (stat < 0) { perror("setsockopt"); exit(1); }
  
  ov = BLOCKSIZE * 4;
  stat = setsockopt(lsock, SOL_SOCKET, SO_RCVBUF, (char *) &ov, sizeof(ov));
  if (stat < 0) { perror("setsockopt (SO_RCVBUF)"); }

  ov = BLOCKSIZE * 4;
  stat = setsockopt(lsock, SOL_SOCKET, SO_SNDBUF, (char *) &ov, sizeof(ov));
  if (stat < 0) { perror("setsockopt (SO_RCVBUF)"); }

  laddr.sin_family = AF_INET;
  laddr.sin_addr.s_addr = INADDR_ANY;
  laddr.sin_port = htons(port);

  stat = bind(lsock, (struct sockaddr *) &laddr, sizeof laddr);
  if (stat) { perror("bind"); exit(1); }
  if (verbose) fprintf(stderr, "bind\n");

  stat = listen(lsock, 1);
  if (stat) { perror("listen"); exit(1); }
  if (verbose) fprintf(stderr, "listen\n");

  while (1) {

    asock = accept(lsock, NULL, NULL);
    if (asock == -1) { perror("accept"); exit(1); }
    if (verbose) fprintf(stderr, "accept\n");

    stat = readp(asock, &version, sizeof(version));
    if (stat < 0) { perror("readp"); close(asock); continue; }
    if (stat == 0) { close(asock); continue; }
    version = ntohl(version);
    
    eof = 0;
    while (!eof) {
      int bytes = 0, size;
      unsigned long number, netlen, outlen = UPSIZE;
      char in[BLOCKSIZE], out[UPSIZE];
      
      stat = readp(asock, &number, sizeof(number));
      if (stat < 0) { perror("readp"); break; }
      if (stat == 0) { break; }
      number = ntohl(number);
      if (verbose > 1) fprintf(stderr, "block %lu ", number);

      stat = readp(asock, &size, sizeof(size));
      if (stat < 0) { perror("readp"); break; }
      if (stat == 0) { break; }
      size = ntohl(size);
      if (verbose > 1) fprintf(stderr, "size %d ", size);

      if (size > BLOCKSIZE) {
	fprintf(stderr, "slave: size > blocksize\n"); break; }

      stat = readp(asock, in, size);
      if (stat < 0) { perror("readp"); break; }
      if (stat == 0) { break; }

      bytes = stat;

      stat = compress2(out, &outlen, in, bytes, Z_DEFAULT_COMPRESSION);
      if (stat != Z_OK) {
	fprintf(stderr, "slave: compress2: error %d\n", stat);
	break;
      }
      if (verbose > 1) fprintf(stderr, "gz %lu ", outlen);
      
      number = htonl(number);
      stat = writep(asock, &number, sizeof(number));
      if (stat <= 0) { perror("write"); break; }

      netlen = htonl(outlen);
      stat = writep(asock, &netlen, sizeof(netlen));
      if (stat <= 0) { perror("write"); break; }
      
      stat = writep(asock, out, outlen);
      if (stat <= 0) { perror("write"); break; }
      
      if (verbose > 1) fprintf(stderr, "\n");
    }
    
    if (verbose) fprintf(stderr, "\n");
    close(asock);
  }

}

/* obligatory comment */
