/***************************************************************************
 *   Copyright (C) 2004 by Predrag Viceic                                  *
 *   viceic@net2000.ch                                             *
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 *   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.  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.             *
 ***************************************************************************/
#include "ladspaeffect.h"

LADSPAEffect::LADSPAEffect(const LADSPA_Descriptor* _desc,QObject *parent, const char *name)
 : QObject(parent, name)
{
    descriptor=_desc;
    plugin_handle_1=NULL;
    plugin_handle_2=NULL;
    for(uint i=0;i<getPortCount();i++){
        LADSPA_PortDescriptor desc=(LADSPA_PortDescriptor)descriptor->PortDescriptors[i];
        LADSPA_PortRangeHint hint=(LADSPA_PortRangeHint)descriptor->PortRangeHints[i];
        LADSPAPort* tempport=new LADSPAPort(i,descriptor->PortNames[i],desc,hint);
        if(tempport->isAudioInput()) audioInputPorts.append(tempport);
        else if(tempport->isAudioOutput()) audioOutputPorts.append(tempport);
        else if(tempport->isControlInput()) controlInputPorts.append(tempport);
        else if(tempport->isControlOutput()) controlOutputPorts.append(tempport);
    }
    inputAudioBufferLeft=0;
    outputAudioBufferLeft=0;
    inputAudioBufferRight=0;
    outputAudioBufferRight=0;
    currentAudioBufferPosition=0;
    isInUse=FALSE;
    connectors=new CustomEffects();
    nbFrames=0;
    portInitDone=FALSE;
}


LADSPAEffect::~LADSPAEffect()
{

}

unsigned long LADSPAEffect::getUniqueID(){
    return descriptor->UniqueID;
}
const char * LADSPAEffect::getLabel(){
    return descriptor->Label;
}
const char * LADSPAEffect::getName(){
    return descriptor->Name;
}
const char * LADSPAEffect::getMaker(){
    return descriptor->Maker;
}
const char * LADSPAEffect::getCopyright(){
    return descriptor->Copyright;
}
unsigned long LADSPAEffect::getPortCount(){
    return descriptor->PortCount;
}

bool LADSPAEffect::isRealtime()
{
    return (bool)LADSPA_IS_REALTIME(descriptor->Properties);
}

bool LADSPAEffect::isInplaceBroken()
{
    return (bool)LADSPA_IS_INPLACE_BROKEN(descriptor->Properties);
}

bool LADSPAEffect::isHardRTCapable()
{
    return (bool)LADSPA_IS_HARD_RT_CAPABLE(descriptor->Properties);
}


/*!
    \fn LADSPAEffect::init()
 */
void LADSPAEffect::init(int rate,float* inputBufferLeft,float* inputBufferRight,
                                          float* outputBufferLeft,float* outputBufferRight)
{
    inputAudioBufferLeft=inputBufferLeft;
    outputAudioBufferLeft=outputBufferLeft;
    inputAudioBufferRight=inputBufferRight;
    outputAudioBufferRight=outputBufferRight;


    plugin_handle_1=descriptor->instantiate(descriptor,rate);
    if(usesDoublePlugin()) plugin_handle_2=descriptor->instantiate(descriptor,rate);
    currentAudioBufferPosition=0;
    activate();
    startPortValues(0);
}


/*!
    \fn LADSPAEffect::isEffectSuitable()
 */
bool LADSPAEffect::isEffectSuitable()
{
    return (/*(audioInputPorts.count()>=1) &&*/ (audioOutputPorts.count()>=1));
}


/*!
    \fn LADSPAEffect::deactivate()
 */
void LADSPAEffect::deactivate()
{
    if (descriptor->deactivate){
      descriptor->deactivate(plugin_handle_1);
      if(usesDoublePlugin()) descriptor->deactivate(plugin_handle_2);
  }
   if (descriptor->cleanup){
      descriptor->cleanup(plugin_handle_1);
      if(usesDoublePlugin())descriptor->cleanup(plugin_handle_2);
  }
}

