/*
 *  tcpvmexportd.c 
 *
 *  Copyright (C) Marzio Malanchini - July 2003
 *
 *  This file is part of transcode, a video stream processing tool
 *
 *  transcode 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.
 *
 *  transcode 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 GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
#include <unistd.h>
#include <stdlib.h>
#include <string.h>


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "pvm_interface.h"
#include "pvm_parser.h"

#include "transcode.h"
#include "aclib/ac.h"
#include "export_pvm_slave.h"
#include "external_codec.h"

#define EXE "tcpvmexportd"

#define MAX_BUF 	1024

#define MASTER_MODE	0x00000001
#define SLAVE_MODE	0x00000010
#define MERGER_MODE	0x00000100
#define CHECK_MODE	0x00001000
#define MAST_MERGE_MODE	MASTER_MODE|MERGER_MODE
#define MAST_CHECK_MODE	MASTER_MODE|CHECK_MODE
#define SLAVE_MERGE_MODE	SLAVE_MODE|MERGER_MODE


void *f_handle=NULL;	/*handle for dlopen/dlclose*/
void *f_ext_handle=NULL;	/*handle for dlopen/dlclose of the external module*/
int (*tc_export)(int,void *,void *);
char *p_param1=NULL,*p_param2=NULL,*p_param3=NULL; /*codec input parameter*/
int tc_accel=-1;
int s_elab_type=TC_VIDEO,s_list_only=0;
char *p_out_file_name=NULL;
char *p_request_func=NULL;
char *p_hostname=NULL;
int verbose=TC_QUIET;
char *p_merge_cmd=NULL;
unsigned int tc_avi_limit=AVI_FILE_LIMIT;	/*NEED TO CHECK*/
char *tc_config_dir=NULL;	/*NEED TO CHECK*/
int s_divxmultipass=0,s_internal_multipass=0;

void version()
{
  /* id string */
  fprintf(stderr, "%s (%s v%s) (C) 2001-2003 Thomas Östreich\n",EXE,PACKAGE,VERSION);
}


void usage(int status)
{
	version(EXE);
	fprintf(stderr,"\nUsage: %s -s|-m [options]\n", EXE);
	fprintf(stderr,"\t -s             start %s in slave mode [default]\n",EXE);
	fprintf(stderr,"\t -m             start %s in master mode [off]\n",EXE);
	fprintf(stderr,"\t -j             start %s in merge mode [off]\n",EXE);
	fprintf(stderr,"\t -L             create only the merge list [off]\n");
	fprintf(stderr,"\t -p number      Multipass. [0]\n");
	fprintf(stderr,"\t -C             Check the config or merge file [off]\n");
	fprintf(stderr,"\t -M             Enable internal multipass [off]\n");
	fprintf(stderr,"\t -c name        name of slave function req in slave mode [none]\n");
	fprintf(stderr,"\t -x parameters  multiplex parameters [none]\n");
	fprintf(stderr,"\t -t type        elab video or audio frame (video|audio|system|multisystem) [video]\n");
	fprintf(stderr,"\t -f name        out file name [/tmp/my_file_name]\n");
	fprintf(stderr,"\t -1 param       first parameter to pass to the slave function [null]\n");
	fprintf(stderr,"\t -2 param       second parameter to pass to the slave function [null]\n");
	fprintf(stderr,"\t -3 param       third parameter to pass to the slave function [null]\n");
	fprintf(stderr,"\t -d level       verbose level 0|1|2 [0]\n");
	fprintf(stderr,"\t -h             print this help\n");
	fprintf(stderr,"\t -v             print version\n");
	exit(status);
}


