#pragma once


#include <stdarg.h>
#include "malloc.h"
#include "../common.h"
#include "../kbd.h"
#include "../print.h"

#define MAX_LINE_LEN 256

static inline int puts(const char *s) {
    print(s);
    put('\n');
    return 0;
}

#define _STDIO_PEEK_BUFF_EMPTY -1
static int _stdio_peek_buf = _STDIO_PEEK_BUFF_EMPTY;
/* getc – blocking, consumes one character */
static inline int getc(void) {
    if (_stdio_peek_buf >= 0) {
        int c = _stdio_peek_buf;
        _stdio_peek_buf = -1;
        return c;
    }
    return kbd_getc_blocking();
}

/* peek – blocking, returns next char WITHOUT consuming it */
static inline int peek(void) {
    if (_stdio_peek_buf < 0)
        _stdio_peek_buf = kbd_getc_blocking();
    return _stdio_peek_buf;
}

/* getc_nb – non-blocking version; returns -1 if no char ready */
static inline int getc_nb(void) {
    if (_stdio_peek_buf >= 0) {
        int c = _stdio_peek_buf;
        _stdio_peek_buf = _STDIO_PEEK_BUFF_EMPTY;
        return c;
    }
    int c = kbd_buffer_getc();
    return c ? c : _STDIO_PEEK_BUFF_EMPTY;
}

/* peek_nb – non-blocking peek; returns -1 if no char ready */
static inline int peek_nb(void) {
    if (_stdio_peek_buf < 0) {
        int c = kbd_buffer_getc();
        if (!c) return -1;
        _stdio_peek_buf = c;
    }
    return _stdio_peek_buf;
}

static inline char *getline_alloc(size_t max_hint) {
    size_t cap = max_hint > 0 ? max_hint : 128;
    char  *buf = (char *)malloc(cap);
    if (!buf) {
		print("getline_alloc failed to alloc! :(\n");
		return NULL;
	}

    size_t len = 0;

    for (;;) {
        int c = getc();



        if (c == '\b') {   /* backspace */
            if (len > 0) {
                len--;
                unput();
            }
            continue;
        }

        /* grow buffer if needed (leave room for NUL) */
        if (len + 1 >= cap) {
            size_t new_cap = cap * 2;
            char  *new_buf = (char *)malloc(new_cap);
            if (!new_buf) { free(buf); print("getline_alloc failed to alloc! :(\n"); return NULL; }
            for (size_t i = 0; i < len; i++) new_buf[i] = buf[i];
            free(buf);
            buf = new_buf;
            cap = new_cap;
        }

        buf[len++] = (char)c;
		if (c == '\n' || c == '\r') {
            put('\n');
            break;
        } else {
        	put(c);   /* echo */
		}
    }

    buf[len] = '\0';
    return buf;
}

static inline int getline_buf(char *buf, size_t n) {
    if (!buf || n == 0) return -1;
    size_t len = 0;

    for (;;) {
        int c = getc();

        if (c == '\n' || c == '\r') {
            put('\n');
            break;
        }

        if (c == '\b') {
            if (len > 0) { len--; unput(); }
            continue;
        }

        if (len < n - 1) {
            buf[len++] = (char)c;
            put(c);
        }
        /* else: buffer full – keep echoing but discard */
    }

    buf[len] = '\0';
    return (int)len;
}


static inline char *_fmt_uint(char *end, u64 val, int base, bool upper) {
    static const char lo[] = "0123456789abcdef";
    static const char up[] = "0123456789ABCDEF";
    const char *digs = upper ? up : lo;
    *--end = '\0';
    if (val == 0) { *--end = '0'; return end; }
    while (val) { *--end = digs[val % (u64)base]; val /= (u64)base; }
    return end;
}

