/***********************************************************/
/*                                                         */
/*   BFONT.c v. 1.0.3 - Billi Font Library by Diego Billi  */
/*                                                         */
/***********************************************************/
^M
#include "stdio.h"^M
#include "string.h"
#include "stdlib.h"
#include "stdarg.h"

#include "SDL_image.h"

#include "BFont.h"

/* Current font */
BFont_Info *CurrentFont;

^M
/* utility functions */
Uint32 GetPixel(SDL_Surface *Surface, Sint32 X, Sint32 Y);
void   PutPixel(SDL_Surface *surface, int x, int y, Uint32 pixel);


void InitFont(BFont_Info *Font)
{
    int x = 0, i = 0;
    Uint32 sentry;

    i= '!';

    sentry = GetPixel(Font->Surface,0,0);
    /* sentry = SDL_MapRGB(Font->Surface->format, 255, 0, 255); */

    if ( Font->Surface==NULL ) {
        fprintf(stderr,"BFont: The font has not been loaded!\n");
        exit(1);
    }
    if (SDL_MUSTLOCK(Font->Surface)) SDL_LockSurface(Font->Surface);
    x=0;
    while ( x < (Font->Surface->w-1) ) {
       if(GetPixel(Font->Surface,x,0) != sentry) {
            Font->Chars[i].x = x;
            Font->Chars[i].y = 1;
            Font->Chars[i].h = Font->Surface->h;
            for (; GetPixel(Font->Surface, x, 0) != sentry && x < (Font->Surface->w); ++x) ;
            Font->Chars[i].w = (x - Font->Chars[i].x);
            i++;
       }
        else {
           x++;
        }
    }
    Font->Chars[' '].x = 0;
    Font->Chars[' '].y = 0;
    Font->Chars[' '].h = Font->Surface->h;
    Font->Chars[' '].w = Font->Chars['!'].w;

    if (SDL_MUSTLOCK(Font->Surface)) SDL_UnlockSurface(Font->Surface);

    Font->h = Font->Surface->h;

    SDL_SetColorKey(Font->Surface, SDL_SRCCOLORKEY, GetPixel(Font->Surface, 0, Font->Surface->h-1));
}


/* Load the font and stores it in the BFont_Info structure */
BFont_Info * LoadFont (char *filename)
{^M
    SDL_Surface *surface = NULL;
    int x;
   BFont_Info *Font=NULL;^M
^M
   if (filename != NULL) {^M
      Font = (BFont_Info *) malloc(sizeof(BFont_Info));^M
       if (Font != NULL) {
         surface = (SDL_Surface *) IMG_Load(filename);
         if (surface != NULL) {^M
            Font->Surface = surface;
            for (x=0; x<256; x++) {
               Font->Chars[x].x = 0;
               Font->Chars[x].y = 0;
               Font->Chars[x].h = 0;
               Font->Chars[x].w = 0;
            }
            /* Init the font */
            InitFont(Font);
            /* Set the font as the current font */
            SetCurrentFont(Font);
         }^M
         else {^M
            /* free memory allocated for the BFont_Info structure */^M
            free(Font);^M
            Font = NULL;^M
         }
       }^M
   }^M

   return Font;
}


void FreeFont(BFont_Info *Font)
{
    SDL_FreeSurface(Font->Surface);^M
   free(Font);
}