int f_init_func(char *p_option,char *p_mod)
{
#ifdef SYS_BSD
	const
#endif
	char *p_error;
	char *p_modpath=MOD_PATH;
	char s_module[MAX_BUF];

	if(!strcasecmp(p_option,"open-external"))
	{
		if (p_mod!=NULL)
		{
			memset((char *)&s_module,'\0',MAX_BUF);
			snprintf(s_module, sizeof(s_module), "%s/export_%s.so", p_modpath,p_mod);
			f_ext_handle=dlopen(s_module, RTLD_GLOBAL|RTLD_LAZY);
			if (!f_ext_handle) 
			{
				fputs (dlerror(), stderr);
				dlclose(f_handle);
				return(1);
			}
			tc_export = dlsym(f_ext_handle, "tc_export");   
			if ((p_error = dlerror()) != NULL)  
			{
				fputs(p_error, stderr);
				return(1);
			}
		}
	}
	else if(!strcasecmp(p_option,"close-external"))
	{
		if (f_ext_handle!=NULL)
			dlclose(f_ext_handle);
		f_ext_handle=NULL;
		tc_export=NULL;
	}
	else if(!strcasecmp(p_option,"status-external"))
	{
		if(f_ext_handle!=NULL)
			return(1);
	}
	else if(!strcasecmp(p_option,"open"))
	{
		if (p_mod!=NULL)
		{
			tc_accel = ac_mmflag();			/*det the properties of cpu*/
			memset((char *)&s_module,'\0',MAX_BUF);
			snprintf(s_module, sizeof(s_module), "%s/export_%s.so", p_modpath,p_mod);
			f_ext_handle=dlopen(s_module, RTLD_GLOBAL|RTLD_LAZY);
			if (!f_ext_handle) 
			{
				fputs (dlerror(), stderr);
				dlclose(f_handle);
				return(1);
			}
			tc_export = dlsym(f_ext_handle, "tc_export");   
			if ((p_error = dlerror()) != NULL)  
			{
				fputs(p_error, stderr);
				return(1);
			}
		}
		if ((f_handle=f_init_pvm_func("open",NULL))==NULL)
			return(1);
	}
	else if(!strcasecmp(p_option,"close"))
	{
		if (f_ext_handle!=NULL)
			dlclose(f_ext_handle);
		(void *)f_init_pvm_func("close",f_handle);
		f_ext_handle=NULL;
		f_handle=NULL;
	}
	else
	{
		fprintf(stderr,"(%s) invalid command \"%s\"\n",__FILE__,p_option);
		return(1);
	}
	return(0);
}



