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

#ifndef NOPULSE
#include <pulse/simple.h>
int tostdout = 0;
#else
const int tostdout = 1;
#endif

#include "defines.h"
#include "wavetable.h"
#include "instruments.h"

#include "songdata.h"


typedef union {
    float f;
    char b[sizeof (float)];
} rawfloat;


int main (int argc, char** argv) {
    int i;
    unsigned f;
    rawfloat rf;
    
    fprintf (stderr, "\"Wubs and Caobel\" by grejppi\n");
    
#ifndef NOPULSE
    for (i = 0; i < argc; ++i) {
        if (!strcmp (argv[i], "-s")) {
            tostdout = 1;
        }
    }
    
    pa_simple *ps;
    if (!tostdout) {
        pa_sample_spec ss = {
            .format = PA_SAMPLE_FLOAT32LE,
            .rate = RATE,
            .channels = 2
        };
        
        ps = pa_simple_new (NULL, "Wubs And Caobel", PA_STREAM_PLAYBACK, NULL, "sik wobs m8", &ss, NULL, NULL, NULL);
    }
#endif
    
    init_waves ();
    
    /* process loop */
    int length = (int) ((60 / 120.0f) * RATE);
    
    Rer* bass = rer (DORIRU);
    Sub* su = sub ();
    
    Pluck* clav = pluck (SAW);
    Pluck* clav2 = pluck (SQUARE);
    clav->fcfct = 32;
    clav2->fcfct = 16;
    
    SVF* rerlpf = svf ();
    SVF* rerhpf = svf ();
    
    #define CHORDS 5
    Pluck** chords = (Pluck**) malloc (sizeof (Pluck*) * CHORDS);
    for (i = 0; i < CHORDS; ++i) {
        chords[i] = pluck (i);
        chords[i]->factor = 0.99998;
        chords[i]->fcfct = 2;
    }
    
    #define CHORDS2 4
    Pluck** chords2 = (Pluck**) malloc (sizeof (Pluck*) * CHORDS2);
    for (i = 0; i < CHORDS2; ++i) {
        chords2[i] = pluck (SAW);
        chords2[i]->fcfct = 40;
    }
    
    Voice* vo = voice (SAW);
    
    Voice* vor = voice (SQUARE);
    #define DELAY (RATE/2)
    float vordelayl[DELAY] = { 0.0f };
    float vordelayr[DELAY] = { 0.0f };
    int delp = 0;
    int derp = DELAY/2;
    
    SVF* submask = svf ();
    svf_update (submask, 60.0f, 0.0f);
    
    int kickpos = -1;
    int snarepos = -1;
    int cbpos = -1;
    int hatpos = -1;
    int hatopen = 0;
    
    int tick = 0;
    
    int beat = 0;
    int row = 0;
    unsigned order = 0;
    unsigned long pos = 0;
    
    unsigned long filterpos = 0;
    
    float fcfct = 32.0f;
    
    while (order < songlength) {
        char buffer[NFRAMES * 2 * sizeof (float)] = { 0.0f };
        
        for (f = 0; f < NFRAMES * 2 * sizeof (float);) {
            clav->fcfct = 16 + (fabs (sin (fcfct)) * 16);
            clav2->fcfct = 8 + (fabs (cos (fcfct)) * 8);
            fcfct -= 0.00000441;
            
            float s = 0.0f;
            float l = 0.0f;
            float r = 0.0f;
            
            THANKS_BULBAPEDIA();
            
            /* sidechain */
            float sc = (tick + (beat * (length / 4))) / (float) (length);
            float sc2 = (sc > 0.5) ? 1.0f : sc*sc*sc;
            
            if (order == songlength - 1) sc = 0.0f;
            
            float wub;
            switch (GET (wub_order)) {
            case '0':
                wub = fabs (cos (tick / (float)(length/8)));
                break;
            case '1':
                wub = sc;
                break;
            case '2':
                wub = 1.0f - sc;
                break;
            case '3':
                wub = fabs (sin (tick / (float)(length/16)));
                break;
            default:
                wub = 1.0f;
                break;
            }
            
            float psc;
            switch (GET (padscorder)) {
            case '0':
                psc = sc;
                break;
            case '1':
                psc = wub;
                break;
            case '2':
                psc = sc2;
                break;
            default:
                psc = 1.0f;
                break;
            }
            
            if (tick == 0) {
                if (GET (kickorder) == 'k') kickpos = 60;
                if (GET (kickorder) == 'q') kickpos = 600;
                if (GET (kickorder) == 'w') kickpos = 900;
                if (GET (snareorder) == 's') snarepos = 0;
                if (GET (cborder) == 'c') cbpos = 0;
                if (GET (hatorder) == 'x') {
                    hatpos = 0;
                    hatopen = 1;
                }
                if (GET (hatorder) == 'o') {
                    hatpos = 0;
                    hatopen = 0;
                }
                
                pluck_on (clav, HZ2 (GET (pl_order)));
                clav->detune = 1.002f;
                pluck_on (clav2, HZ2 (GET (pl_order)));
                clav2->detune = 2.0f;
                
                if (GET (bassorder) != HOLD) {
                    rer_on (bass, HZ2 (GET (bassorder)));
                    sub_on (su, HZ2 (GET (bassorder)));
                }
                if (GET (bassorder) == OFF) {
                    rer_off (bass);
                }
                
                if (GET (vororder) != HOLD) {
                    voice_on (vor, 2 * HZ2 (GET (vororder)), 1);
                }
                if (GET (vororder) == OFF) {
                    voice_off (vor);
                }
                
                if (beat == 0) {
                    for (i = 0; i < CHORDS; ++i) {
                        pluck_on (chords[i], HZ2 (patterns[pad_order[order]][i]));
                    }
                }
                
                for (i = 0; i < CHORDS2; ++i) {
                    pluck_on (chords2[i], HZ2 (patterns[c2order[order]][i])*2);
                }
                
                if (GET (vocvorder) != HOLD || GET (vockorder) != HOLD) {
                    voice_on (vo, HZ2 (GET (vockorder))*2, GET (vocvorder));
                }
                if (GET (vocvorder) == OFF) {
                    voice_off (vo);
                }
                
                if (order == 64) filterpos = 0;
            }
            
            svf_update (rerlpf, 2.1f + (4000.0f * wub * wub), (0.3f * wub));
            svf_update (rerhpf, 0.1f + 2000.0f - (1000.0f * wub), (0.5f * wub * wub));
            
            float plucks = 0.0f;
            plucks += pluck_next (clav);
            plucks += pluck_next (clav2) * sc;
            
            s += plucks / 1.5f;
            
            plucks = 0.0f;
            for (i = 0; i < CHORDS; ++i) {
                chords[i]->fcfct = fmax ((filterpos / (length / 4.0f)) * 0.125, 1);
                if (row > 11) {
                    chords[i]->detune = fmax (1.0f - (sc * sc * sc * sc), 0.12);
                } else {
                    chords[i]->detune = 1.0f;
                }
                plucks += pluck_next (chords[i]) * psc;
            }
            s += plucks / (float) (CHORDS - 1);
            
            plucks = 0.0f;
            for (i = 0; i < CHORDS2; ++i) {
                plucks += pluck_next (chords2[i]);
            }
            s += plucks * 0.6f;
            
            /* make room for bass */
            s = svf_process (submask, s, svf_h, 0);
            
            float rers = rer_next (bass) ;//+ rer_next (pad2);
            float rerr = 0.0f;
            if (bass->on) {
                rerr += svf_process (rerlpf, rers, svf_l, 1) * wub * sc;
                rerr += svf_process (rerhpf, rers, svf_b, 1) * wub * sc;
            }
            s += atan (rerr) * 2;
            
            //~ s = 0;
            s += atan (sub_next (su) * sc * sc * 4);
            
            float drums = 0.0f;
            if (kickpos != -1) {
                drums += kick[kickpos] * 2;
                if (++kickpos == WAVESIZE) kickpos = -1;
            }
            if (snarepos != -1) {
                drums -= snare[snarepos];
                if (600+(snarepos*4) < WAVESIZE) l += snare[600+(snarepos*4)] * 0.5;
                if (462+(snarepos*4) < WAVESIZE) r += snare[462+(snarepos*4)] * 0.5;
                if (++snarepos == WAVESIZE) snarepos = -1;
            }
            if (hatpos != -1) {
                drums += (hatopen ? ophat : clhat)[hatpos];
                if (++hatpos == WAVESIZE) hatpos = -1;
            }
            if (cbpos != -1) {
                drums += caobel[(int)(cbpos*1.122)] * 1.5;
                if ((int)(++cbpos*1.222) >= WAVESIZE) cbpos = -1;
            }
            
            s += drums;
            
            //~ vo->detune = 0.75f + (wub * 0.25f);
            //~ vo->detune = wub;
            s += voice_next (vo) * 0.6f;
            
            float vors = voice_next (vor);
            vordelayl[delp % DELAY] = ((vordelayr[derp % DELAY]*4) + vors) / 5.0f;
            
            vordelayr[derp % DELAY] = ((vordelayl[delp % DELAY]*4) + vors) / 5.0f;
            
            l += vordelayl[delp++ % DELAY] * 1.2f;
            r += vordelayr[derp++ % DELAY] * 1.2f;
            
            l += s;
            r += s;
            
            l /= 5.0f;
            r /= 5.0f;
            
            rf.f = l;
            for (i = 0; i < sizeof (float); ++i) {
                buffer[f++] = rf.b[i];
            }
            
            rf.f = r;
            for (i = 0; i < sizeof (float); ++i) {
                buffer[f++] = rf.b[i];
            }
            
            /* song position */
            if (++tick == length / 4) {
                if (++row == 16) {
                    row = 0;
                    ++order;
                }
                if (++beat == 4) {
                    beat = 0;
                }
                tick = 0;
            }
            
            filterpos++;
            pos++;
        }
        
        if (tostdout) {
            for (i = 0; i < sizeof (float) * NFRAMES * 2; ++i) {
                putchar (buffer[i]);
            }
#ifndef NOPULSE
        } else {
            pa_simple_write (ps, buffer, sizeof (float) * NFRAMES * 2, NULL);
#endif
        }
    }
    
    //~ if (tostdout) {
        //~ rf.f = 0.0f;
        //~ while (pos++ % RATE != 0) {
            //~ for (i = 0; i < sizeof (float); ++i) {
                //~ putchar (rf.b[i]);
            //~ }
        //~ }
    //~ }
    
#ifndef NOPULSE
    if (!tostdout) pa_simple_free (ps);
#endif
    
    return 0;
}
