/* 
 * Copyright (C) 2003 by Dirk Meyer
 * 
 * This file is part of xine, a unix video player.
 * 
 * xine 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.
 * 
 * xine 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
 *
 * The code is taken from xine-ui/src/xitk/post.c at changed to work with fbxine
 * Modified for VDR xineliboutput plugin by Petri Hintukainen, 2006
 *   - runtime re-configuration (load/unload, enable/disable)
 *   - support for multiple streams 
 *   - support for mosaico post plugin (picture-in-picture)
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "post.h"

#if 1
# include <sys/types.h>
# include <linux/unistd.h>
# include <errno.h>

# define LOG_MODULENAME "[xine-post] "
# include "../logdefs.h"

  /* from xine_frontend.c or vdr tools.c: */
  extern int SysLogLevel; /* errors and info, no debug */
  /* from xine_frontend.c: */
  extern int LogToSysLog; /* log to syslog instead of console */

# if !defined(XINELIBOUTPUT_DEBUG_STDOUT) && \
     !defined(XINELIBOUTPUT_DEBUG_STDERR)

#   undef x_syslog

    pid_t gettid(void); /*_syscall0(pid_t, gettid);*/

    static void x_syslog(int level, const char *fmt, ...)
    {
      va_list argp;
      char buf[512];
      va_start(argp, fmt);
      vsnprintf(buf, 512, fmt, argp);
      if(!LogToSysLog) {
	printf(LOG_MODULENAME "%s\n", buf);
      } else {
	syslog(level, "[%d] " LOG_MODULENAME "%s", gettid(), buf);
      }
      va_end(argp);
    }
# endif

#else
# define LOGERR(x...)
# define LOGMSG(x...)
# define LOGDBG(x...)
# define TRACELINE (void)
#endif


#define fe_t post_plugins_t

typedef struct {
  xine_post_t                 *post;
  xine_post_api_t             *api;
  xine_post_api_descr_t       *descr;
  xine_post_api_parameter_t   *param;
  char                        *param_data;

  int                          x;
  int                          y;

  int                          readonly;

  char                       **properties_names;
} post_object_t;


static int __pplugin_retrieve_parameters(post_object_t *pobj) 
{
  xine_post_in_t             *input_api;
  
  if((input_api = (xine_post_in_t *) xine_post_input(pobj->post, 
						     "parameters"))) {
    xine_post_api_t            *post_api;
    xine_post_api_descr_t      *api_descr;
    xine_post_api_parameter_t  *parm;
    int                         pnum = 0;
    
    post_api = (xine_post_api_t *) input_api->data;
    
    api_descr = post_api->get_param_descr();
    
    parm = api_descr->parameter;
    pobj->param_data = malloc(api_descr->struct_size);
    
    while(parm->type != POST_PARAM_TYPE_LAST) {
      
      post_api->get_parameters(pobj->post, pobj->param_data);
      
      if(!pnum)
	pobj->properties_names = (char **) xine_xmalloc(sizeof(char *) * 2);
      else
	pobj->properties_names = (char **) 
	  realloc(pobj->properties_names, sizeof(char *) * (pnum + 2));
      
      pobj->properties_names[pnum]     = strdup(parm->name);
      pobj->properties_names[pnum + 1] = NULL;
      pnum++;
      parm++;
    }
    
    pobj->api      = post_api;
    pobj->descr    = api_descr;
    pobj->param    = api_descr->parameter;
    
    return 1;
  }

  return 0;
}

static void _pplugin_update_parameter(post_object_t *pobj) 
{
  pobj->api->set_parameters(pobj->post, pobj->param_data);
  pobj->api->get_parameters(pobj->post, pobj->param_data);
}

