/*
 * Server program copyright (C) 2009 - 2013 H.Niwa 
 */

/*
 * 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

#ifndef __MINGW32__ 

#include "config.h"
 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>

#include <string>
#include <complex>

#include "syserr.h"
#include "bin_node.h"
#include "gc.h"
#include "var.h"
#include "pred.h"
#include "context.h"
#include "unify.h"
#include "builtin.h"
#include "sysmodule.h"

#include "server.h"


int ProcServer(Node* &val, int n, char* sockname)
{
	int fd1, fd2;
	struct sockaddr_un saddr;
	struct sockaddr_un caddr;
	char buf[1024+1];


	fd1 = socket(PF_UNIX, SOCK_STREAM, 0);
	if (fd1 < 0) {
		syserr("ProcServer error : socket \n");
		return 0;
	}

	bzero((char *) &saddr, sizeof(saddr));

	saddr.sun_family = AF_UNIX;
	strncpy(saddr.sun_path, sockname, strlen(sockname));

	unlink(sockname);
	if (bind(fd1, (struct sockaddr *) &saddr,
		     sizeof(saddr.sun_family) + strlen(sockname)) < 0) {
		close(fd1);
		unlink(sockname);
		syserr("ProcServer error : bind \n");
		return 0;
	}

	if (listen(fd1, 1) < 0) {
		close(fd1);
		unlink(sockname);
		syserr("ProcServer error : listen \n");
		return 0;
	}

	socklen_t len;
	int	i, ret;

	if (val == Nil) {
		for (i = 0; i < n; i++) {
			val = Cons(Nil, val);
		}
	}

	len = sizeof(caddr);

	int pno;
	for (pno=0; pno < n; pno++) {
		if ((fd2 = accept(fd1, (struct sockaddr *) &caddr, &len)) < 0) {
			shutdown(fd1, SHUT_RDWR);
			close(fd1);
			unlink(sockname);
			syserr("ProcServer error : accept \n");
			return 0;
		}
//printf("ProcServer recv \n");

		ret = recv(fd2, buf, 1024, 0);
		if (ret < 0) {
			shutdown(fd1, SHUT_RDWR);
			close(fd1);
			unlink(sockname);
			syserr("ProcServer error : recv error \n");
			return 0;
		}

		int* pktlen    = (int*)buf;
		int* num       = (int*)(buf+sizeof(int));
		int* status    = (int*)(buf+sizeof(int)*2);
		int* floatflg  = (int*)(buf+sizeof(int)*3);
		buf[1024] = 0;

//printf("ProcServer n %d pktlen %d num %d status %d : %s\n", n, *pktlen, *num, *status, buf+12);
		if (*num >= n) {
			shutdown(fd1, SHUT_RDWR);
			close(fd1);
			close(fd2);
			unlink(sockname);
			syserr("ProcServer error : too long LIST \n");
			return 0;
		}

		if (*status == 1) {
			if (*floatflg) {
				long double* real  
					= (long double*)(buf+sizeof(int)*4);
				long double* image  
					= (long double*)(buf+sizeof(int)*4
							+sizeof(long double));
				std::complex<long double> c(*real, *image);
				
				int j;
				Node* nd = val;
				for (j = 0; j < *num; j++) {
					nd = nd->Cdr();
				}
				((List*)nd)->SetCar(mka(c));

			} else {				
				std::string s = buf+sizeof(int)*4;
		
				int i, l = *pktlen;
				for (i = 1024; i < l; i+=1024) {
					ret = recv(fd2, buf, 1024, 0);
					if (ret < 0) {
						shutdown(fd1, SHUT_RDWR);
						close(fd1);
						unlink(sockname);
						syserr("ProcServer error : recv error \n");
						return 0;
					}

					buf[1024] = 0;
					s += buf;
				}

				int j;
				Node* nd = val;
				for (j = 0; j < *num; j++) {
					nd = nd->Cdr();
				}
				((List*)nd)->SetCar(mka((char*)s.c_str()));
			}
		}		
		close(fd2);

//printf("server pno %d \n", pno);
//PrintNode("LIST val ", val);

	}
	
	shutdown(fd1, SHUT_RDWR);
	close(fd1);
	
	unlink(sockname);
	return 1;
}

int FirstProcServer(Node* &val, int n, char* sockname)
{
	int fd1, fd2;
	struct sockaddr_un saddr;
	struct sockaddr_un caddr;
	char buf[1024+1];

	fd1 = socket(PF_UNIX, SOCK_STREAM, 0);
	if (fd1 < 0) {
		syserr("ProcServer error : socket \n");
		return 0;
	}

	bzero((char *) &saddr, sizeof(saddr));

	saddr.sun_family = AF_UNIX;
	strncpy(saddr.sun_path, sockname, strlen(sockname));

	unlink(sockname);
	if (bind(fd1, (struct sockaddr *) &saddr,
		     sizeof(saddr.sun_family) + strlen(sockname)) < 0) {
		close(fd1);
		unlink(sockname);
		syserr("ProcServer error : bind \n");
		return 0;
	}

	if (listen(fd1, 1) < 0) {
		close(fd1);
		unlink(sockname);
		syserr("ProcServer error : listen \n");
		return 0;
	}

	socklen_t len;
	int	i, ret;

	if (val == Nil) {
		for (i = 0; i < n; i++) {
			val = Cons(Nil, val);
		}
	}

	len = sizeof(caddr);

	int pno;
	for (pno=0; pno < n; pno++) {
		if ((fd2 = accept(fd1, (struct sockaddr *) &caddr, &len)) < 0) {
			shutdown(fd1, SHUT_RDWR);
			close(fd1);
			unlink(sockname);
			syserr("ProcServer error : accept \n");
			return 0;
		}

		ret = read(fd2, buf, 1024);

		int* pktlen    = (int*)buf;
		int* num       = (int*)(buf+sizeof(int));
		int* status    = (int*)(buf+sizeof(int)*2);
		int* floatflg  = (int*)(buf+sizeof(int)*3);
		buf[1024] = 0;

		if (*num >= n) {
			shutdown(fd1, SHUT_RDWR);
			close(fd1);
			close(fd2);
			unlink(sockname);
			syserr("ProcServer error : too long LIST \n");
			return 0;
		}

		if (*status == 1) {
			if (*floatflg) {
				long double* real  
					= (long double*)(buf+sizeof(int)*4);
				long double* image  
					= (long double*)(buf+sizeof(int)*4
							+sizeof(long double));
				std::complex<long double> c(*real, *image);
				
				val = mka(c);
				close(fd2);
				break;
			} else {
				std::string s = buf+sizeof(int)*4;
//printf("server %s %s \n", buf+12, s.c_str());
		
				int i, l = *pktlen;
				for (i = 1024; i < l; i+=1024) {
					ssize_t sz = read(fd2, buf, 1024);
					buf[1024] = 0;
					s += buf;
//printf("... server %s %s \n", buf, s.c_str());
				}

				val = mka((char*)s.c_str());
				close(fd2);
				break;
			}
		}		
		close(fd2);

//printf("server pno %d \n", pno);
//PrintNode("LIST val ", val);

	}
	
	shutdown(fd1, SHUT_RDWR);
	close(fd1);
	
	unlink(sockname);
	return 1;
}

#endif /* __MINGW32__ */
