/*
 * tcmodule-plugin.h -- transcode module system, take two: plugin parts.
 * (C) 2005-2010 - Francesco Romani <fromani -at- gmail -dot- com>
 *
 * 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 of the License, 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#ifndef TCMODULE_PLUGIN_H
#define TCMODULE_PLUGIN_H

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

#include <stdint.h>

#include "tcmodule-info.h"
#include "tcmodule-data.h"


#define TC_MODULE_SELF_CHECK(self, WHERE) do { \
    if ((self) == NULL) { \
        tc_log_error(MOD_NAME, WHERE ": " # self " is NULL"); \
        return TC_ERROR; /* catch all for filter/encoders/decoders/(de)muxers */ \
    } \
} while (0)


#define TC_HAS_FEATURE(flags, feat) \
    ((flags & (TC_MODULE_FEATURE_ ## feat)) ?1 :0)

#ifdef HAVE_GCC_ATTRIBUTES
__attribute__((unused))
#endif
static int tc_module_av_check(uint32_t flags)
{
    int i = 0;

    i += TC_HAS_FEATURE(flags, AUDIO);
    i += TC_HAS_FEATURE(flags, VIDEO);
    i += TC_HAS_FEATURE(flags, EXTRA);

    return i;
}

#ifdef HAVE_GCC_ATTRIBUTES
__attribute__((unused))
#endif
static int tc_module_cap_check(uint32_t flags)
{
    int i = 0;

    i += TC_HAS_FEATURE(flags, DECODE);
    i += TC_HAS_FEATURE(flags, FILTER);
    i += TC_HAS_FEATURE(flags, ENCODE);
    i += TC_HAS_FEATURE(flags, MULTIPLEX);
    i += TC_HAS_FEATURE(flags, DEMULTIPLEX);

    return i;
}

#undef TC_HAS_FEATURE


#define TC_MODULE_INIT_CHECK(self, FEATURES, feat) do { \
    int j = tc_module_cap_check((feat)); \
    \
    if ((!((FEATURES) & TC_MODULE_FEATURE_MULTIPLEX) \
      && !((FEATURES) & TC_MODULE_FEATURE_DEMULTIPLEX)) \
     && (tc_module_av_check((feat)) > 1)) { \
        tc_log_error(MOD_NAME, "unsupported stream types for" \
                           " this module instance"); \
    return TC_ERROR; \
    } \
    \
    if (j != 0 && j != 1) { \
        tc_log_error(MOD_NAME, "feature request mismatch for" \
                           " this module instance (req=%i)", j); \
    return TC_ERROR; \
    } \
    /* is perfectly fine to request to do nothing */ \
    if ((feat != 0) && ((FEATURES) & (feat))) { \
        (self)->features = (feat); \
    } else { \
        tc_log_error(MOD_NAME, "this module does not support" \
                               " requested feature"); \
        return TC_ERROR; \
    } \
} while (0)

/*
 * autogeneration macros for generic init/fini pair
 * looks like generic pair is needed often that expected;
 * In future module system revision, maybe they will be
 * moved into core.
 */

#define TC_MODULE_GENERIC_INIT(MODNAME, MODDATA) \
    static int MODNAME ## _init(TCModuleInstance *self, uint32_t features) \
    { \
        MODDATA *pd = NULL; \
        \
        TC_MODULE_SELF_CHECK(self, "init"); \
        TC_MODULE_INIT_CHECK(self, MOD_FEATURES, features); \
        \
        pd = tc_malloc(sizeof(MODDATA)); \
        if (pd == NULL) { \
            tc_log_error(MOD_NAME, "init: out of memory!"); \
            return TC_ERROR; \
        } \
        \
        self->userdata = pd; \
        \
        if (verbose) { \
            tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP); \
        } \
        \
        return TC_OK; \
    }

#define TC_MODULE_GENERIC_FINI(MODNAME) \
    static int MODNAME ## _fini(TCModuleInstance *self) \
    { \
        TC_MODULE_SELF_CHECK(self, "fini"); \
        \
        tc_free(self->userdata); \
        self->userdata = NULL; \
        return TC_OK; \
    }

/*
 * autogeneration macro for TCModuleInfo descriptor
 */
#define TC_MODULE_INFO(PREFIX) \
static const TCModuleInfo PREFIX ## _info = { \
    .features    = MOD_FEATURES,          \
    .flags       = MOD_FLAGS,             \
    .name        = MOD_NAME,              \
    .version     = MOD_VERSION,           \
    .description = MOD_CAP,               \
    .codecs_in   = PREFIX ## _codecs_in,  \
    .codecs_out  = PREFIX ## _codecs_out, \
    .formats_in  = PREFIX ## _formats_in, \
    .formats_out = PREFIX ## _formats_out \
}