static void __pplugin_update_parameters(xine_post_t *post, char *args)
{
  post_object_t  pobj;
  char *p;

  memset(&pobj, 0, sizeof(post_object_t));
  pobj.post = post;
	  
  if(__pplugin_retrieve_parameters(&pobj)) {
    int   i;
	    
    if(pobj.properties_names && args && *args) {
      char *param;
	      
      while((param = xine_strsep(&args, ",")) != NULL) {
		
	p = param;
		
	while((*p != '\0') && (*p != '='))
	  p++;
	
	if(p && strlen(p)) {
	  int param_num = 0;
		  
	  *p++ = '\0';
		  
	  while(pobj.properties_names[param_num]
		&& strcasecmp(pobj.properties_names[param_num], param))
	    param_num++;
		  
	  if(pobj.properties_names[param_num]) {
	    
	    pobj.param    = pobj.descr->parameter;
	    pobj.param    += param_num;
	    pobj.readonly = pobj.param->readonly;
		    
	    switch(pobj.param->type) {
	    case POST_PARAM_TYPE_INT:
	      if(!pobj.readonly) {
		if(pobj.param->enum_values) {
		  char **values = pobj.param->enum_values;
		  int    i = 0;
	  
		  while(values[i]) {
		    if(!strcasecmp(values[i], p)) {
		      *(int *)(pobj.param_data + pobj.param->offset) = i;
		      break;
		    }
		    i++;
		  }

		  if( !values[i] ) 
		    *(int *)(pobj.param_data + pobj.param->offset) = (int) strtol(p, &p, 10);
		} else {
		  *(int *)(pobj.param_data + pobj.param->offset) = (int) strtol(p, &p, 10);
		}
		_pplugin_update_parameter(&pobj);
	      }
	      break;
		      
	    case POST_PARAM_TYPE_DOUBLE:
	      if(!pobj.readonly) {
		*(double *)(pobj.param_data + pobj.param->offset) = strtod(p, &p);
		_pplugin_update_parameter(&pobj);
	      }
	      break;
		      
	    case POST_PARAM_TYPE_CHAR:
	    case POST_PARAM_TYPE_STRING:
	      if(!pobj.readonly) {
		if(pobj.param->type == POST_PARAM_TYPE_CHAR) {
		  int maxlen = pobj.param->size / sizeof(char);
			  
		  snprintf((char *)(pobj.param_data + pobj.param->offset), maxlen, "%s", p);
		  _pplugin_update_parameter(&pobj);
		}
		else
		  fprintf(stderr, "parameter type POST_PARAM_TYPE_STRING not supported yet.\n");
	      }
	      break;
		      
	    case POST_PARAM_TYPE_STRINGLIST: /* unsupported */
	      if(!pobj.readonly)
		fprintf(stderr, "parameter type POST_PARAM_TYPE_STRINGLIST not supported yet.\n");
	      break;
		      
	    case POST_PARAM_TYPE_BOOL:
	      if(!pobj.readonly) {
		*(int *)(pobj.param_data + pobj.param->offset) = ((int) strtol(p, &p, 10)) ? 1 : 0;
		_pplugin_update_parameter(&pobj);
	      }
	      break;
	    }
	  }
	}
      } 
      
      i = 0;
      
      while(pobj.properties_names[i]) {
	free(pobj.properties_names[i]);
	i++;
      }
      
      free(pobj.properties_names);
    }
    
    free(pobj.param_data);
  }
}

/* -post <name>:option1=value1,option2=value2... */
static post_element_t **pplugin_parse_and_load(fe_t *fe, 
					       int plugin_type,
					       const char *pchain, 
					       int *post_elements_num) 
{
  post_element_t **post_elements = NULL;
  char            *post_chain;

  *post_elements_num = 0;

  if(pchain && strlen(pchain)) {
    char *p;
    
    xine_strdupa(post_chain, pchain);
    
    while((p = xine_strsep(&post_chain, ";"))) {
      
      if(p && strlen(p)) {
	char          *plugin, *args = NULL;
	xine_post_t   *post;
	
	while(*p == ' ')
	  p++;
	
	plugin = strdup(p);
	
	if((p = strchr(plugin, ':')))
	  *p++ = '\0';
	
	if(p && (strlen(p) > 1))
	  args = p;
#if 0
	post = xine_post_init(fe->xine, plugin, 0, 
			      &fe->audio_port, &fe->video_port);
#else
	if(plugin_type == XINE_POST_TYPE_VIDEO_COMPOSE) {
	  post = xine_post_init(fe->xine, plugin, 2, 
				&fe->audio_port, &fe->video_port);
	} else
	  post = xine_post_init(fe->xine, plugin, 0, 
				&fe->audio_port, &fe->video_port);
#endif

        if (post && plugin_type) {
          if (post->type != plugin_type) {
            xine_post_dispose(fe->xine, post);
            post = NULL;
          }
        }
          
	if(post) {
	 
	  if(!(*post_elements_num))
	    post_elements = (post_element_t **) xine_xmalloc(sizeof(post_element_t *) * 2);
	  else
	    post_elements = (post_element_t **) 
	      realloc(post_elements, sizeof(post_element_t *) * ((*post_elements_num) + 2));
	  
	  post_elements[(*post_elements_num)] = (post_element_t *) 
	    xine_xmalloc(sizeof(post_element_t));
	  post_elements[(*post_elements_num)]->post = post;
	  post_elements[(*post_elements_num)]->name = strdup(plugin);
#if 1
	  post_elements[(*post_elements_num)]->args = args ? strdup(args) : NULL;
	  post_elements[(*post_elements_num)]->enable = 0;
#endif
	  (*post_elements_num)++;
	  post_elements[(*post_elements_num)] = NULL;

	  __pplugin_update_parameters(post, args);
	}
	
	free(plugin);
      }
    }
  }

  return post_elements;
}