BFont_Info * SetFontColor(BFont_Info *Font,Uint8 r, Uint8 g, Uint8 b)
{
    int x,y;

    BFont_Info *newfont;
    SDL_Surface *surface = NULL;

    Uint32 pixel;
    Uint8 old_r, old_g, old_b;
    Uint8 new_r, new_g, new_b;
    Uint32 color_key;
^M
   newfont = (BFont_Info *) malloc(sizeof(BFont_Info));^M
   if (newfont != NULL) {^M

      newfont->h = Font->h;

      for (x=0; x<256; x++) {
         newfont->Chars[x].x = Font->Chars[x].x;
         newfont->Chars[x].y = Font->Chars[x].y;
         newfont->Chars[x].h = Font->Chars[x].h;
         newfont->Chars[x].w = Font->Chars[x].w;
      }

      surface = SDL_ConvertSurface(Font->Surface, Font->Surface->format, Font->Surface->flags);
      if (surface != NULL) {

         if (SDL_MUSTLOCK(surface))       SDL_LockSurface(surface);
         if (SDL_MUSTLOCK(Font->Surface)) SDL_LockSurface(Font->Surface);

         color_key =  GetPixel(surface, 0, surface->h-1);

         printf("looking...\n");
         for( x=0; x < Font->Surface->w; x++) {
            for( y=0; y < Font->Surface->h; y++) {
               old_r = old_g = old_b = 0;
               pixel = GetPixel(Font->Surface,x,y);

               if (pixel != color_key) {
                  SDL_GetRGB(pixel, surface->format, &old_r,&old_g,&old_b);

                  new_r = (Uint8) ((old_r * r) / 255);
                  new_g = (Uint8) ((old_g * g) / 255);
                  new_b = (Uint8) ((old_b * b) / 255);

                  pixel = SDL_MapRGB(surface->format,new_r,new_g,new_b);
                  PutPixel(surface,x,y,pixel);
               }
            }
         }
         printf("unlooking...\n");
         if (SDL_MUSTLOCK(surface))       SDL_UnlockSurface(surface);
         if (SDL_MUSTLOCK(Font->Surface)) SDL_UnlockSurface(Font->Surface);

         SDL_SetColorKey(surface, SDL_SRCCOLORKEY, color_key);
      }

      newfont->Surface = surface;
   }
    return newfont;
}

/* Set the current font */
void SetCurrentFont(BFont_Info *Font)
{
    CurrentFont = Font;
}

/* Returns the pointer to the current font strucure in use */
BFont_Info * GetCurrentFont(void)
{
    return CurrentFont;
}

/* Return the font height */
int FontHeight (BFont_Info *Font)
{
    return (Font->h);
}

void SetFontHeight(BFont_Info *Font, int height)
{
    Font->h = height;
}


/* Return the width of the "c" character */
int CharWidth(BFont_Info *Font,int c)
{
    return Font->Chars[c].w;
}

/* Puts a single char on the surface */
int PutChar(SDL_Surface *Surface, int x, int y, int c)
{^M
    return PutCharFont(Surface,  CurrentFont, x,y,c);
}

/* Puts a single char on the surface with the specified font */
int PutCharFont(SDL_Surface *Surface, BFont_Info *Font,int x, int y, int c)
{^M
    int r=0;
    SDL_Rect dest;

    dest.w = CharWidth(Font,' ');
    dest.h = FontHeight(Font);
    dest.x = x;
    dest.y = y;
    if (c != ' ') {
        SDL_BlitSurface( Font->Surface, &Font->Chars[c], Surface, &dest);
    }
    r = dest.w;
    return r;
}

void PutString(SDL_Surface *Surface, int x, int y, char *text)
{
    PutStringFont(Surface, CurrentFont, x, y, text);
}

void PutStringFont(SDL_Surface *Surface, BFont_Info *Font, int x, int y, char *text)
{
    int i=0;
    while (text[i]!='\0') {
        x  += PutCharFont(Surface,Font,x,y,text[i]);
        i++;
    }
}


int TextWidth(char *text)
{
    return TextWidthFont( CurrentFont, text);
}

int TextWidthFont(BFont_Info *Font, char *text)
{
    int i=0,x=0;

    while (text[i]!='\0') {
            x += CharWidth(Font,text[i]);
            i++;
    }
    return x;
}


/* counts the spaces of the strings */
int count (char *text)
{
    char *p = NULL;
    int pos = -1;
    int i   = 0;
    /* Calculate the space occupied by the text without spaces */
    while ((p=strchr(&text[pos+1],' ')) != NULL) {
            i++;
            pos = p - text;
    }
    return i;
}

void JustifiedPutString(SDL_Surface *Surface, int y, char *text)
{
    JustifiedPutStringFont( Surface, CurrentFont, y,text);
}