/* please note the MISSING trailing comma */
#define TC_MODULE_CLASS_HEAD(PREFIX) \
    .version     = TC_MODULE_VERSION,  \
    .info        = & ( PREFIX ## _info)


/*
 * autogeneration for supported codecs/multiplexors
 */
#define TC_MODULE_FILTER_FORMATS(PREFIX) \
static const TCFormatID PREFIX ## _formats_in[]  = { TC_FORMAT_ERROR }; \
static const TCFormatID PREFIX ## _formats_out[] = { TC_FORMAT_ERROR }

#define TC_MODULE_CODEC_FORMATS(PREFIX) \
static const TCFormatID PREFIX ## _formats_in[]  = { TC_FORMAT_ERROR }; \
static const TCFormatID PREFIX ## _formats_out[] = { TC_FORMAT_ERROR }

#define TC_MODULE_MPLEX_FORMATS_CODECS(PREFIX) \
static const TCCodecID  PREFIX ## _codecs_out[] = { TC_CODEC_ERROR }; \
static const TCFormatID PREFIX ## _formats_in[] = { TC_FORMAT_ERROR }

#define TC_MODULE_DEMUX_FORMATS_CODECS(PREFIX) \
static const TCCodecID  PREFIX ## _codecs_in = { TC_CODEC_ERROR }; \
static const TCFormatID PREFIX ## _formats_out[] = { TC_FORMAT_ERROR }

/*
 * plugin entry point prototype
 */
const TCModuleClass *tc_plugin_setup(void);

#define TC_MODULE_ENTRY_POINT(MODNAME) \
    extern const TCModuleClass *tc_plugin_setup(void) \
    { \
        return &( MODNAME ## _class); \
    }


/* TODO: unify in a proper way OLDINTERFACE and OLDINTERFACE_M */
#define TC_FILTER_OLDINTERFACE(name) \
    /* Old-fashioned module interface. */ \
    static TCModuleInstance mod; \
    \
    int tc_filter(frame_list_t *frame, char *options) \
    { \
        if (frame->tag & TC_FILTER_INIT) { \
            if (name ## _init(&mod, TC_MODULE_FEATURE_FILTER) < 0) { \
                return TC_ERROR; \
            } \
            return name ## _configure(&mod, options, tc_get_vob()); \
        \
        } else if (frame->tag & TC_FILTER_GET_CONFIG) { \
            return name ## _get_config(&mod, options); \
        \
        } else if (frame->tag & TC_FILTER_CLOSE) { \
            if (name ## _stop(&mod) < 0) { \
                return TC_ERROR; \
            } \
            return name ## _fini(&mod); \
        } \
        \
        return name ## _process(&mod, frame); \
    } 



#define TC_FILTER_OLDINTERFACE_INSTANCES	128

/* FIXME:
 * this uses the filter ID as an index--the ID can grow
 * arbitrarily large, so this needs to be fixed
 */
#define TC_FILTER_OLDINTERFACE_M(name) \
    /* Old-fashioned module interface. */ \
    static TCModuleInstance mods[TC_FILTER_OLDINTERFACE_INSTANCES]; \
    \
    int tc_filter(frame_list_t *frame, char *options) \
    { \
	TCModuleInstance *mod = &mods[frame->filter_id]; \
	\
        if (frame->tag & TC_FILTER_INIT) { \
            tc_log_info(MOD_NAME, "instance #%i", frame->filter_id); \
            if (name ## _init(mod, TC_MODULE_FEATURE_FILTER) < 0) { \
                return TC_ERROR; \
            } \
            return name ## _configure(mod, options, tc_get_vob()); \
        \
        } else if (frame->tag & TC_FILTER_GET_CONFIG) { \
            return name ## _get_config(mod, options); \
        \
        } else if (frame->tag & TC_FILTER_CLOSE) { \
            if (name ## _stop(mod) < 0) { \
                return TC_ERROR; \
            } \
            return name ## _fini(mod); \
        } \
        \
        return name ## _process(mod, frame); \
    } 



#endif /* TCMODULE_PLUGIN_H */