/*!
    \fn LADSPAEffect::cleanup()
 */
void LADSPAEffect::cleanup()
{
   if (descriptor->cleanup){
      descriptor->cleanup(plugin_handle_1);
      if(usesDoublePlugin())descriptor->cleanup(plugin_handle_2);
  }
}

/*!
    \fn LADSPAEffect::activate()
 */
void LADSPAEffect::activate()
{
    if(descriptor->activate){
        descriptor->activate(plugin_handle_1);
        if(usesDoublePlugin())descriptor->activate(plugin_handle_2);
    }
}

/*!
    \fn LADSPAEffect::process(long size)
 */
void LADSPAEffect::process(long size)
{
    connectors->processEffects(size,CustomEffect::POSITION_PRE);
    descriptor->run(plugin_handle_1,size);
    if(usesDoublePlugin())descriptor->run(plugin_handle_2,size);
    connectors->processEffects(size,CustomEffect::POSITION_PASS);
    connectors->processEffects(size,CustomEffect::POSITION_POST);
}


/*!
    \fn LADSPAEffect::getControlInputPorts()
 */
QPtrList<LADSPAPort>* LADSPAEffect::getControlInputPorts()
{
    return &controlInputPorts;
}

QPtrList<LADSPAPort>* LADSPAEffect::getControlOutputPorts()
{
    return &controlOutputPorts;
}

QPtrList<LADSPAPort>* LADSPAEffect::getAudioInputPorts()
{
    return &audioInputPorts;
}

QPtrList<LADSPAPort>* LADSPAEffect::getAudioOutputPorts()
{
    return &audioOutputPorts;
}

void LADSPAEffect::advancePortValues(int length){

    for(QPtrList<LADSPAPort>::Iterator it = controlInputPorts.begin(); it != controlInputPorts.end(); ++it){
        (*it)->advanceValue();
        descriptor->connect_port(plugin_handle_1,(*it)->getPortNumber(),(*it)->getCPortValuePointer());
        if(usesDoublePlugin())
            descriptor->connect_port(plugin_handle_2,(*it)->getPortNumber(),(*it)->getCPortValuePointer());
        //std::cout<<"cport value: "<<*(*it)->getCPortValuePointer()<<"\n";
    }
        currentAudioBufferPosition+=length;

        //inputs
        for(QPtrList<LADSPAPort>::Iterator it = audioInputPorts.begin(); it != audioInputPorts.end(); ++it){
            if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_L){
                descriptor->connect_port(
                            plugin_handle_1,(*it)->getPortNumber(),&inputAudioBufferLeft[currentAudioBufferPosition]);
            }else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_R){
                descriptor->connect_port(
                           plugin_handle_1,(*it)->getPortNumber(),&inputAudioBufferRight[currentAudioBufferPosition]);
            }else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_LR){
            }else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_ZERO){
                //OK
            }
            if(usesDoublePlugin()){
                 if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_L){
                    descriptor->connect_port(
                             plugin_handle_2,(*it)->getPortNumber(),&inputAudioBufferLeft[currentAudioBufferPosition]);
                }else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_R){
                    descriptor->connect_port(
                           plugin_handle_2,(*it)->getPortNumber(),&inputAudioBufferRight[currentAudioBufferPosition]);
                }else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_LR){
                }else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_ZERO){
                    //OK
                }
            }
        }
        //end inputs
        //outputs
        for(QPtrList<LADSPAPort>::Iterator it = audioOutputPorts.begin(); it != audioOutputPorts.end(); ++it){
            if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_L){
                descriptor->connect_port(
                            plugin_handle_1,(*it)->getPortNumber(),&outputAudioBufferLeft[currentAudioBufferPosition]);
            }else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_R){
                descriptor->connect_port(
                        plugin_handle_1,(*it)->getPortNumber(),&outputAudioBufferRight[currentAudioBufferPosition]);
            }else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_LR){
            }else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_ZERO){
            }
            if(usesDoublePlugin()){
                if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_L){
                    descriptor->connect_port(
                           plugin_handle_2,(*it)->getPortNumber(),&outputAudioBufferLeft[currentAudioBufferPosition]);
                }else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_R){
                    descriptor->connect_port(
                       plugin_handle_2,(*it)->getPortNumber(),&outputAudioBufferRight[currentAudioBufferPosition]);
                }else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_LR){
                }else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_ZERO){
                }
            }
        }
        //end outputs
        connectors->initEffects(length);
        connectors->advancePositions(length);

}

