
#ifdef TEXTMODE
#define DISABLE_FBDEV
#endif

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

// points to the EHDR (start of page containing the binary at runtime)
extern uint8_t ehdr_base[];
// points to the end of the file
extern uint8_t file_end[0];
__attribute__((__section__(".elf.end"))) uint8_t file_end[0];

#include "def.h"
#include "sys.h"
#include "dbg.h"
#include "tex.h"
#include "fnt.h"
#include "snd.h"
#include "gfx.h"

/*
 * TODO: !inc/sys.h
 * * fix alignment inserted by linker
 * * better stub gen stuff
 * * rocket integration -> doneish // will I ever use this?
 * * content!
 */

// go home GCC, you're drunk
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-const-variable"
//static const char conrst[]="\x1B[m\x1B" "c";
static const char fbdevfile[] = "/dev/fb1";
static const uint8_t tonearr[]={
    0xEF,0,0xEF,0xEF,0xEF,0,0xFF,0xFF,0xEF,0xEF,0xEF,0xEF,0xDF,0xDF,0xDF,0xDF,
/*#ifndef DESPERATE
    0xEF,0,0xEF,0xEF,0xEF,0,0xFF,0xFF,0xEF,0xEF,0xEF,0xEF,0xAF,0xAF,0xAF,0xAF
#endif*/
};
#pragma GCC diagnostic pop

inline static uint32_t xorshift32(uint32_t x) {
    x ^= x << 13;
    x ^= x >> 17;
    x ^= x << 5;
    return x;
}

static uint8_t getm2(size_t i, uint16_t max) {
/*#ifndef VERY_DESPERATE
#define M2OP %
#else*/
#define M2OP &
//#endif

    if(!max)return i^i;
    uint8_t* p=ehdr_base;
    uint8_t r= p[i M2OP max];
    max>>=1;
    return r+p[i M2OP max];

#undef M2OP
}