void JustifiedPutStringFont(SDL_Surface *Surface, BFont_Info *Font,  int y, char *text)
{
    int spaces = 0;
    int gap;
    int single_gap;
    int dif;

    char *strtmp;
    char *p;
    int pos = -1;
    int xpos = 0;


    if (strchr(text,' ') == NULL) {
        PutStringFont(Surface, Font, 0, y, text);
    }
    else {
        gap = (Surface->w-1) - TextWidthFont(Font,text);

        if (gap <= 0) {
            PutStringFont(Surface, Font,0,y,text);
        } else {
            spaces = count(text);
            dif = gap % spaces;
            single_gap = (gap - dif) / spaces;
            xpos=0;
            pos = -1;
            while ( spaces > 0 ) {
                p = strstr(&text[pos+1]," ");
                strtmp = NULL;
                strtmp = (char *) calloc ( (p - &text[pos+1]) + 1,sizeof(char));
                if (strtmp != NULL) {
                    strncpy (strtmp, &text[pos+1], (p - &text[pos+1]));
                    PutStringFont(Surface, Font, xpos, y, strtmp);
                    xpos = xpos + TextWidthFont(Font, strtmp) + single_gap + CharWidth(Font,' ');
                    if (dif >= 0) {
                        xpos ++;
                        dif--;
                    }
                    pos = p - text;
                    spaces--;
                    free(strtmp);
                }
            }
            strtmp = NULL;
            strtmp = (char *) calloc ( strlen( &text[pos+1]) + 1,sizeof(char));

            if (strtmp != NULL) {
                strncpy (strtmp, &text[pos+1], strlen( &text[pos+1]));
                PutStringFont(Surface, Font,xpos, y, strtmp);
                free(strtmp);
            }
        }
    }
}

void CenteredPutString(SDL_Surface *Surface, int y, char *text)
{
    CenteredPutStringFont(Surface, CurrentFont, y, text);
}

void CenteredPutStringFont(SDL_Surface *Surface, BFont_Info *Font, int y, char *text)
{
    PutStringFont(Surface, Font, Surface->w/2-TextWidthFont(Font,text)/2, y, text);
}

void RightPutString(SDL_Surface *Surface, int y, char *text)
{
    RightPutStringFont(Surface, CurrentFont, y, text);
}

void RightPutStringFont(SDL_Surface *Surface, BFont_Info *Font, int y, char *text)
{
    PutStringFont(Surface, Font, Surface->w - TextWidthFont(Font,text) - 1, y, text);
}

void LeftPutString(SDL_Surface *Surface, int y, char *text)
{
    LeftPutStringFont(Surface, CurrentFont, y, text);
}

void LeftPutStringFont(SDL_Surface *Surface, BFont_Info *Font, int y, char *text)
{
    PutStringFont(Surface, Font, 0, y, text);
}

/******/

void PrintString (SDL_Surface *Surface, int x, int y, char *fmt, ...)
{
    va_list args;
    char *temp;
    va_start (args,fmt);

    if ( (temp = (char *) malloc(1000+1)) != NULL) {
        vsprintf(temp,fmt,args);

        PutStringFont(Surface, CurrentFont, x, y, temp);

        free (temp);
    }
    va_end(args);
}

void PrintStringFont(SDL_Surface *Surface, BFont_Info *Font, int x, int y, char *fmt, ...)
{
    va_list args;
    char *temp;
    va_start (args,fmt);

    if ( (temp = (char *) malloc(1000+1)) != NULL) {
        vsprintf(temp,fmt,args);
        PutStringFont(Surface, Font, x, y, temp);
        free (temp);
    }
    va_end(args);
}

void CenteredPrintString(SDL_Surface *Surface, int y,  char *fmt, ...)
{
    va_list args;
    char *temp;
    va_start (args,fmt);

    if ( (temp = (char *) malloc(1000+1)) != NULL) {
        vsprintf(temp,fmt,args);
        CenteredPutString(Surface, y, temp);
        free (temp);
    }
    va_end(args);
}

void CenteredPrintStringFont(SDL_Surface *Surface, BFont_Info *Font, int y,  char *fmt, ...)
{
    va_list args;
    char *temp;
    va_start (args,fmt);

    if ( (temp = (char *) malloc(1000+1)) != NULL) {
        vsprintf(temp,fmt,args);
        CenteredPutStringFont(Surface, Font, y, temp);
        free (temp);
    }
    va_end(args);

}