void pplugin_parse_and_store_post(fe_t *fe, int plugin_type, 
				  const char *post_chain) 
{
  post_element_t ***_post_elements;
  int *_post_elements_num;
  post_element_t **posts = NULL;
  int              num;

  switch(plugin_type) {
    case XINE_POST_TYPE_VIDEO_FILTER:
      _post_elements = &fe->post_video_elements;
      _post_elements_num = &fe->post_video_elements_num;
      break;
    case XINE_POST_TYPE_VIDEO_COMPOSE:
      _post_elements = &fe->post_pip_elements;
      _post_elements_num = &fe->post_pip_elements_num;
      break;
    case XINE_POST_TYPE_AUDIO_VISUALIZATION:
      _post_elements = &fe->post_vis_elements;
      _post_elements_num = &fe->post_vis_elements_num;
      break;
    default:
      _post_elements = &fe->post_audio_elements;
      _post_elements_num = &fe->post_audio_elements_num;
      break;
  }

  if((posts = pplugin_parse_and_load(fe, plugin_type, post_chain, &num))) {
    if(*_post_elements_num) {
      int i;
      int ptot = *_post_elements_num + num;
      
      *_post_elements = (post_element_t **) realloc(*_post_elements, 
						    sizeof(post_element_t *) * (ptot + 1));
      for(i = *_post_elements_num; i <  ptot; i++)
	(*_post_elements)[i] = posts[i - *_post_elements_num];

      (*_post_elements)[i] = NULL;
      (*_post_elements_num) += num;

    }
    else {
      *_post_elements     = posts;
      *_post_elements_num = num;
    }
  }
}


void vpplugin_parse_and_store_post(fe_t *fe, const char *post_chain) 
{
  pplugin_parse_and_store_post(fe, XINE_POST_TYPE_VIDEO_FILTER, post_chain);
  pplugin_parse_and_store_post(fe, XINE_POST_TYPE_VIDEO_COMPOSE, post_chain);
}


void applugin_parse_and_store_post(fe_t *fe, const char *post_chain) 
{
  pplugin_parse_and_store_post(fe, XINE_POST_TYPE_AUDIO_FILTER, post_chain);
  pplugin_parse_and_store_post(fe, XINE_POST_TYPE_AUDIO_VISUALIZATION, post_chain);
}


static void _vpplugin_unwire(fe_t *fe) 
{
  xine_post_out_t  *vo_source;
  
  vo_source = xine_get_video_source(fe->stream);

  (void) xine_post_wire_video_port(vo_source, fe->video_port);
}


static void _applugin_unwire(fe_t *fe) 
{
  xine_post_out_t  *ao_source;
  
  ao_source = xine_get_audio_source(fe->stream);

  (void) xine_post_wire_audio_port(ao_source, fe->audio_port);
}


