#include "common.h"

// Notification toast state
static char  notifMsg[128] = {0};
static float notifTimer    = 0.f;

// ---- Automatic dedupe + alphabetical sort for simple list .txt files ----

static int DedupeSortStrCmpCI(const char *a, const char *b)
{
    while(*a && *b){
        char ca=*a, cb=*b;
        if(ca>='A'&&ca<='Z') ca=(char)(ca+32);
        if(cb>='A'&&cb<='Z') cb=(char)(cb+32);
        if(ca!=cb) return (unsigned char)ca-(unsigned char)cb;
        a++; b++;
    }
    return (unsigned char)*a-(unsigned char)*b;
}

#define DEDUPE_MAX_LINES 2048
#define DEDUPE_MAX_LEN    128

static int DedupeSortRowCmp(const void *a, const void *b)
{
    return DedupeSortStrCmpCI((const char*)a,(const char*)b);
}

void DedupeSortTextFile(const char *path)
{
    FILE *f=fopen(path,"r");
    if(!f) return;

    static char header[4096];
    static char lines[DEDUPE_MAX_LINES][DEDUPE_MAX_LEN];
    static char unique[DEDUPE_MAX_LINES][DEDUPE_MAX_LEN];
    header[0]='\0';
    int lineCount=0;
    bool inHeader=true;
    char raw[256];

    while(fgets(raw,sizeof(raw),f)){
        char *p=raw; while(*p==' '||*p=='\t') p++;
        bool isCommentOrBlank = (*p=='#' || *p=='\n' || *p=='\r' || *p=='\0');
        if(inHeader && isCommentOrBlank){
            strncat(header,raw,sizeof(header)-strlen(header)-1);
            continue;
        }
        inHeader=false;

        int n=(int)strlen(raw);
        while(n>0 && (raw[n-1]=='\n'||raw[n-1]=='\r'||raw[n-1]==' '||raw[n-1]=='\t')) raw[--n]='\0';
        char *q=raw; while(*q==' '||*q=='\t') q++;
        if(*q=='\0') continue;   // drop stray blank lines mixed into the body

        if(lineCount<DEDUPE_MAX_LINES){
            strncpy(lines[lineCount],q,DEDUPE_MAX_LEN-1);
            lines[lineCount][DEDUPE_MAX_LEN-1]='\0';
            lineCount++;
        }
    }
    fclose(f);

    int uniqueCount=0;
    for(int i=0;i<lineCount;i++){
        bool dup=false;
        for(int j=0;j<uniqueCount;j++) if(DedupeSortStrCmpCI(lines[i],unique[j])==0){ dup=true; break; }
        if(!dup){ strncpy(unique[uniqueCount],lines[i],DEDUPE_MAX_LEN-1); unique[uniqueCount][DEDUPE_MAX_LEN-1]='\0'; uniqueCount++; }
    }

    qsort(unique,uniqueCount,DEDUPE_MAX_LEN,DedupeSortRowCmp);

    FILE *out=fopen(path,"w");
    if(!out) return;
    if(strlen(header)) fputs(header,out);
    for(int i=0;i<uniqueCount;i++) fprintf(out,"%s\n",unique[i]);
    fclose(out);
}

void safe_strncpy(char *dst, const char *src, size_t dstsize)
{
    if(dstsize==0) return;
    strncpy(dst, src, dstsize-1);
    dst[dstsize-1]='\0';
}

void ShowNotif(const char *msg)
{
    strncpy(notifMsg, msg, sizeof(notifMsg) - 1);
    notifMsg[sizeof(notifMsg)-1] = '\0';
    notifTimer = 2.5f;
}

// Notification toast overlay — visible on all tabs
void DrawNotifToast(float dt)
{
    if(notifTimer<=0) return;
    notifTimer-=dt;
    float alpha=notifTimer>0.4f?1.f:notifTimer/0.4f;
    float slide=notifTimer>2.2f?(2.5f-notifTimer)/0.3f:1.f;
    if(slide<0) { slide=0; }
    if(slide>1) { slide=1; }
    int nw=MeasureText(notifMsg,14)+26;
    int nx=900-nw-14;
    int ny=(int)(558-(slide*44));
    DrawRectangleRounded((Rectangle){(float)nx,(float)ny,(float)nw,34},0.35f,6,
        (Color){Q_NOTIF.r,Q_NOTIF.g,Q_NOTIF.b,(unsigned char)(165*alpha)});
    DrawText(notifMsg,nx+13,ny+9,14,(Color){255,255,255,(unsigned char)(255*alpha)});
}

// Drawing primitives
Color Brighten(Color c, float f)
{
    return (Color){
        (unsigned char)fminf(255.f,c.r*f),
        (unsigned char)fminf(255.f,c.g*f),
        (unsigned char)fminf(255.f,c.b*f),
        c.a
    };
}