void RightPrintString(SDL_Surface *Surface, int y, char *fmt, ...)
{
    va_list args;
    char *temp;
    va_start (args,fmt);

    if ( (temp = (char *) malloc(1000+1)) != NULL) {
        vsprintf(temp,fmt,args);
        RightPutString(Surface, y, temp);
        free (temp);
    }
    va_end(args);
}

void RightPrintStringFont(SDL_Surface *Surface, BFont_Info *Font, int y,  char *fmt, ...)
{
    va_list args;
    char *temp;
    va_start (args,fmt);

    if ( (temp = (char *) malloc(1000+1)) != NULL) {
        vsprintf(temp,fmt,args);
        RightPutStringFont(Surface, Font, y, temp);
        free (temp);
    }
    va_end(args);
}

void LeftPrintString(SDL_Surface *Surface, int y, char *fmt, ...)
{
    va_list args;
    char *temp;
    va_start (args,fmt);

    if ( (temp = (char *) malloc(1000+1)) != NULL) {
        vsprintf(temp,fmt,args);
        LeftPutString(Surface, y, temp);
        free (temp);
    }
    va_end(args);
}

void LeftPrintStringFont(SDL_Surface *Surface, BFont_Info *Font, int y,  char *fmt, ...)
{
    va_list args;
    char *temp;
    va_start (args,fmt);

    if ( (temp = (char *) malloc(1000+1)) != NULL) {
        vsprintf(temp,fmt,args);
        LeftPutStringFont(Surface, Font, y, temp);
        free (temp);
    }
    va_end(args);
}

void JustifiedPrintString(SDL_Surface *Surface, int y, char *fmt, ...)
{
    va_list args;
    char *temp;
    va_start (args,fmt);

    if ( (temp = (char *) malloc(1000+1)) != NULL) {
        vsprintf(temp,fmt,args);
        JustifiedPutString( Surface,  y,temp);
        free (temp);
    }
    va_end(args);
}

void JustifiedPrintStringFont(SDL_Surface *Surface, BFont_Info *Font,  int y, char *fmt, ...)
{
    va_list args;
    char *temp;
    va_start (args,fmt);

    if ( (temp = (char *) malloc(1000+1)) != NULL) {
        vsprintf(temp,fmt,args);
        JustifiedPutStringFont( Surface, Font, y,temp);
        free (temp);
    }
    va_end(args);
}


/*********************************************************************************************************/
/*********************************************************************************************************/
/*********************************************************************************************************/

void   PutPixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
    int bpp = surface->format->BytesPerPixel;
    /* Here p is the address to the pixel we want to set */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {
    case 1:
        *p = pixel;
        break;

    case 2:
        *(Uint16 *)p = pixel;
        break;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;
        } else {
            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;
        }
        break;

    case 4:
        *(Uint32 *)p = pixel;
        break;
    }
}

Uint32 GetPixel(SDL_Surface *Surface, Sint32 X, Sint32 Y)
{

   Uint8  *bits;
   Uint32 Bpp;

   if (X<0) puts("x too small in GetPixel!");
   if (X>=Surface->w) puts("x too big in GetPixel!");

   Bpp = Surface->format->BytesPerPixel;

   bits = ((Uint8 *)Surface->pixels)+Y*Surface->pitch+X*Bpp;

   // Get the pixel
   switch(Bpp) {
      case 1:
         return *((Uint8 *)Surface->pixels + Y * Surface->pitch + X);
         break;
      case 2:
         return *((Uint16 *)Surface->pixels + Y * Surface->pitch/2 + X);
         break;
      case 3: { // Format/endian independent
         Uint8 r, g, b;
         r = *((bits)+Surface->format->Rshift/8);
         g = *((bits)+Surface->format->Gshift/8);
         b = *((bits)+Surface->format->Bshift/8);
         return SDL_MapRGB(Surface->format, r, g, b);
         }
         break;
      case 4:
         return *((Uint32 *)Surface->pixels + Y * Surface->pitch/4 + X);
         break;
   }

    return -1;
}