static inline int vprintf(const char *fmt, va_list ap) {
    int written = 0;

#define _EMIT(c)  do { put(c); written++; } while(0)
#define _EMITS(s) do { const char *_p=(s); while(*_p){_EMIT(*_p);_p++;} } while(0)

    while (*fmt) {
        if (*fmt != '%') { _EMIT(*fmt++); continue; }
        fmt++;  /* skip '%' */

        /* flags */
        bool left_align = false;
        bool zero_pad   = false;
        if (*fmt == '-') { left_align = true; fmt++; }
        if (*fmt == '0') { zero_pad   = true; fmt++; }

        /* width */
        int width = 0;
        while (*fmt >= '0' && *fmt <= '9') width = width*10 + (*fmt++ - '0');

        /* length modifier */
        int lng = 0;  /* 0=int, 1=long, 2=long long */
        if (*fmt == 'l') { lng=1; fmt++; }
        if (*fmt == 'l') { lng=2; fmt++; }

        char  tmp[66];
        char *s    = NULL;
        char  cbuf[2] = {0,0};
        bool  neg  = false;

        switch (*fmt++) {
        case '%':
            _EMIT('%');
            continue;

        case 'c':
            cbuf[0] = (char)va_arg(ap, int);
            s = cbuf;
            break;

        case 's':
            s = va_arg(ap, char *);
            if (!s) s = "(null)";
            break;

        case 'd': case 'i': {
            i64 v = (lng==2) ? va_arg(ap,long long) :
                    (lng==1) ? va_arg(ap,long)       :
                               va_arg(ap,int);
            if (v < 0) { neg = true; v = -v; }
            s = _fmt_uint(tmp+65, (u64)v, 10, false);
            break;
        }

        case 'u': {
            u64 v = (lng==2) ? va_arg(ap,unsigned long long) :
                    (lng==1) ? va_arg(ap,unsigned long)       :
                               va_arg(ap,unsigned int);
            s = _fmt_uint(tmp+65, v, 10, false);
            break;
        }

        case 'o': {
            u64 v = (lng==2) ? va_arg(ap,unsigned long long) :
                    (lng==1) ? va_arg(ap,unsigned long)       :
                               va_arg(ap,unsigned int);
            s = _fmt_uint(tmp+65, v, 8, false);
            break;
        }

        case 'x': {
            u64 v = (lng==2) ? va_arg(ap,unsigned long long) :
                    (lng==1) ? va_arg(ap,unsigned long)       :
                               va_arg(ap,unsigned int);
            s = _fmt_uint(tmp+65, v, 16, false);
            break;
        }

        case 'X': {
            u64 v = (lng==2) ? va_arg(ap,unsigned long long) :
                    (lng==1) ? va_arg(ap,unsigned long)       :
                               va_arg(ap,unsigned int);
            s = _fmt_uint(tmp+65, v, 16, true);
            break;
        }

        case 'p': {
            u64 v = (u64)(uintptr_t)va_arg(ap, void *);
            print("0x");
            written += 2;
            s = _fmt_uint(tmp+65, v, 16, false);
            break;
        }

        default:
            _EMIT('?');
            continue;
        }

        /* padding / output */
        int slen = 0;
        for (const char *p = s; *p; p++) slen++;
        if (neg) slen++;  /* for '-' */

        int pad = width - slen;

        if (!left_align && pad > 0) {
            char pc = zero_pad ? '0' : ' ';
            if (neg && zero_pad) { _EMIT('-'); neg=false; written++; }
            while (pad-- > 0) { _EMIT(pc); written++; }
        }
        if (neg) { _EMIT('-'); written++; }
        while (*s) { _EMIT(*s++); written++; }
        if (left_align && pad > 0)
            while (pad-- > 0) { _EMIT(' '); written++; }
    }

#undef _EMIT
#undef _EMITS
    return written;
}

static inline int printf(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    int r = vprintf(fmt, ap);
    va_end(ap);
    return r;
}


typedef struct { char *p; size_t rem; } _sbuf;

/* We re-implement the inner loop targeting a string buffer instead of the
 * screen.  Factoring out a shared back-end with a function pointer would be
 * cleaner but adds complexity; this keeps it self-contained. */