int main(int argc,char **argv)
{
	char s_cmd;
	char *p_argv[]={"-s",(char*)0,(char*)0,(char*)0,(char*)0,(char*)0,(char*)0,(char*)0,(char*)0,(char*)0,(char*)0};
	int s_slave=0,s_cont=1,s_file_dest;
	char *p_tmp_file="/tmp/my_file_name";
	pvm_config_env *p_pvm_conf;
	pvm_config_filelist *p_my_filelist=NULL;
	char s_hostname[MAX_BUF];

	p_out_file_name=p_tmp_file;

	if((gethostname((char *)&s_hostname,MAX_BUF))!=0)
	{
		memset((char *)&s_hostname,'\0',MAX_BUF);
		snprintf((char *)&s_hostname,MAX_BUF,"localhost-%d\n",getpid());
	}
	p_hostname=(char *)s_hostname;

	while ((s_cmd = getopt(argc, argv, "mMCLsjx:c:1:2:3:t:f:d:p:vh")) != -1) 
	{
		switch (s_cmd) 
		{
			case 'c': 
		  		if(optarg[0]=='-') 
			  		usage(EXIT_FAILURE);
		  		p_argv[s_cont++]="-c";
		  		p_argv[s_cont++]=p_request_func=optarg;
		  	break;
			case 'M':
				s_internal_multipass=1;
		  	break;
			case 'p':
				s_divxmultipass=((atoi(optarg)<0)||(atoi(optarg)>3))?0:atoi(optarg);
		  	break;
			case 'd':
				verbose=((atoi(optarg)<0)||(atoi(optarg)>2))?0:atoi(optarg);
		  	break;
			case 'C':
		  		s_slave|=CHECK_MODE;		
		  	break;
			case 'L':
		  		s_list_only=1;		
		  	break;
			case 'j':
		  		s_slave|=MERGER_MODE;		
		  	break;
			case 's':
		  		s_slave|=SLAVE_MODE;		/*default*/
		  	break;
			case 'm':
		  		s_slave|=MASTER_MODE;
		  	break;
			case 'f':
		  		p_argv[s_cont++]="-f";
		  		p_argv[s_cont++]=p_out_file_name=optarg;
		  	break;
			case 't':
		  		p_argv[s_cont++]="-t";
		  		p_argv[s_cont++]=optarg;
				if(!strcasecmp(optarg,"video"))
					s_elab_type=TC_VIDEO;
				else if(!strcasecmp(optarg,"audio"))
					s_elab_type=TC_AUDIO;
				else if(!strcasecmp(optarg,"system"))
					s_elab_type=TC_VIDEO_AUDIO;
				else if(!strcasecmp(optarg,"multisystem"))
					s_elab_type=TC_MULTI_VIDEO_AUDIO;
				else
					usage(EXIT_FAILURE);
		  	break;
			case 'x':
		  		p_merge_cmd=optarg;	/*multiplex command*/
		  	break;
			case '1':
		  		p_param1=strtok(optarg,"\"");	/*First parameter*/
		  	break;
			case '2':
		  		p_param2=strtok(optarg,"\"");	/*Second parameter*/
		  	break;
			case '3':
		  		p_param3=strtok(optarg,"\"");	/*Third parameter*/
		  	break;
			case 'v': 
		  		version(EXE);
		  		exit(0);
		  	break;
			case 'h':
		  		usage(EXIT_SUCCESS);
			default:
				usage(EXIT_FAILURE);
		}
	}
	
	if(optind < argc) 
	{
		if(strcmp(argv[optind],"-")!=0) 
			usage(EXIT_FAILURE);
        }
	if(argc==1) 
		usage(EXIT_FAILURE);
	if (((s_slave==SLAVE_MODE)||(s_slave==(SLAVE_MERGE_MODE)))&&(p_request_func==NULL))
		usage(EXIT_FAILURE);
	if ((s_slave==(MAST_MERGE_MODE)||s_slave==(MAST_CHECK_MODE))&&(p_out_file_name==NULL))
		usage(EXIT_FAILURE);
	switch(s_slave)
	{
		case MAST_CHECK_MODE:
		case MAST_MERGE_MODE:
			if ((p_pvm_conf=f_pvm_parser(p_out_file_name,"open"))==NULL)
			{
				fprintf(stderr,"[%s] error checking %s\n",EXE,p_out_file_name);
				exit(1);
			}
			if (s_slave == (MAST_MERGE_MODE))
			{
				if (p_pvm_conf->s_sys_list.p_destination!=NULL)	/*syslist?*/
				{
					if(f_system_merge(p_pvm_conf))	/*if 1 then error*/
						exit(1);
				}
				else if (p_pvm_conf->p_add_list->p_destination!=NULL)
				{
					if ((s_file_dest=creat(p_pvm_conf->p_add_list->p_destination,S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH))==-1)
					{
						fprintf(stderr,"[%s] can't create %s output file.\n",EXE,p_pvm_conf->p_add_list->p_destination);
						p_pvm_conf=f_pvm_parser(p_out_file_name,"close");
						exit(1);
					}
					for (p_my_filelist=p_pvm_conf->p_add_list;p_my_filelist!=NULL;p_my_filelist=p_my_filelist->p_next)
					{
						fprintf(stderr,"[%s] merge into %s and remove file %s\n",EXE,p_pvm_conf->p_add_list->p_destination,p_my_filelist->p_filename);
						if(f_copy_remove_func("open",p_my_filelist->p_filename,s_file_dest))	/*if 1 then error*/
							exit(1);
					}
					close(s_file_dest);
					f_copy_remove_func("close",NULL,0);
				}
				else
				{
					fprintf(stderr,"[%s] no destination file name.\n",EXE);
					exit(1);
				}
				for (p_my_filelist=p_pvm_conf->p_rem_list;p_my_filelist!=NULL;p_my_filelist=p_my_filelist->p_next)
				{
					fprintf(stderr,"[%s] remove file %s\n",EXE,p_my_filelist->p_filename);
					remove(p_my_filelist->p_filename);
				}
			}
			p_pvm_conf=f_pvm_parser(p_out_file_name,"close");
		break;
		case SLAVE_MODE:
		case SLAVE_MERGE_MODE:
			if((!strcasecmp(p_request_func,"mpeg2enc-mp2enc"))||(!strcasecmp(p_request_func,"mpeg-mpeg"))||(!strcasecmp(p_request_func,"avi-avi")))
			{
				if (f_init_func("open",NULL))		/*init the pvm interface*/
					exit(1);
				f_pvm_skeduler(f_export_func);
			}
			else
			{
				if (f_init_func("open",p_request_func))		
					exit(1);
				f_pvm_skeduler(f_export_func);
			}
		break;
		default:
		break;
	}
	f_init_func("close",NULL);
	return(0);
}