#ifndef DEBUG
int _start(void);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wattributes"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-attributes"
__attribute__((__noreturn__, __externally_visible__,
    __section__(".text.startup")
#ifndef __clang__
    , __naked__
#endif
))
#pragma clang diagnostic pop
// breaks?
//#pragma GCC diagnostic pop
int _start()
#else
int main()
#endif
{
#if defined(ALIGN_STACK) && !defined(DESPERATE) && defined(__x86_64__)
    asm volatile("subq $8, %rsp");
#endif
    // else: NOTE - stack is unaligned, etc. etc. etc.

    //load_font(fnt);

    snd_init();

#ifndef DISABLE_FBDEV
    const int fb = (int)SYS_open(fbdevfile, O_RDWR);

    struct fb_fix_screeninfo fix;
    //struct fb_var_screeninfo var;

    //SYS_ioctl(fb, FBIOGET_VSCREENINFO, &var);
    SYS_ioctl(fb, FBIOGET_FSCREENINFO, &fix);

#ifndef FBHEIGHT
    const size_t
        yres = var.yres,
        xres = var.xres;
#else
    const size_t
        yres = FBHEIGHT,
        xres = FBWIDTH ;
#endif

    const size_t sz =
#ifndef FBHEIGHT
        (size_t)var.yres_virtual
#else
        FBHEIGHT
#endif
        * fix.line_length;
    void*  mem = SYS_mmap(NULL, sz, /*PROT_READ|PROT_WRITE, MAP_SHARED               ,*/ fb);
    //void* bmem = SYS_mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1);
#else
    SYS_fcntl(STDERR_FILENO, F_SETFL,
        (size_t)SYS_fcntl(STDERR_FILENO, F_GETFL, 0)|O_NONBLOCK);
#endif

    uint32_t rds = *(uint32_t*)&_start;

    // filter coeffs (for ci'evru)
    const float aa = 0.25f/*0.20f*/;
    // filter state (ss: ci'evru, zz: global)
    float ss = 0.0f, zz = ss;

    uint8_t auring[AURINGSZ];
    uint8_t* aubuf=auring;
    size_t auringpos;auringpos^=auringpos;
    //uint8_t aubuf[AUBUFSZ];
    uint8_t fbbuf[FBBUFSZ];
    //for (uint8_t* p=fbbuf,e=fbbuf+sizeof fbbuf; p!=e;++p) *p^=*p;
    const size_t fboff =
#ifndef DESPERATE
        0x1FF1 // 5 bytes // iku'i ja'a se nitcu
#else
        0x2000
#endif
        ;

    for (size_t t = 0, i = t; ; ++t) {
        if (/*!t ||*/ SYS_write(STDOUT_FILENO, aubuf, AUBUFSZ) == AUBUFSZ) {

            {
                uint8_t* prevau=auring+((auringpos+AUBUFSZ)&(AURINGSZ-1)),
                    *cpv=prevau,
                    *npv=prevau+AUBUFSZ; // people container
#ifndef DISABLE_FBDEV
                uint8_t* d=mem,*e=d+sz;
                //size_t j;j^=j;
                do{
                    *d=*cpv;
                    ++cpv;
                    ++d;
                    //++j;
                    if(cpv>=npv)cpv=prevau;
                    //j%=fix.line_length;
                    //j&=AUBUFSZ-1;
                    //swap_buf(aubuf, d, AUBUFSZ);
                    //d+=AUBUFSZ
                }while(d<e);
#else
                char outs[AUBUFSZ*9],
                        *outp=outs,*oute=outp+sizeof outs;
                do{
                    uint8_t v=*cpv;
                    ++cpv;
                    if(cpv>=npv)cpv=prevau;

                    *outp='\x1B';++outp;
                    *outp='['   ;++outp;
                    *outp='3'   ;++outp;
                    *outp='0'+(v>>5);++outp;
                    *outp=';'   ;++outp;
                    *outp='4'   ;++outp;
                    *outp='0'+(v& 7);++outp;
                    *outp='m'   ;++outp;
                    // without a space before the minus sign,
                    // GCC (7.3.1 20180312, arch package) chokes!
                    *outp=(v>>3)|0x40;++outp;
                    //*outp=v%(0x7E -0x20)+0x20;++outp;
                }while(outp<oute);
                SYS_write(STDERR_FILENO,outs,sizeof outs);
#endif
            }

            auringpos+=AUBUFSZ;
            aubuf=auring+(auringpos&=AURINGSZ-1);
            for (size_t j = 0; j < AUBUFSZ; ++j, ++i) {
                // TODO: OPTIMIZE tHIS CODE!
                // it's currently size-inefficient as fuck

                rds=xorshift32(rds);

                uint8_t k=i>>16;
                uint16_t e,ih;

                uint8_t v=0,w=v,v_=w,mask;
                float co=v; // filter cutoff

                if (k>=(uint8_t)0x48||k<(uint8_t)0x2){
                    v=(0x20+((i/3)&0x80)+(rds&0x1F));
                    if(k<(uint8_t)0x2){
                        aubuf[j]=v;
                        continue;
                    }
                    goto endph;
                }

                e=0x300;
                if (k<(uint8_t)0xC){
                    ih=(uint16_t)i>>9;
                    e+=ih>>1;
                    if (k>(uint8_t)0x8)e-=ih;
                }
                // actual size of the binary doesn't matter, segfaults won't
                // happen as long as we stay within the page boundary
                v = (ehdr_base[i%e]);
                if (k<(uint8_t)0x08) goto wsamp;
                mask=(uint8_t)0-(uint8_t)popcnt((uint16_t)i&(uint16_t)0x2000);
                //mask^=~mask;
                //if ((uint16_t)i&(uint16_t)0x2000)mask^=mask;
                v&=mask;
                if (k<(uint8_t)0x0A) goto wsamp;
                // TODO: use fixed-point calculations
                // TODO: -> ~100 bytes can be gained
                // - DOESN'T SEEM TO WORK :/
                if (k<(uint8_t)0x0C) {
                    co = (0.0000076294f) * (i - 0xA0000); // TODO: use ints
//#ifndef VERY_DESPERATE
                    //const size_t PRECISION=4;
                    uint32_t iii = /*(127-PRECISION)<<23|((i-0xA0000)>>(17-PRECISION));*/ *(uint32_t*)&co;
                    iii=0x1fbd1df5+(iii>>1);
                    iii=0x1fbd1df5+(iii>>1);
                    iii=0x1fbd1df5+(iii>>1);
                    co = *(float*)&iii;
//#endif
                    goto wsamp;
                }

                // 'rhythm' start:

                mask^=~mask;
                ih=i;
                if (!(ih<(uint16_t)0x1800)) mask^=mask;
                v=getm2(i,0x5FF)&mask;
                v&=mask;

                if (k<(uint8_t)0x12) goto wsamp;

                mask^=~mask;
                if (!((ih)>(uint16_t)0x8000&&(ih)<(uint16_t)0x9800)
#ifndef DESPERATE
                        ||(k==(uint8_t)0x23
#ifndef VERY_DESPERATE
                            &&ih>(uint16_t)0x8600
#endif
                        )
#endif
                    )mask^=mask;
                v+=getm2(i,0x3FF)&mask;
                if (k<(uint8_t)0x16) goto wsamp;

                mask^=~mask;
                if (!((ih)>(uint16_t)0xC000)
#ifndef DESPERATE
                        ||(k==(uint8_t)0x22
#ifndef VERY_DESPERATE
                            &&ih>(uint16_t)0xC600
#endif
                        )||k==(uint8_t)0x23
#endif
                    )mask^=mask;
                v+=getm2(i,0x2FF)&mask;

                if (k<(uint8_t)0x1A) goto wsamp;

#ifndef DESPERATE
                mask^=~mask;
                if (!((ih)>(uint16_t)0xE000)
#ifndef DESPERATE
                        ||k==(uint8_t)0x22||k==(uint8_t)0x23
#endif
                    )mask^=mask;
                v+=getm2(i,0x1FF)&mask;
#endif

                if (k<(uint8_t)0x1E) goto wsamp;

            endph:
                // feedbackdinges
                mask^=~mask;
                if (!(ih>(uint16_t)0x1000&&ih<(uint16_t)0x3000))mask^=mask;
                v_ = getm2(i,0x1FF)&mask;
                w=fbbuf[i&(FBBUFSZ-1)];
                w>>=1;w+=w>>1;
                ss=w+(ss*aa);
                w=(uint8_t)ss;
                if (k<(uint8_t)0x48)w+=v_;
                fbbuf[(i+fboff)&(FBBUFSZ-1)]=v_=w;

                if ((k<(uint8_t)0x28
#ifndef VERY_DESPERATE
                            // ka'e danfu
                            &&!(k==(uint8_t)0x27&&(ih<(uint16_t)0x4000))
#endif
                        )||k>(uint8_t)0x4A)goto wsamp;

                // jusyvru
                w=getm2(i,tonearr[(uint8_t)(i>>12)&(uint8_t)(sizeof(tonearr)-1)]<<2)^(((uint8_t)rds)&((uint8_t)i&(uint8_t)0x80));
                if(k>(uint8_t)0x48)w>>=1;
                co=aa;
                if (w)v>>=1;
                v+=w;

                if (k<(uint8_t)0x34||k>=(uint8_t)0x38) goto wsamp;

                co = (0.0000038146972656f) * (i - 0x340000); // TODO: use ints
                //uint32_t co_=119<<23|((i-0x340000)>>10);
                //co=*(float*)co_;
//#ifndef VERY_DESPERATE
                co*=co; // 2B
//#endif

            wsamp:
                w=v;
                v=(v>>1)+(v_>>1);
                // make it more gritty
                if(w
#ifndef VERY_DESPERATE
                        &&k<(uint8_t)0x48 // 5B iku'i ja'a se nitcu
#endif
                    ) {
                    uint8_t vv=0xFF,jj=i>>5;
                    jj&=0x3FF;v+=vv>>jj;
                    jj^=0x3FF;v+=vv>>jj;
                }

                // apply a global filter, then output the sample
                // don't convert v to unorm float,
                // because the math works out anyway
                zz=v+co*(zz-v);
                aubuf[j] = (uint8_t)zz;
#ifndef INFINITE_LOOP
                if (k>=(uint8_t)0x50){
#ifdef DISABLE_FBDEV
                    // reset color
                    /*const static char rst[]="\x1B[m\x1B" "c";
                    SYS_write(STDERR_FILENO,rst,sizeof rst);*/
#endif
                    SYS_int3();//goto outer;
                    __builtin_unreachable();
                }
#endif
            }
        }

#ifndef DISABLE_FBDEV
#endif

        //swap_buf(bmem, mem, sz);

        //sleep_60fps(); // screen flicker, who cares?
    }
//outer:

    // currently, it doesn't even exit
/*#if !defined(VERY_DESPERATE) && !defined(INFINITE_LOOP)
#ifndef DESPERATE
    snd_kill();
#ifndef DISABLE_FBDEV
    SYS_munmap(mem, sz);
    SYS_close(fb);
#endif
#endif

    //SYS_exit(0);
    SYS_int3(); // :D
#else
    __builtin_unreachable();
#endif*/
    __builtin_unreachable();
}