void LADSPAEffect::startControlPortValues(long start_sample){
    for(QPtrList<LADSPAPort>::Iterator it = controlInputPorts.begin(); it != controlInputPorts.end(); ++it){
        (*it)->startValue(start_sample/1024);
        descriptor->connect_port(plugin_handle_1,(*it)->getPortNumber(),(*it)->getCPortValuePointer());
        if(usesDoublePlugin())
            descriptor->connect_port(plugin_handle_2,(*it)->getPortNumber(),(*it)->getCPortValuePointer());
    }
}

void LADSPAEffect::startPortValues(long start_sample){
    currentAudioBufferPosition=start_sample;
    startControlPortValues(currentAudioBufferPosition);
    connectAudioPorts(currentAudioBufferPosition);//reconnect audio buffers
}





/*!
    \fn LADSPAEffect::setNbFrames(int)
 */
void LADSPAEffect::setNbFrames(long _nbFrames)
{
    nbFrames=_nbFrames;
    for(QPtrList<LADSPAPort>::Iterator it = controlInputPorts.begin(); it != controlInputPorts.end(); ++it){
        (*it)->setNbFrames(nbFrames);
    }
}

/*!
    \fn LADSPAEffect::setNbFrames(int)
 */
void LADSPAEffect::initControlPortsValues()
{
    for(QPtrList<LADSPAPort>::Iterator it = controlInputPorts.begin(); it != controlInputPorts.end(); ++it){
        (*it)->initControlValues();
        std::cout<<"control values init ok\n";
    }
}



/*!
    \fn LADSPAEffect::setInUse(bool)
 */
void LADSPAEffect::setInUse(bool tf)
{
    isInUse=tf;
}


/*!
    \fn LADSPAEffect::getIsInUse()
 */
bool LADSPAEffect::getIsInUse()
{
    return isInUse;
}


/*!
    \fn LADSPAEffect::hasMonoInput()
 */
bool LADSPAEffect::hasMonoInput()
{
    return getAudioInputPorts()->count()==1;
}


/*!
    \fn LADSPAEffect::hasMonoOutput()
 */
bool LADSPAEffect::hasMonoOutput()
{
    return getAudioOutputPorts()->count()==1;
}


/*!
    \fn LADSPAEffect::hasAudioInput()
 */
bool LADSPAEffect::hasAudioInput()
{
    return getAudioInputPorts()->count()>0;
}


/*!
    \fn LADSPAEffect::getNonAssignedOutput()
 */
int LADSPAEffect::getNonAssignedOutput()
{
    bool leftOk=false;
    bool rightOk=false;
    for (QPtrList<LADSPAPort>::Iterator it = audioOutputPorts.begin(); it != audioOutputPorts.end(); ++it){
            if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_L) leftOk=TRUE;
            else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_R) rightOk=TRUE;
            else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_LR){
                leftOk=TRUE;
                rightOk=TRUE;
            }
            if(usesDoublePlugin()){
                if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_L) leftOk=TRUE;
                else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_R) rightOk=TRUE;
                else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_LR){
                    leftOk=TRUE;
                    rightOk=TRUE;
                }
            }
    }
    if(leftOk && rightOk) return LADSPAPort::CONNECTION_METHOD_ZERO;
    if(leftOk && !rightOk) return LADSPAPort::CONNECTION_METHOD_R;
    if(!leftOk && rightOk) return LADSPAPort::CONNECTION_METHOD_L;
    if(!leftOk && !rightOk) return LADSPAPort::CONNECTION_METHOD_LR;
}