static void _vpplugin_rewire_from_post_elements(fe_t *fe, post_element_t **post_elements, int post_elements_num) 
{  
  if(post_elements_num) {
    xine_post_out_t   *vo_source;
    int                i = 0;
    
    for(i = (post_elements_num - 1); i >= 0; i--) {
      const char *const *outs = xine_post_list_outputs(post_elements[i]->post);
      const xine_post_out_t *vo_out = xine_post_output(post_elements[i]->post, (char *) *outs);
      if(i == (post_elements_num - 1)) {
	xine_post_wire_video_port((xine_post_out_t *) vo_out, fe->video_port);
      }
      else {
	const xine_post_in_t *vo_in;
	int                   err;
	
	/* look for standard input names */
	vo_in = xine_post_input(post_elements[i + 1]->post, "video");
	if( !vo_in )
	  vo_in = xine_post_input(post_elements[i + 1]->post, "video in");
	err = xine_post_wire((xine_post_out_t *) vo_out, 
			     (xine_post_in_t *) vo_in);	
      }
    }

    if(fe->post_pip_enable && 
       !strcmp(post_elements[0]->name, "mosaico")) {
      vo_source = xine_get_video_source(fe->pip_stream);
      xine_post_wire_video_port(vo_source, 
				post_elements[0]->post->video_input[1]);
    }
    
    if(fe->slave_stream)
      vo_source = xine_get_video_source(fe->slave_stream);
    else
      vo_source = xine_get_video_source(fe->stream);
    xine_post_wire_video_port(vo_source, 
			      post_elements[0]->post->video_input[0]);
  }
}


static void _applugin_rewire_from_post_elements(fe_t *fe, post_element_t **post_elements, int post_elements_num) 
{
  if(post_elements_num) {
    xine_post_out_t   *ao_source;
    int                i = 0;
    
    for(i = (post_elements_num - 1); i >= 0; i--) {
      const char *const *outs = xine_post_list_outputs(post_elements[i]->post);
      const xine_post_out_t *ao_out = xine_post_output(post_elements[i]->post, (char *) *outs);

      if(i == (post_elements_num - 1)) {
	xine_post_wire_audio_port((xine_post_out_t *) ao_out, fe->audio_port);
      }
      else {
	const xine_post_in_t *ao_in;
	int                   err;
	
	/* look for standard input names */
	ao_in = xine_post_input(post_elements[i + 1]->post, "audio");
	if( !ao_in )
	  ao_in = xine_post_input(post_elements[i + 1]->post, "audio in");
	
	err = xine_post_wire((xine_post_out_t *) ao_out, (xine_post_in_t *) ao_in);	
      }
    }

    if(fe->slave_stream)
      ao_source = xine_get_audio_source(fe->slave_stream);
    else
      ao_source = xine_get_audio_source(fe->stream);
    xine_post_wire_audio_port(ao_source, post_elements[0]->post->audio_input[0]);
  }
}

static post_element_t **_pplugin_join_deinterlace_and_post_elements(fe_t *fe, int *post_elements_num) 
{
  post_element_t **post_elements;
  int i = 0, j = 0, n = 0;

  *post_elements_num = 0;
  if( fe->post_video_enable )
    *post_elements_num += fe->post_video_elements_num;

  if( fe->post_pip_enable )
    *post_elements_num += fe->post_pip_elements_num;

  if( *post_elements_num == 0 )
    return NULL;

  post_elements = (post_element_t **) 
    xine_xmalloc(sizeof(post_element_t *) * (*post_elements_num));

  if(fe->post_pip_enable)
    for( i = 0; i < fe->post_pip_elements_num; i++ ) {
      if(fe->post_pip_elements[i]->enable)
	post_elements[i+j-n] = fe->post_pip_elements[i];
      else
	n++;
    }

  if(fe->post_video_enable)
    for( j = 0; j < fe->post_video_elements_num; j++ ) {
      if(fe->post_video_elements[j]->enable)
	post_elements[i+j-n] = fe->post_video_elements[j];
      else
	n++;
    }
  
  *post_elements_num -= n;
  if( *post_elements_num == 0 ) {
    free(post_elements);
    return NULL;
  }
  
  return post_elements;
}

static post_element_t **_pplugin_join_visualization_and_post_elements(fe_t *fe, int *post_elements_num) 
{
  post_element_t **post_elements;
  int i = 0, j = 0, n = 0;

  *post_elements_num = 0;
  if( fe->post_audio_enable )
    *post_elements_num += fe->post_audio_elements_num;

  if( fe->post_vis_enable )
    *post_elements_num += fe->post_vis_elements_num;

  if( *post_elements_num == 0 )
    return NULL;

  post_elements = (post_element_t **) 
    xine_xmalloc(sizeof(post_element_t *) * (*post_elements_num));

  if(fe->post_audio_enable)
    for( j = 0; j < fe->post_audio_elements_num; j++ ) {
      if(fe->post_audio_elements[j]->enable)
	post_elements[i+j-n] = fe->post_audio_elements[j];
      else
	n++;
    }

  if(fe->post_vis_enable)
    for( i = 0; i < fe->post_vis_elements_num; i++ ) { 
      if(fe->post_vis_elements[i]->enable)
	post_elements[i+j-n] = fe->post_vis_elements[i];      
      else
	n++;
    }

  *post_elements_num -= n;
  if( *post_elements_num == 0 ) {
    free(post_elements);
    return NULL;
  }

  return post_elements;
}

