/**
        \file  ADM_videoFilters.cpp
        \brief Handle current filter list
        \author mean fixounet@free.fr 2010

*/
/***************************************************************************
 *                                                                         *
 *   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 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

#include "BVector.h"
#include "ADM_default.h"
#include "ADM_edit.hxx"
#include "ADM_videoFilterApi.h"
#include "ADM_videoFilters.h"
#include "ADM_videoFilterBridge.h"
#include "ADM_filterChain.h"
#include "ADM_filterThread.h"
#include "ADM_coreVideoFilterFunc.h"

extern ADM_coreVideoFilter *bridge;
extern ADM_Composer *video_body;

/**
    \fn ADM_vf_getSize
    \brief Returns # of active filters
*/
uint32_t                ADM_vf_getSize(void)
{
    return (uint32_t)ADM_VideoFilters.size();
}
/**
    \fn ADM_vf_getInstance
    \brief Return filter instance  of rank index
*/
ADM_coreVideoFilter     *ADM_vf_getInstance(int index)
{
    ADM_assert(index<ADM_vf_getSize());
    return ADM_VideoFilters[index].instance;
}
/**
    \fn ADM_vf_getEnabled
    \brief Return filter enabled status  of rank index
*/
bool ADM_vf_getEnabled(int index)
{
    ADM_assert(index<ADM_vf_getSize());
    return ADM_VideoFilters[index].enabled;
}
/**
    \fn ADM_vf_getTag
    \brief Return tag of index active filter
*/
uint32_t                ADM_vf_getTag(int index)
{
    ADM_assert(index<ADM_vf_getSize());
    return ADM_VideoFilters[index].tag;
}

/**
    \fn ADM_vf_configureFilterAtIndex
*/
bool ADM_vf_configureFilterAtIndex(int index)
{
    ADM_info("Configuring filter at index %d\n",index);
    //
    int nb=ADM_VideoFilters.size();
    ADM_assert(index < nb);
    ADM_VideoFilterElement *e=&(ADM_VideoFilters[index]);
    ADM_coreVideoFilter *instance=e->instance;
    ADM_assert(instance);

    if(instance->configure() && ((index < nb-1) || (e->tag == VF_PARTIAL_FILTER))) // not the last one -OR- partial filter (if only partial timing were changed, the child filter might operate noughty, until next chain rebuild)
    {
        return ADM_vf_recreateChain();
    }
    return true;
}

/**
    \fn ADM_vf_moveFilterUp
*/
bool ADM_vf_moveFilterUp(int index)
{
    ADM_info("Moving up filter at index %d\n",index);
    //
    if(!index) return true;
    uint32_t top=index-1;
    ADM_VideoFilterElement scratch=ADM_VideoFilters[top];

    ADM_VideoFilters[top]=ADM_VideoFilters[top+1];
    ADM_VideoFilters[top+1]=scratch;
    return ADM_vf_recreateChain();
}

/**
    \fn ADM_vf_moveFilterDown
*/
bool ADM_vf_moveFilterDown(int index)
{
    ADM_info("Moving down filter at index %d\n",index);
    //
    ADM_assert(index+1<ADM_VideoFilters.size());
    uint32_t top=index;
    ADM_VideoFilterElement scratch=ADM_VideoFilters[top];

    ADM_VideoFilters[top]=ADM_VideoFilters[top+1];
    ADM_VideoFilters[top+1]=scratch;
    return ADM_vf_recreateChain();
}
/**
 * 
 */
extern ADM_coreVideoFilter *createPartialFilter(const char *internalName,CONFcouple *couples,ADM_coreVideoFilter *source, bool omitConfigDialog);
bool ADM_vf_partialize(int index, bool omitConfigDialog)
{
    ADM_info("Partializing filter at index %d\n",index);
    //
    ADM_assert(index<ADM_VideoFilters.size());
    
    
    ADM_VideoFilterElement scratch=ADM_VideoFilters[index];
    const char *internalName=ADM_vf_getInternalNameFromTag(scratch.tag);
    CONFcouple *conf=NULL;
    if(!scratch.instance->getCoupledConf (&conf))
      {
        ADM_warning("Cannot get configuration\n");
        return false;
      }
    // Create
    ADM_coreVideoFilter *partialized=createPartialFilter(internalName,conf,scratch.instance->getSource(), omitConfigDialog);
    if(!partialized)
    {
        return false;
    }

    //--
    ADM_VideoFilterElement scratch2;
    scratch2.enabled = scratch.enabled;
    scratch2.instance=partialized;
    scratch2.tag=VF_PARTIAL_FILTER;
    scratch2.objectId=0;
    ADM_VideoFilters[index]=scratch2;
    delete scratch.instance;
    scratch.instance=NULL;
    //--
    return ADM_vf_recreateChain();
}
/**
 * 
 */