/*!
    \fn LADSPAEffect::usesDoublePlugin()
 */
bool LADSPAEffect::usesDoublePlugin()
{
    return usesDoublePluginVal;
}


/*!
    \fn LADSPAEffect::connectAudioPorts(float* start)
 */
void LADSPAEffect::connectAudioPorts(long start)
{
    //inputs
        connectors->clear();
        for(QPtrList<LADSPAPort>::Iterator it = audioInputPorts.begin(); it != audioInputPorts.end(); ++it){
            if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_L){
                descriptor->connect_port(
                            plugin_handle_1,(*it)->getPortNumber(),&inputAudioBufferLeft[start]);
            }else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_R){
                descriptor->connect_port(
                            plugin_handle_1,(*it)->getPortNumber(),&inputAudioBufferRight[start]);
            }else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_LR){
                NtoMonoMixer* mixer=new NtoMonoMixer(2);
                mixer->setPre();mixer->init(1024);
                mixer->setInput_P(0,&inputAudioBufferLeft[start]);
                mixer->setInput_P(1,&inputAudioBufferRight[start]);
                descriptor->connect_port(
                            plugin_handle_1,(*it)->getPortNumber(),mixer->getOutput_P());
                connectors->add(mixer);
            }else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_ZERO){
                InputZero* zero=new InputZero();
                zero->setPre(); zero->init(1024);
                connectors->add(zero);
                descriptor->connect_port(
                            plugin_handle_1,(*it)->getPortNumber(),zero->getOutput_P());
            }
            if(usesDoublePlugin()){
                if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_L){
                descriptor->connect_port(
                            plugin_handle_2,(*it)->getPortNumber(),&inputAudioBufferLeft[start]);

                }else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_R){
                    descriptor->connect_port(
                                plugin_handle_2,(*it)->getPortNumber(),&inputAudioBufferRight[start]);

                }else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_LR){
                    NtoMonoMixer* mixer=new NtoMonoMixer(2);
                    mixer->setPre();mixer->init(1024);
                    mixer->setInput_P(0,&inputAudioBufferLeft[start]);
                    mixer->setInput_P(1,&inputAudioBufferRight[start]);
                    descriptor->connect_port(
                                plugin_handle_2,(*it)->getPortNumber(),mixer->getOutput_P());
                    connectors->add(mixer);
                }else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_ZERO){
                    InputZero* zero=new InputZero();
                    zero->setPre(); zero->init(1024);
                    connectors->add(zero);
                    descriptor->connect_port(
                                plugin_handle_2,(*it)->getPortNumber(),zero->getOutput_P());
                }
            }
        }
        //end inputs
        //outputs
        for(QPtrList<LADSPAPort>::Iterator it = audioOutputPorts.begin(); it != audioOutputPorts.end(); ++it){
            if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_L){
                descriptor->connect_port(
                            plugin_handle_1,(*it)->getPortNumber(),&outputAudioBufferLeft[start]);
            }else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_R){
                descriptor->connect_port(
                            plugin_handle_1,(*it)->getPortNumber(),&outputAudioBufferRight[start]);
            }else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_LR){
                // NOT YET OK!!

                MonoToStereoSplitter* split=new MonoToStereoSplitter();
                split->setPost();split->init(1024);
                descriptor->connect_port(
                            plugin_handle_1,(*it)->getPortNumber(),split->getInput_P());
                split->setOutputL_P(&outputAudioBufferLeft[start]);
                split->setOutputR_P(&outputAudioBufferRight[start]);
                connectors->add(split);
            }else if((*it)->getConnectionMethod()==LADSPAPort::CONNECTION_METHOD_ZERO){
                OutputToZero* zero=new OutputToZero();
                zero->setPost(); zero->init(1024);
                connectors->add(zero);
                descriptor->connect_port(
                            plugin_handle_1,(*it)->getPortNumber(),zero->getInput_P());
            }
            if(usesDoublePlugin()){
                 if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_L){
                    descriptor->connect_port(
                            plugin_handle_2,(*it)->getPortNumber(),&outputAudioBufferLeft[start]);
                }else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_R){
                    descriptor->connect_port(
                                plugin_handle_2,(*it)->getPortNumber(),&outputAudioBufferRight[start]);
                }else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_LR){
                    //split
                }else if((*it)->getConnectionMethodDouble()==LADSPAPort::CONNECTION_METHOD_ZERO){
                    OutputToZero* zero=new OutputToZero();
                    zero->setPost(); zero->init(1024);
                    connectors->add(zero);
                    descriptor->connect_port(
                                plugin_handle_2,(*it)->getPortNumber(),zero->getInput_P());
                }
            }
        }
        //end outputs
        //passthrough ports
        int nonAssigned=getNonAssignedOutput();
        if(nonAssigned==LADSPAPort::CONNECTION_METHOD_L){
            PassThrough* passt=new PassThrough(1);
            passt->setPost();
            passt->setInput_P(0,&inputAudioBufferLeft[start]);
            passt->setOutput_P(0,&outputAudioBufferLeft[start]);
            connectors->add(passt);
        }else if(nonAssigned==LADSPAPort::CONNECTION_METHOD_R){
            PassThrough* passt=new PassThrough(1);
            passt->setPost();
            passt->setInput_P(0,&inputAudioBufferRight[start]);
            passt->setOutput_P(0,&outputAudioBufferRight[start]);
            connectors->add(passt);
        }else if(nonAssigned==LADSPAPort::CONNECTION_METHOD_LR){
            PassThrough* passt=new PassThrough(2);
            passt->setPost();
            passt->setInput_P(0,&inputAudioBufferLeft[start]);
            passt->setOutput_P(0,&outputAudioBufferLeft[start]);
            passt->setInput_P(1,&inputAudioBufferRight[start]);
            passt->setOutput_P(1,&outputAudioBufferRight[start]);
            connectors->add(passt);
        }
        //end passthrough ports
}