static void _vpplugin_rewire(fe_t *fe) 
{
  static post_element_t **post_elements;
  int post_elements_num;

  post_elements = _pplugin_join_deinterlace_and_post_elements(fe, &post_elements_num);

  if( post_elements ) {
    _vpplugin_rewire_from_post_elements(fe, post_elements, post_elements_num);

    free(post_elements);
  }
}

static void _applugin_rewire(fe_t *fe) 
{
  static post_element_t **post_elements;
  int post_elements_num;

  post_elements = _pplugin_join_visualization_and_post_elements(fe, &post_elements_num);

  if( post_elements ) {
    _applugin_rewire_from_post_elements(fe, post_elements, post_elements_num);

    free(post_elements);
  }
}

void vpplugin_rewire_posts(fe_t *fe) 
{
  /*TRACELINE;*/
  _vpplugin_unwire(fe);
  _vpplugin_rewire(fe);
}

void applugin_rewire_posts(fe_t *fe) 
{
  /*TRACELINE;*/
  _applugin_unwire(fe);
  _applugin_rewire(fe);
}

static int _pplugin_enable_post(post_plugins_t *fe, const char *name,
				const char *args,
				post_element_t **post_elements,
				int post_elements_num,
				int *found)
{
  int i, result = 0;

  for(i=0; i<post_elements_num; i++)
    if(post_elements[i])
      if(!strcmp(post_elements[i]->name, name)) {
	if(post_elements[i]->enable == 0) {
	  post_elements[i]->enable = 1;
	  result = 1;
	}

	*found = 1;

	if(args && *args) {
	  char *tmp = strdup(args);
	  __pplugin_update_parameters(post_elements[i]->post, tmp);
	  free(tmp);
	  if(post_elements[i]->args)
	    free(post_elements[i]->args);
	  post_elements[i]->args = strdup(args);
	}
      }

  return result;
}

static int _vpplugin_enable_post(post_plugins_t *fe, const char *name, 
				 const char *args, int *found)
{
  return
    _pplugin_enable_post(fe, name, args, fe->post_video_elements, 
			 fe->post_video_elements_num, found) +
    _pplugin_enable_post(fe, name, args, fe->post_pip_elements, 
			 fe->post_pip_elements_num, found);
}

static int _applugin_enable_post(post_plugins_t *fe, const char *name,
				 const char *args, int *found)
{
  return
    _pplugin_enable_post(fe, name, args, fe->post_audio_elements, 
			 fe->post_audio_elements_num, found) +
    _pplugin_enable_post(fe, name, args, fe->post_vis_elements, 
			 fe->post_vis_elements_num, found);
}

static char * _pp_name(const char *initstr)
{
  char *name = strdup(initstr), *pt;

  if(NULL != (pt = strchr(name, ':')))
    *pt = 0;

  return name;
}

static const char * _pp_args(const char *initstr)
{
  char *pt = strchr(initstr, ':');
  if(pt && *(pt+1))
    return pt+1;
  return NULL;
}

int vpplugin_enable_post(post_plugins_t *fe, const char *initstr,
			 int *found)
{
  char *name = _pp_name(initstr);
  const char *args = _pp_args(initstr);

  int result = _vpplugin_enable_post(fe, name, args, found);

  LOGDBG("  * enable post %s --> %s, %s", name,
	 *found ? "found"   : "not found",
	 result ? "enabled" : "no action");    

  if(!*found) {
    LOGDBG("  * loading post %s", initstr);
    vpplugin_parse_and_store_post(fe, initstr);
    result = _vpplugin_enable_post(fe, name, NULL, found);
    LOGDBG("  * enable post %s --> %s, %s", name,
	   *found ? "found"   : "not found",
	   result ? "enabled" : "no action");    
  } 
  
  if(result)
    _vpplugin_unwire(fe);
  
  free(name);
  return result;
}

