/**********************************************************************
 
	Copyright (C) 2003 
	Hirohisa MORI <joshua@nichibun.ac.jp>
	Tomohito Nakajima <nakajima@zeta.co.jp>

	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/


/*
#include <stdio.h>
#include <stdlib.h>
#include "memory_debug.h"
#include "utils.h"
#include <windows.h>
#include "init.h"
*/
#include "stream.h"
#include "memory_debug.h"
#include "init.h"

#include "avt.h" /* for er_panic , but why defined this header .... ?*/

extern int max_fid;
extern S_TABLE s_file_table;
extern int s_task_flag;

void meke_proc_send_event_name(char *ret_event_name, const char *map_name){
	sprintf(ret_event_name, "%s_event", map_name);
}

/* make proc_send chunks and write that to shared memory */
HANDLE setup_proc_send(STREAM **stp, char *ret_map_name, char *close_flags, char *messge_on_give, char *message_on_receive)
{
int stream_count;
STREAM **streams;
PROC_SEND_CHUNK **chunks;
int chunk_count;
int total_size;
static LONG map_index=0;
void *shared_buffer;
int pointer;
PROC_SEND_MESSAGE *msg;
int i;
HANDLE file_map;
PROC_SEND_CHUNK terminater;

	streams = stp;
	for( stream_count = 0; *stp; ++stp,++stream_count);
	
	/* allocate chunk array buffer. stream_count + 2(for PROC_SEND_MESSAGE chunk + terminater) */
	chunk_count = stream_count+2;
	chunks = (PROC_SEND_CHUNK **)d_alloc(chunk_count*sizeof(PROC_SEND_CHUNK *));
	memset(chunks, 0, (chunk_count)*sizeof(PROC_SEND_CHUNK *));
	
	/* first chunk is the message to be written by child process on receive this streams. */
	msg = (PROC_SEND_MESSAGE*)d_alloc(sizeof(PROC_SEND_HEADER)+strlen(message_on_receive)+1);
	strcpy(msg->message_on_receive, message_on_receive);
	msg->header.size = sizeof(PROC_SEND_HEADER)+strlen(message_on_receive)+1;
	msg->header.type = 'm';
	chunks[0]=(PROC_SEND_CHUNK *)msg;
	
	/* convert streams to proc_send_chunks */
	for(i=0; i<stream_count; ++i){
		if(messge_on_give && strlen(messge_on_give)>0){
			if(s_write(streams[i], messge_on_give, strlen(messge_on_give)) < 0){
				er_panic("setup_proc_send messge_on_give '%s'", messge_on_give);
			}
		}
		streams[i]->h.tbl->proc_send(streams[i], &chunks[i+1], close_flags[i]);
	}

	terminater.header.size=sizeof(PROC_SEND_HEADER);
	terminater.header.type='t';
	chunks[i+1] = &terminater;
	
	/* get total size of chunks */
	total_size = 0;
	for(i=0;i<chunk_count;++i){
		total_size += chunks[i]->header.size;
	}
	
	/* write chunks to shared buffer */
	sprintf(ret_map_name, "gbs_proc_send%ld_%ld", GetCurrentProcessId(), InterlockedIncrement(&map_index));
	file_map = CreateFileMapping((HANDLE)0xffffffff,
			NULL,
			PAGE_READWRITE,
			0, 
			total_size, ret_map_name);
	if(file_map==NULL){
		er_panic("can not create file mapping. cause:%d", GetLastError());
	}
	
	shared_buffer = MapViewOfFile(file_map, FILE_MAP_WRITE, 0, 0, total_size);
	if(shared_buffer==NULL){
		er_panic("can not map view of file cause:%d", GetLastError());
	}
	pointer=0;
	for(i=0;i<chunk_count;++i){
		memcpy(((char*)shared_buffer)+pointer, chunks[i], chunks[i]->header.size);
		pointer += chunks[i]->header.size;
	}
	
	UnmapViewOfFile(shared_buffer);
	
	d_f_ree(msg);
	d_f_ree(chunks);

	return file_map;
}

int
launch_proc(
	char * app_name,
	char ** argv,
	STREAM ** stp,
	char * close_flags,
	char * ret_str_parent,
	char * ret_str_child)
{

PROCESS_INFORMATION  pi;
STARTUPINFO  si;
char command_line[MAX_PATH];
char map_name[256];
HANDLE hmap;
BOOL stream_to_pass_exists;
HANDLE stream_receive_event;
SECURITY_ATTRIBUTES sa;
int i;

	stream_to_pass_exists = (stp && stp[0]);
	if(ret_str_parent==0) 
		ret_str_parent = "";
	if(ret_str_child==0)
		ret_str_child = "";

	/* ready to pass streams to child process */
	hmap = setup_proc_send(stp, map_name, close_flags, ret_str_parent, ret_str_child);
	
	/* launch child proc */
    memset(&si, 0, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);
	
	strcpy(command_line, app_name);
	if(stream_to_pass_exists){
		/* prepare stream recieve event object */
		char event_name[256];
		meke_proc_send_event_name(event_name, map_name);
		sa.bInheritHandle = TRUE;
		sa.lpSecurityDescriptor = NULL;
		sa.nLength = sizeof(sa);
		stream_receive_event = CreateEvent(&sa, FALSE, FALSE, event_name);
		if(stream_receive_event==NULL){
			er_panic("error create event %s", map_name);
		}
		/* "+" indicates stream passed. and map_name is shared memory name where streams locates.*/
		strcat(command_line, " +");
		strcat(command_line, map_name);
	}

	for(i=0; argv[i]; ++i){
		strcat(command_line, " ");
		strcat(command_line, argv[i]);
	}

	remove_command_line_ampersand(command_line);
	if(!CreateProcess(NULL, command_line, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)){
		char *err_msg;
		err_msg = d_alloc(strlen(command_line)+64);
		sprintf(err_msg, "create process error! command_line=%s", command_line);
		er_panic(err_msg);
	}
	
	if(stream_to_pass_exists){
		/* wait for pass stream complete or child process end */
		for(;;){
			if(WaitForSingleObject(stream_receive_event, 1000)!=WAIT_TIMEOUT){
				break;
			}
			if(WaitForSingleObject(pi.hProcess, 0)!=WAIT_TIMEOUT){
				er_panic("child process finished before receive handles");
				break;
			}
		}
		
		CloseHandle(hmap);
		CloseHandle(stream_receive_event);
	}
	
	if(s_task_flag & INI_WAITCHI)
		WaitForSingleObject( pi.hProcess, INFINITE );
	
	return pi.dwProcessId;
}