static inline int vsnprintf(char *out, size_t size, const char *fmt, va_list ap) {
    if (!out || !size) return 0;
    char *cur = out;
    char *end = out + size - 1;  /* leave room for NUL */
    int written = 0;

#define _SEMIT(c)  do { if(cur<end){*cur++=(char)(c);} written++; } while(0)

    while (*fmt) {
        if (*fmt != '%') { _SEMIT(*fmt++); continue; }
        fmt++;

        bool left_align=false, zero_pad=false;
        if (*fmt=='-'){left_align=true;fmt++;}
        if (*fmt=='0'){zero_pad=true;fmt++;}
        int width=0;
        while(*fmt>='0'&&*fmt<='9') width=width*10+(*fmt++-'0');
        int lng=0;
        if(*fmt=='l'){lng=1;fmt++;}
        if(*fmt=='l'){lng=2;fmt++;}

        char  tmp[66];
        char *s=NULL;
        char  cbuf[2]={0,0};
        bool  neg=false;

        switch(*fmt++) {
        case '%': _SEMIT('%'); continue;
        case 'c': cbuf[0]=(char)va_arg(ap,int); s=cbuf; break;
        case 's': s=va_arg(ap,char*); if(!s)s="(null)"; break;
        case 'd': case 'i': {
            i64 v=(lng==2)?va_arg(ap,long long):(lng==1)?va_arg(ap,long):va_arg(ap,int);
            if(v<0){neg=true;v=-v;}
            s=_fmt_uint(tmp+65,(u64)v,10,false); break;
        }
        case 'u': {
            u64 v=(lng==2)?va_arg(ap,unsigned long long):(lng==1)?va_arg(ap,unsigned long):va_arg(ap,unsigned int);
            s=_fmt_uint(tmp+65,v,10,false); break;
        }
        case 'o': {
            u64 v=(lng==2)?va_arg(ap,unsigned long long):(lng==1)?va_arg(ap,unsigned long):va_arg(ap,unsigned int);
            s=_fmt_uint(tmp+65,v,8,false); break;
        }
        case 'x': {
            u64 v=(lng==2)?va_arg(ap,unsigned long long):(lng==1)?va_arg(ap,unsigned long):va_arg(ap,unsigned int);
            s=_fmt_uint(tmp+65,v,16,false); break;
        }
        case 'X': {
            u64 v=(lng==2)?va_arg(ap,unsigned long long):(lng==1)?va_arg(ap,unsigned long):va_arg(ap,unsigned int);
            s=_fmt_uint(tmp+65,v,16,true); break;
        }
        case 'p': {
            u64 v=(u64)(uintptr_t)va_arg(ap,void*);
            _SEMIT('0'); _SEMIT('x'); written+=2;
            s=_fmt_uint(tmp+65,v,16,false); break;
        }
        default: _SEMIT('?'); continue;
        }

        int slen=0; for(const char*p=s;*p;p++) slen++; if(neg) slen++;
        int pad=width-slen;
        if(!left_align&&pad>0){char pc=zero_pad?'0':' '; if(neg&&zero_pad){_SEMIT('-');neg=false;} while(pad-->0){_SEMIT(pc);}}
        if(neg){_SEMIT('-');}
        while(*s){_SEMIT(*s++);}
        if(left_align&&pad>0) while(pad-->0){_SEMIT(' ');}
    }
#undef _SEMIT
    *cur = '\0';
    return written;
}

static inline int snprintf(char *out, size_t size, const char *fmt, ...) {
    va_list ap; va_start(ap, fmt);
    int r = vsnprintf(out, size, fmt, ap);
    va_end(ap); return r;
}

static inline int sprintf(char *out, const char *fmt, ...) {
    va_list ap; va_start(ap, fmt);
    /* pass a huge size – caller's responsibility to have enough room */
    int r = vsnprintf(out, (size_t)-1, fmt, ap);
    va_end(ap); return r;
}

static inline int _skip_ws(const char **p) {
    while (**p==' '||**p=='\t'||**p=='\n'||**p=='\r') (*p)++;
    return 0;
}