void Panel(Rectangle r, Color bg, Color border)
{
    DrawRectangleRounded(r,0.06f,8,bg);
    DrawRectangleRoundedLinesEx(r,0.06f,8,1.5f,border);
}

// Small badge pill font-12, used by the tab bar
void Badge(int x, int y, const char *txt, Color bg, Color fg)
{
    int tw=MeasureText(txt,12);
    DrawRectangleRounded((Rectangle){(float)x,(float)y,(float)(tw+16),20.f},0.5f,8,bg);
    DrawText(txt,x+8,y+4,12,fg);
}

// Small badge pill font-13, used by the Classes panel
void ClsBadge(int x, int y, const char *txt, Color bg, Color fg)
{
    int tw=MeasureText(txt,13);
    DrawRectangleRounded((Rectangle){(float)x,(float)y,(float)(tw+16),22.f},0.5f,8,bg);
    DrawText(txt,x+8,y+4,13,fg);
}

// Full-size button — returns true on click
bool Btn(Rectangle r, const char *txt, Color bg, Color fg)
{
    bool hov  = CheckCollisionPointRec(GetMousePosition(), r);
    bool down = hov && IsMouseButtonDown(MOUSE_LEFT_BUTTON);
    Color c = down?Brighten(bg,0.78f):hov?Brighten(bg,1.28f):bg;
    DrawRectangleRounded(r,0.18f,8,c);
    int fs=16, tw=MeasureText(txt,fs);
    DrawText(txt,(int)(r.x+(r.width-(float)tw)/2.f),(int)(r.y+(r.height-(float)fs)/2.f),fs,fg);
    return hov && IsMouseButtonPressed(MOUSE_LEFT_BUTTON);
}

// Smaller button — returns true on click
bool SmallBtn(Rectangle r, const char *txt, Color bg, Color fg)
{
    bool hov = CheckCollisionPointRec(GetMousePosition(), r);
    Color c  = hov?Brighten(bg,1.28f):bg;
    DrawRectangleRounded(r,0.20f,8,c);
    int fs=13, tw=MeasureText(txt,fs);
    DrawText(txt,(int)(r.x+(r.width-(float)tw)/2.f),(int)(r.y+(r.height-(float)fs)/2.f),fs,fg);
    return hov && IsMouseButtonPressed(MOUSE_LEFT_BUTTON);
}

void ProgBar(Rectangle r, float pct, Color fill)
{
    DrawRectangleRounded(r,0.5f,8,C_PANEL3);
    if(pct>0.001f){
        Rectangle f={r.x,r.y,r.width*fminf(pct,1.f),r.height};
        DrawRectangleRounded(f,0.5f,8,fill);
    }
}

void HDivider(int x, int y, int w)
{
    DrawLineEx((Vector2){(float)x,(float)y},(Vector2){(float)(x+w),(float)y},1.f,C_BORDER);
}

void DrawXMark(Rectangle box, float thickness, Color color)
{
    float pad=box.width*0.28f;
    float x1=box.x+pad,        y1=box.y+pad;
    float x2=box.x+box.width-pad, y2=box.y+box.height-pad;
    DrawLineEx((Vector2){x1,y1},(Vector2){x2,y2},thickness,color);
    DrawLineEx((Vector2){x1,y2},(Vector2){x2,y1},thickness,color);
}

// TBox input widget
void TBoxTick(TBox *t, Rectangle r)
{
    if(IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
        t->active = CheckCollisionPointRec(GetMousePosition(), r);
    if(!t->active) return;
    for(int k=GetCharPressed(); k; k=GetCharPressed()){
        int n=(int)strlen(t->buf);
        if(k>=32 && n<(int)sizeof(t->buf)-1){ t->buf[n]=(char)k; t->buf[n+1]='\0'; }
    }
    if(IsKeyPressed(KEY_BACKSPACE) && strlen(t->buf)>0)
        t->buf[strlen(t->buf)-1]='\0';
    if(IsKeyPressed(KEY_ESCAPE)) t->active=false;
}

void TBoxDraw(const TBox *t, Rectangle r, const char *ph)
{
    DrawRectangleRounded(r, 0.12f, 8, C_INPUT);
    DrawRectangleRoundedLinesEx(r, 0.12f, 8, 1.5f, t->active?C_BLUE:C_BORDER);
    bool has=(strlen(t->buf)>0);
    DrawText(has?t->buf:ph, (int)r.x+10, (int)(r.y+(r.height-18.f)/2.f), 18, has?C_TEXT:C_MUTED);
    if(t->active && has && (int)(GetTime()*2.0)%2==0){
        int tw=MeasureText(t->buf,18);
        DrawRectangle((int)r.x+11+tw, (int)(r.y+(r.height-18.f)/2.f), 2, 18, C_TEXT);
    }
}