int applugin_enable_post(post_plugins_t *fe, const char *initstr,
			 int *found)
{
  const char *args = _pp_args(initstr);
  char *name = _pp_name(initstr);

  int result = _vpplugin_enable_post(fe, name, args, found); 

  LOGDBG("  * enable post %s --> %s, %s", name,
	 *found ? "found"   : "not found",
	 result ? "enabled" : "no action");    

  if(!*found) {
    LOGDBG("  * loading post %s", initstr);
    applugin_parse_and_store_post(fe, initstr);
    result = _applugin_enable_post(fe, name, NULL, found);
    LOGDBG("  * enable post %s --> %s, %s", name,
	   *found ? "found"   : "not found",
	   result ? "enabled" : "no action");    
  }
  
  if(result) 
    _applugin_unwire(fe);

  free(name);
  return result;
}

static int _pplugin_disable_post(post_plugins_t *fe, const char *name,
				 post_element_t **post_elements,
				 int post_elements_num)
{
  int i, result = 0;
  /*TRACELINE;*/
  if(post_elements)
    for(i = 0; i < post_elements_num; i++)
      if(post_elements[i])
	if(!name || !strcmp(post_elements[i]->name, name)) 
	  if(post_elements[i]->enable == 1) {
	    post_elements[i]->enable = 0;
	    result = 1;
	  }
  return result;
}

int vpplugin_disable_post(post_plugins_t *fe, const char *name)
{
  /*TRACELINE;*/
  if(_pplugin_disable_post(fe, name, fe->post_video_elements,
			   fe->post_video_elements_num) ||
     _pplugin_disable_post(fe, name, fe->post_pip_elements,
			   fe->post_pip_elements_num)) {
    _vpplugin_unwire(fe);
    return 1;
  }
  return 0;
}

int applugin_disable_post(post_plugins_t *fe, const char *name)
{
  /*TRACELINE;*/
  if(_pplugin_disable_post(fe, name, fe->post_audio_elements,
			   fe->post_audio_elements_num) ||
     _pplugin_disable_post(fe, name, fe->post_vis_elements,
			   fe->post_vis_elements_num)) {
    _applugin_unwire(fe);
    return 1;
  }
  return 0;
}

static int _pplugin_unload_post(post_plugins_t *fe, const char *name,
				post_element_t ***post_elements,
				int *post_elements_num)
{
  /* does not unwrire plugins ! */
  int i, j, result = 0;
  /*TRACELINE;*/

  if(!*post_elements || !*post_elements_num)
    return 0;

  for(i=0; i < *post_elements_num; i++)
    if((*post_elements)[i])
      if(!name || !strcmp((*post_elements)[i]->name, name)) {

	xine_post_dispose(fe->xine, (*post_elements)[i]->post);

	free((*post_elements)[i]->name);

	if((*post_elements)[i]->args)
	  free((*post_elements)[i]->args);

	free((*post_elements)[i]);

	for(j=i; j < *post_elements_num - 1; j++) 
	  (*post_elements)[j] = (*post_elements)[j+1];

	(*post_elements_num) --;
	(*post_elements)[(*post_elements_num)] = 0;

	result = 1;
      }

  if(*post_elements_num <= 0) {
    if(*post_elements)
      free(*post_elements);
    *post_elements = NULL;
  }

  return result;
}

int vpplugin_unload_post(post_plugins_t *fe, const char *name)
{
  int result = vpplugin_disable_post(fe, name);

  /* unload already disabled plugins too (result=0) */
  _pplugin_unload_post(fe, name, &fe->post_video_elements, 
		       &fe->post_video_elements_num);
  _pplugin_unload_post(fe, name, &fe->post_pip_elements, 
		       &fe->post_pip_elements_num);
  
  /* result indicates only unwiring condition, not unload result */ 
  return result;
}

int applugin_unload_post(post_plugins_t *fe, const char *name)
{
  int result = applugin_disable_post(fe, name);

  /* unload already disabled plugins too (result=0) */
  _pplugin_unload_post(fe, name, &fe->post_audio_elements, 
		       &fe->post_audio_elements_num);
  _pplugin_unload_post(fe, name, &fe->post_vis_elements, 
		       &fe->post_vis_elements_num);

  /* result indicates only unwiring condition, not unload result */ 
  return result;
}


/* end of post.c */
  