static inline int vsscanf(const char *str, const char *fmt, va_list ap) {
    const char *s = str;
    int matched = 0;

    while (*fmt) {
        if (*fmt == ' ') { _skip_ws(&s); fmt++; continue; }
        if (*fmt != '%') {
            if (*s != *fmt) return matched;
            s++; fmt++; continue;
        }
        fmt++;  /* skip '%' */
        if (*fmt == '%') { if (*s=='%') s++; fmt++; continue; }

        int lng = 0;
        if (*fmt=='l'){lng=1;fmt++;}
        if (*fmt=='l'){lng=2;fmt++;}

        _skip_ws(&s);

        switch (*fmt++) {
        case 'c': {
            char *out = va_arg(ap, char*);
            if (!*s) return matched;
            *out = *s++;
            matched++;
            break;
        }
        case 's': {
            char *out = va_arg(ap, char*);
            if (!*s) return matched;
            while (*s && *s!=' '&&*s!='\t'&&*s!='\n'&&*s!='\r')
                *out++ = *s++;
            *out = '\0';
            matched++;
            break;
        }
        case 'd': case 'i': {
            bool neg2 = false;
            if (*s=='-'){neg2=true;s++;}
            else if(*s=='+') s++;
            if (*s<'0'||*s>'9') return matched;
            i64 v=0;
            while(*s>='0'&&*s<='9') v=v*10+(*s++-'0');
            if(neg2) v=-v;
            if(lng==2) *va_arg(ap,long long*)=(long long)v;
            else if(lng==1) *va_arg(ap,long*)=(long)v;
            else *va_arg(ap,int*)=(int)v;
            matched++;
            break;
        }
        case 'u': {
            if(*s<'0'||*s>'9') return matched;
            u64 v=0;
            while(*s>='0'&&*s<='9') v=v*10+(*s++-'0');
            if(lng==2) *va_arg(ap,unsigned long long*)=(unsigned long long)v;
            else if(lng==1) *va_arg(ap,unsigned long*)=(unsigned long)v;
            else *va_arg(ap,unsigned int*)=(unsigned int)v;
            matched++;
            break;
        }
        case 'x': case 'X': {
            if(*s=='0'&&(*(s+1)=='x'||*(s+1)=='X')) s+=2;
            u64 v=0; bool any=false;
            while((*s>='0'&&*s<='9')||(*s>='a'&&*s<='f')||(*s>='A'&&*s<='F')){
                u8 d=(*s>='a')?*s-'a'+10:(*s>='A')?*s-'A'+10:*s-'0';
                v=v*16+d; s++; any=true;
            }
            if(!any) return matched;
            if(lng==2) *va_arg(ap,unsigned long long*)=(unsigned long long)v;
            else if(lng==1) *va_arg(ap,unsigned long*)=(unsigned long)v;
            else *va_arg(ap,unsigned int*)=(unsigned int)v;
            matched++;
            break;
        }
        case 'o': {
            if(*s<'0'||*s>'7') return matched;
            u64 v=0;
            while(*s>='0'&&*s<='7') v=v*8+(*s++-'0');
            if(lng==2) *va_arg(ap,unsigned long long*)=(unsigned long long)v;
            else if(lng==1) *va_arg(ap,unsigned long*)=(unsigned long)v;
            else *va_arg(ap,unsigned int*)=(unsigned int)v;
            matched++;
            break;
        }
        default:
            return matched;
        }
    }
    return matched;
}

static inline int sscanf(const char *str, const char *fmt, ...) {
    va_list ap; va_start(ap, fmt);
    int r = vsscanf(str, fmt, ap);
    va_end(ap); return r;
}


static inline int scanf(const char *fmt, ...) {
    char *line = getline_alloc(MAX_LINE_LEN);
    if (!line) return -1;
    va_list ap; va_start(ap, fmt);
    int r = vsscanf(line, fmt, ap);
    va_end(ap);
    free(line);
    return r;
}