/*!
    \fn LADSPAEffect::setDoublePlugin(bool)
 */
void LADSPAEffect::setDoublePlugin(bool tf)
{
    usesDoublePluginVal=tf;
}


/*!
    \fn LADSPAEffect::getFrames()
 */
long LADSPAEffect::getFrames()
{
    return nbFrames;
}


/*!
    \fn LADSPAEffect::getPort(int port_no)
 */
LADSPAPort* LADSPAEffect::getPort(int port_no)
{
    for(QPtrList<LADSPAPort>::Iterator it = controlInputPorts.begin(); it != controlInputPorts.end(); ++it){
        if((*it)->getPortNumber()==port_no) return (*it);
    }
    for(QPtrList<LADSPAPort>::Iterator it = audioInputPorts.begin(); it != audioInputPorts.end(); ++it){
        if((*it)->getPortNumber()==port_no) return (*it);
    }
    for(QPtrList<LADSPAPort>::Iterator it = audioOutputPorts.begin(); it != audioOutputPorts.end(); ++it){
        if((*it)->getPortNumber()==port_no) return (*it);
    }
    return 0;
}


/*!
    \fn LADSPAEffect::wasPortInitDone()
 */
bool LADSPAEffect::wasPortInitDone()
{
    return portInitDone;
}


/*!
    \fn LADSPAEffect::setPortInitDone(bool tf)
 */
void LADSPAEffect::setPortInitDone(bool tf)
{
    portInitDone=tf;
}
