#include "welay_t.h"

void welay_t_setup(void){
    pcWelay_t = class_new(gensym("welay_tilde"),
          (t_newmethod)welay_t_new, (t_method)welay_t_delete,
          sizeof(sWelay_t),
          CLASS_DEFAULT,
          A_DEFFLOAT, 0);
    
    class_addmethod(pcWelay_t,
        (t_method)welay_t_dsp, gensym("dsp"), 0);
    class_addbang(pcWelay_t, welay_t_bang);
    class_addmethod(pcWelay_t, (t_method)welay_t_set_soundbuffer_length_factor, gensym("set_soundbuffer_length_factor"), A_DEFFLOAT, 0);
    CLASS_MAINSIGNALIN(pcWelay_t, sWelay_t, fConstSignal);
}

void *welay_t_new(t_floatarg fArg UNUSED){
    
    sWelay_t *psT = (sWelay_t *)pd_new(pcWelay_t);
    
    psT->x_inf = floatinlet_new(&psT->x_obj, &psT->fDelay);
    psT->x_out = outlet_new(&(psT->x_obj), &s_signal);
    
    psT->fDelay=0;
    
    psT->sSound.iLength=9000*PD_BLOCKSIZE;
    
    psT->sSound.pA=NULL;
    psT->sSound.pC=NULL;
    psT->sSound.pE=NULL;
    
    welay_t_initialize_sSound(psT);
    
    return (void *)psT;
}

void welay_t_delete(sWelay_t* psT){
    outlet_free(psT->x_out);
    inlet_free(psT->x_inf);
    psT->x_out=NULL;
    psT->x_inf=NULL;
    
    wabor_t_free(psT->sSound.pA);
    
    psT->sSound.pA=NULL;
    psT->sSound.pC=NULL;
    psT->sSound.pE=NULL;   
    
}  

void welay_t_dsp(sWelay_t* psT, t_signal **sp){
#define TELAY_T_PERFORM_NUM_POINTERS (4)
    dsp_add(welay_t_perform, TELAY_T_PERFORM_NUM_POINTERS,
            psT,
            sp[0]->s_vec,
            sp[1]->s_vec,
            sp[0]->s_n
            );
}
t_int *welay_t_perform(t_int *aw){
    sWelay_t *psT = (sWelay_t *)(aw[1]);
    t_sample *in  = (t_sample *)(aw[2]);
    t_sample *out = (t_sample *)(aw[3]);
    t_int   iLength = (t_int)       (aw[4]);
    t_int iDelay=(t_int)psT->fDelay;
    
    if(iLength!=PD_BLOCKSIZE) return (aw + TELAY_T_PERFORM_NUM_POINTERS + 1);
    //schreiben
    psT->sSound.pC+=PD_BLOCKSIZE;
    if(psT->sSound.pC==psT->sSound.pE) psT->sSound.pC=psT->sSound.pA;
    memcpy(psT->sSound.pC,in,PD_BLOCKSIZE * sizeof *(psT->sSound.pC));
    
    //lesen
    
    if(iDelay<0) {
        psT->fDelay=0;
        iDelay=0; 
    }
    if(iDelay > psT->sSound.iLength) {
        welay_t_set_soundbuffer_length_factor(psT, (t_float)iDelay/PD_BLOCKSIZE + 500);
    }
    t_int sL=psT->sSound.iLength;
    float *sA=psT->sSound.pA;
    float *sC=psT->sSound.pC;
    float *sR=sC-iDelay;
    float *sRE=sR+PD_BLOCKSIZE;
    if(sRE<sA){
        sR+=sL;
        memcpy(out, sR, PD_BLOCKSIZE * sizeof *out);
    } else if(sR>=sA) {
        memcpy(out, sR, PD_BLOCKSIZE * sizeof *out);
    } else {
        t_int L1=sA-sR;
        sR+=sL;
        memcpy(out, sR, L1 * sizeof *out);
        memcpy(out+L1, sA, (PD_BLOCKSIZE-L1) * sizeof *out);
    }
    
    return (aw + TELAY_T_PERFORM_NUM_POINTERS + 1);
    
}
#undef TELAY_T_PERFORM_NUM_POINTERS
    
    
void welay_t_bang(sWelay_t const * const psT){
    post("Welay: Delay=%i",(t_int)psT->fDelay);
}

void welay_t_initialize_sSound(sWelay_t* psT){
    t_int iSize=psT->sSound.iLength;

    wabor_t_free(psT->sSound.pA);
    
    psT->sSound.pA = wabor_t_malloc0(iSize * sizeof *(psT->sSound.pA));
    if(psT->sSound.pA==NULL){
        psT->sSound.pC=NULL;
        psT->sSound.pE=NULL;
        return;
    }
    psT->sSound.pC = psT->sSound.pA;
    psT->sSound.pE = psT->sSound.pA + iSize;
    
    memset(psT->sSound.pA, 0, iSize * sizeof *(psT->sSound.pA));
    return;
}
void welay_t_set_soundbuffer_length_factor(sWelay_t *psT, t_float fFactor){
    if(fFactor<=1) fFactor=3000;
    psT->sSound.iLength=(t_int)fFactor*PD_BLOCKSIZE;
    welay_t_initialize_sSound(psT);
    return;   
}