extern uint32_t ADM_vf_getTagFromInternalName(const char *name);
bool ADM_vf_absolvePartialized(int index)
{
    ADM_info("Absolving partialized filter at index %d\n",index);
    //
    ADM_assert(index<ADM_VideoFilters.size());
    
    
    ADM_VideoFilterElement scratch=ADM_VideoFilters[index];
    const char *internalName=ADM_vf_getInternalNameFromTag(scratch.tag);
    CONFcouple *conf=NULL;
    if(!scratch.instance->getCoupledConf (&conf))
    {
        ADM_warning("Cannot get configuration\n");
        return false;
    }
    // first conf entry: filterName
    // second and third are start and stop time
    int confNb = conf->getSize();
    ADM_assert(confNb>=3);
    char * filterNameName, * filterNameValue;
    conf->getInternalName(0, &filterNameName, &filterNameValue);
    ADM_assert(strcmp(filterNameName,"filterName")==0);
    
    CONFcouple * newConf = new CONFcouple(confNb-3);
    for (int i=3; i<confNb; i++)
    {
        char * name, * key;
        conf->getInternalName(i, &name, &key);
        newConf->setInternalName(name, key);
    }
    
    uint32_t newTag = ADM_vf_getTagFromInternalName(filterNameValue);
    
    ADM_coreVideoFilter *unPartialized=ADM_vf_createFromTag(newTag, scratch.instance->getSource(), newConf);
    delete newConf;
    if(!unPartialized)
    {
        return false;
    }

    //--
    ADM_VideoFilterElement scratch2;
    scratch2.enabled = scratch.enabled;
    scratch2.instance=unPartialized;
    scratch2.tag=newTag;
    scratch2.objectId=0;
    ADM_VideoFilters[index]=scratch2;
    delete scratch.instance;
    scratch.instance=NULL;
    //--
    return ADM_vf_recreateChain();
}

/**
    \fn createVideoFilterChain
    \brief Create a filter chain
*/
ADM_videoFilterChain *createVideoFilterChain(uint64_t startAt,uint64_t endAt)
{
    ADM_videoFilterChain *chain=new ADM_videoFilterChain;
    // 1- Add bridge always # 1
    ADM_videoFilterBridge *bridge=new ADM_videoFilterBridge(video_body, startAt,endAt);
    chain->push_back(bridge);
    ADM_coreVideoFilter *f=bridge;
    // Now create a clone of the videoFilterChain we have here
    int nb=ADM_VideoFilters.size();
    bool openGl=false;
    for(int i=0;i<nb;i++)
    {
            if (!(ADM_VideoFilters[i].enabled)) continue;
            // Get configuration
            CONFcouple *c;
            ADM_coreVideoFilter *old=ADM_VideoFilters[i].instance;
            uint32_t tag=ADM_VideoFilters[i].tag;
            old->getCoupledConf(&c);
            ADM_coreVideoFilter *nw=ADM_vf_createFromTag(tag,f,c);
            if(c) delete c;
            f=nw;
            chain->push_back(nw);

            VF_CATEGORY type=ADM_vf_getFilterCategoryFromTag(tag);
            if(type== VF_OPENGL) openGl=true;

    }
    // Last create the thread
#if 1
    // Make sure there is no openGl filter in the queue, it is not thread safe...

    if(openGl==true)
    {
        ADM_warning("The filter chain contains an openGl filter, disabling threads \n");
    }else
    {
        int m=chain->size();
        ADM_coreVideoFilter *last=(*chain)[m-1];
        ADM_videoFilterQueue *thread=new ADM_videoFilterQueue(last);
        chain->push_back(thread);
    }
#endif
    return chain;
}
/**
    \fn createEmptyVideoFilterChain
    \brief Create an empty filter chain
*/
ADM_videoFilterChain *createEmptyVideoFilterChain(uint64_t startAt,uint64_t endAt)
{
    ADM_videoFilterChain *chain=new ADM_videoFilterChain;
    // 1- Add bridge always # 1
    ADM_videoFilterBridge *bridge=new ADM_videoFilterBridge(video_body, startAt,endAt);
    chain->push_back(bridge);
    // Last create the thread
#if 1
    int m=chain->size();
    ADM_coreVideoFilter *last=(*chain)[m-1];
    ADM_videoFilterQueue *thread=new ADM_videoFilterQueue(last);
    chain->push_back(thread);
#endif
    return chain;
}
/**
        \fn destroyVideoFilterChain
        \brief Destroy a filter chain
*/
bool                 destroyVideoFilterChain(ADM_videoFilterChain *chain)
{
    ADM_assert(chain->size());
    int nb=chain->size();
    if(!nb)
    {
        ADM_info("Empty filter chain\n");
        return true;
    }
    nb--;
    for(int i=nb;i>=0;i--) // delete from the end
    {
        ADM_coreVideoFilter *filter=(*chain)[i];
        delete filter;
        (*chain)[i]=NULL;
    }
    delete chain;
    return true;
}
/**
    \fn ADM_vf_getConfigurationFromIndex
*/
bool ADM_vf_getConfigurationFromIndex(int index,CONFcouple **c)
{
        ADM_assert(index<ADM_VideoFilters.size());
            ADM_coreVideoFilter *old=ADM_VideoFilters[index].instance;
            old->getCoupledConf(c);

        return true;
}


// EOF
