// this is heavily based on HelloGhoti by
// Frederik De Kegel

#ifndef __fbrender_cpp__
#define __fbrender_cpp__

void FbRender_Open();
void FbRender_Close();
void FbRender_Flush();


#ifdef BARCELONA_TARGET

  #define FB_DEVICE_NAME "/dev/ts"
  static int fbfd = -1;
  static struct fb_var_screeninfo vinfo;
  static struct fb_fix_screeninfo finfo;
  static long int screensize = 0;
  static char *fbp = 0;
  static char *fbbackp = 0;
  inline unsigned short Color(int r, int g, int b)
  {
    return (b>>3) + ((g>>2)*32) + ((r>>3)*2048);// (gray>>2)<<5;
  }
  #define BLACK  0x0000
  #define WHITE  0xffff
  #define RED    0xf000
  #define GREEN  0x0f00
  #define BLUE   0x00f0
  #define YELLOW 0xff00
  #define CharWidth    8


  inline int FbRender_ScreenWidth() { return vinfo.xres; }
  inline int FbRender_ScreenHeight() { return vinfo.yres; }
  inline int FbRender_Bpp() { return vinfo.bits_per_pixel; }

#endif // BARCELONA_TARGET


void FbRender_Open();
void FbRender_Close();
void FbRender_Clear(int aFromY, int aNrLines, unsigned short aColor);
void FbRender_BlitCharacter(int x, int y, unsigned short aColor, unsigned short aBackColor, char character);
void FbRender_BlitCharacterItalic(int x, int y, unsigned short aColor, unsigned short aBackColor, char character);
void FbRender_BlitText(int x, int y, unsigned short aColor, unsigned short aBackColor, char *string);
void FbRender_BlitTextItalic(int x, int y, unsigned short aColor, unsigned short aBackColor, char *string);


void FbRender_Open()
{
#ifdef BARCELONA_TARGET
  fbfd = open("/dev/fb", O_RDWR);

  if (!fbfd) 
  {
    printf("Error: could not open framebuffer device.\n");
    exit(1);
  }

  // printf("framebuffer device successfull opened.\n");
  // Get fixed screen information
  if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) 
  {
    printf("Fout bij het lezen van de vaste informatie.\n");
    exit(2);
  }

  // Get variable screen information
  if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) 
  {
    printf("Fout bij het lezen van de variabele informatie.\n");
    exit(3);
  }

  printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel );

  // Figure out the size of the screen in bytes
  screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

  // Map the device to memory
  fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
  if ((int)fbp == -1) 
  {
    printf("Fout: gefaald bij het mappen van de framebuffer device in het geheugen.\n");
    exit(4);
  }
  fbbackp = (char*)malloc(screensize);
  printf("De framebuffer device is succesvol gemapped in het geheugen.\n");
#endif // BARCELONA_TARGET
}
void FbRender_Close()
{
#ifdef BARCELONA_TARGET
  if (fbfd > 0)
  {
    munmap(fbp, screensize);
    close(fbfd);
  }
  if (fbbackp) free(fbbackp);
  fbfd = -1;
#endif // BARCELONA_TARGET
}



void FbRender_Clear(int aFromY, int aNrLines, unsigned short aColor)
{
#ifdef BARCELONA_TARGET
  unsigned short *ptr  = (unsigned short*)(fbbackp+aFromY*((vinfo.xres*vinfo.bits_per_pixel)/8));
  unsigned short *endp = ptr + aNrLines * ((vinfo.xres*vinfo.bits_per_pixel)/16);

  while (ptr < endp)
  {
    *ptr++ = aColor;
    *ptr++ = aColor;
    *ptr++ = aColor;
    *ptr++ = aColor;
  }
#endif // BARCELONA_TARGET
}


#ifdef BARCELONA_TARGET
inline void FbRender_PutPixel(int x, int y, unsigned short color)
{
  unsigned short *ptr  = (unsigned short*)(fbbackp+y*((vinfo.xres*vinfo.bits_per_pixel)/8));
  ptr+=x;
  *ptr = color;
}
#endif // BARCELONA_TARGET


#ifdef BARCELONA_TARGET

// Character set
static const unsigned char asciiTable[]=
{
	0x00,0x00,0x00,0x00,0x00,//0x00,
	0x00,0x00,0x5F,0x00,0x00,//0x00,
	0x00,0x07,0x00,0x07,0x00,//0x00,
	0x14,0x7F,0x14,0x7F,0x14,//0x00,
	0x24,0x2A,0x7F,0x2A,0x12,//0x00,
	0x23,0x13,0x08,0x64,0x62,//0x00,
	0x36,0x49,0x55,0x22,0x50,//0x00,
	0x00,0x05,0x03,0x00,0x00,//0x00,
	0x00,0x1C,0x22,0x41,0x00,//0x00,
	0x00,0x41,0x22,0x1C,0x00,//0x00,
	0x14,0x08,0x3E,0x08,0x14,//0x00,
	0x08,0x08,0x3E,0x08,0x08,//0x00,
	0x00,0x50,0x30,0x00,0x00,//0x00,
	0x08,0x08,0x08,0x08,0x08,//0x00,
	0x00,0x60,0x60,0x00,0x00,//0x00,
	0x20,0x10,0x08,0x04,0x02,//0x00,
	0x3E,0x51,0x49,0x45,0x3E,//0x00,
	0x00,0x42,0x7F,0x40,0x00,//0x00,
	0x42,0x61,0x51,0x49,0x46,//0x00,
	0x21,0x41,0x45,0x4B,0x31,//0x00,
	0x18,0x14,0x12,0x7F,0x10,//0x00,
	0x27,0x45,0x45,0x45,0x39,//0x00,
	0x3C,0x4A,0x49,0x49,0x30,//0x00,
	0x01,0x71,0x09,0x05,0x03,//0x00,
	0x36,0x49,0x49,0x49,0x36,//0x00,
	0x06,0x49,0x49,0x29,0x1E,//0x00,
	0x00,0x36,0x36,0x00,0x00,//0x00,
	0x00,0x56,0x36,0x00,0x00,//0x00,
	0x08,0x14,0x22,0x41,0x00,//0x00,
	0x14,0x14,0x14,0x14,0x14,//0x00,
	0x00,0x41,0x22,0x14,0x08,//0x00,
	0x02,0x01,0x51,0x09,0x06,//0x00,
	0x32,0x49,0x79,0x41,0x3E,//0x00,
	0x7E,0x11,0x11,0x11,0x7E,//0x00,
	0x7F,0x49,0x49,0x49,0x36,//0x00,
	0x3E,0x41,0x41,0x41,0x22,//0x00,
	0x7F,0x41,0x41,0x22,0x1C,//0x00,
	0x7F,0x49,0x49,0x49,0x41,//0x00,
	0x7F,0x09,0x09,0x09,0x01,//0x00,
	0x3E,0x41,0x49,0x49,0x7A,//0x00,
	0x7F,0x08,0x08,0x08,0x7F,//0x00,
	0x00,0x41,0x7F,0x41,0x00,//0x00,
	0x20,0x40,0x41,0x3F,0x01,//0x00,
	0x7F,0x08,0x14,0x22,0x41,//0x00,
	0x7F,0x40,0x40,0x40,0x40,//0x00,
	0x7F,0x02,0x0C,0x02,0x7F,//0x00,
	0x7F,0x04,0x08,0x10,0x7F,//0x00,
	0x3E,0x41,0x41,0x41,0x3E,//0x00,
	0x7F,0x09,0x09,0x09,0x06,//0x00,
	0x3E,0x41,0x51,0x21,0x5E,//0x00,
	0x7F,0x09,0x19,0x29,0x46,//0x00,
	0x46,0x49,0x49,0x49,0x31,//0x00,
	0x01,0x01,0x7F,0x01,0x01,//0x00,
	0x3F,0x40,0x40,0x40,0x3F,//0x00,
	0x1F,0x20,0x40,0x20,0x1F,//0x00,
	0x3F,0x40,0x38,0x40,0x3F,//0x00,
	0x63,0x14,0x08,0x14,0x63,//0x00,
	0x07,0x08,0x70,0x08,0x07,//0x00,
	0x61,0x51,0x49,0x45,0x43,//0x00,
	0x00,0x7F,0x41,0x41,0x00,//0x00,
	0x02,0x04,0x08,0x10,0x20,//0x00,
	0x00,0x41,0x41,0x7F,0x00,//0x00,
	0x04,0x02,0x01,0x02,0x04,//0x00,
	0x40,0x40,0x40,0x40,0x40,//0x40,
	0x00,0x01,0x02,0x04,0x00,//0x00,
	0x20,0x54,0x54,0x54,0x78,//0x00,
	0x7F,0x48,0x44,0x44,0x38,//0x00,
	0x38,0x44,0x44,0x44,0x20,//0x00,
	0x38,0x44,0x44,0x48,0x7F,//0x00,
	0x38,0x54,0x54,0x54,0x18,//0x00,
	0x08,0x7E,0x09,0x01,0x02,//0x00,
	0x0C,0x52,0x52,0x52,0x3E,//0x00,
	0x7F,0x08,0x04,0x04,0x78,//0x00,
	0x00,0x44,0x7D,0x40,0x00,//0x00,
	0x20,0x40,0x44,0x3D,0x00,//0x00,
	0x7F,0x10,0x28,0x44,0x00,//0x00,
	0x00,0x41,0x7F,0x40,0x00,//0x00,
	0x7C,0x04,0x18,0x04,0x78,//0x00,
	0x7C,0x08,0x04,0x04,0x78,//0x00,
	0x38,0x44,0x44,0x44,0x38,//0x00,
	0x7C,0x14,0x14,0x14,0x08,//0x00,
	0x08,0x14,0x14,0x18,0x7C,//0x00,
	0x7C,0x08,0x04,0x04,0x08,//0x00,
	0x48,0x54,0x54,0x54,0x20,//0x00,
	0x04,0x3F,0x44,0x40,0x20,//0x00,
	0x3C,0x40,0x40,0x20,0x7C,//0x00,
	0x1C,0x20,0x40,0x20,0x1C,//0x00,
	0x3C,0x40,0x30,0x40,0x3C,//0x00,
	0x44,0x28,0x10,0x28,0x44,//0x00,
	0x0C,0x50,0x50,0x50,0x3C,//0x00,
	0x44,0x64,0x54,0x4C,0x44,//0x00,
	0x00,0x08,0x36,0x41,0x00,//0x00,
	0x00,0x00,0x7F,0x00,0x00,//0x00,
	0x00,0x41,0x36,0x08,0x00,//0x00,
	0x10,0x08,0x08,0x10,0x08,//0x00,
	0x78,0x46,0x41,0x46,0x78,//0x00
};

#endif // BARCELONA_TARGET


#ifdef BARCELONA_TARGET
void FbRender_BlitCharacter(int x, int y, unsigned short aColor, unsigned short aBackColor, char character)
{
  unsigned char data0,data1,data2,data3,data4;

  if (x<0 || y<0) return;
  if (x>ScreenWidth-CharWidth-1 || y>ScreenHeight-7) return;


  if ((character < 32) || (character > 126)) character = ' ';

  const unsigned char *aptr = &asciiTable[(character-32)*5];
  data0 = *aptr++;
  data1 = *aptr++;
  data2 = *aptr++;
  data3 = *aptr++;
  data4 = *aptr;

  register unsigned short *ptr  = (unsigned short*)(fbbackp+y*((vinfo.xres*vinfo.bits_per_pixel)/8));
  ptr+=x;
  register unsigned short *endp  = ptr+CharWidth*ScreenWidth;
  while (ptr<endp)
  {
    if (data0 & 1) *ptr++ = aColor; else *ptr++ = aBackColor; data0 >>= 1;
    if (data1 & 1) *ptr++ = aColor; else *ptr++ = aBackColor; data1 >>= 1;
    if (data2 & 1) *ptr++ = aColor; else *ptr++ = aBackColor; data2 >>= 1;
    if (data3 & 1) *ptr++ = aColor; else *ptr++ = aBackColor; data3 >>= 1;
    if (data4 & 1) *ptr++ = aColor; else *ptr++ = aBackColor; data4 >>= 1;
    ptr+=ScreenWidth-(CharWidth-3);
  }
}
#endif // BARCELONA_TARGET

#ifdef BARCELONA_TARGET
void FbRender_BlitCharacterItalic(int x, int y, unsigned short aColor, unsigned short aBackColor, char character)
{
  unsigned char data0,data1,data2,data3,data4;

  if (x<0 || y<0) return;
  if (x>ScreenWidth-CharWidth-1 || y>ScreenHeight-7) return;


  if ((character < 32) || (character > 126)) character = ' ';

  const unsigned char *aptr = &asciiTable[(character-32)*5];
  data0 = *aptr++;
  data1 = *aptr++;
  data2 = *aptr++;
  data3 = *aptr++;
  data4 = *aptr;

  register unsigned short *ptr  = (unsigned short*)(fbbackp+y*((vinfo.xres*vinfo.bits_per_pixel)/8));
  ptr+=x;
  register unsigned short *endp  = ptr+CharWidth*ScreenWidth;
  while (ptr<endp)
  {
    if (data0 & 1) *ptr++ = aColor; else *ptr++ = aBackColor; data0 >>= 1;
    if (data1 & 1) *ptr++ = aColor; else *ptr++ = aBackColor; data1 >>= 1;
    if (data2 & 1) *ptr++ = aColor; else *ptr++ = aBackColor; data2 >>= 1;
    if (data3 & 1) *ptr++ = aColor; else *ptr++ = aBackColor; data3 >>= 1;
    if (data4 & 1) *ptr++ = aColor; else *ptr++ = aBackColor; data4 >>= 1;
    ptr+=ScreenWidth-(CharWidth-2);
  }
}
#endif // BARCELONA_TARGET


void FbRender_BlitText(int x, int y, unsigned short aColor, unsigned short aBackColor, char *str)
{
#ifdef BARCELONA_TARGET
  while (*str)
  {
    FbRender_BlitCharacter(x, y, aColor, aBackColor, *str);
    x+=CharWidth;
    str++;
  }
#endif // BARCELONA_TARGET

}

void FbRender_BlitTextItalic(int x, int y, unsigned short aColor, unsigned short aBackColor, char *str)
{
#ifdef BARCELONA_TARGET
  while (*str)
  {
    FbRender_BlitCharacterItalic(x, y, aColor, aBackColor, *str);
    x+=CharWidth;
    str++;
  }
#endif // BARCELONA_TARGET

}

void FbRender_Flush()
{
static int screenshotnr=0;
char cmd[255];

screenshotnr++;
#ifdef BARCELONA_TARGET
  memcpy(fbp,fbbackp,screensize);
  sprintf( cmd, "./screenshot.sh %d", screenshotnr  );
  // fprintf(stderr,"Kommando: %s\n",cmd);
  if (screenrecord==1)
    system(cmd);
#endif // BARCELONA_TARGET

}


void FbRender_Scroll(int target_y, int source_y, int height)
{
  memmove(fbbackp+target_y*ScreenHeight*2,fbbackp+source_y*ScreenHeight*2,height*ScreenHeight*2);
}



#endif // __fbrender_cpp__



#ifndef __tsscreen_cpp__
#define __tsscreen_cpp__

#include <barcelona/Barc_ts.h>


void TsScreen_Init(); 
void TsScreen_Exit(); 

static int tsfd = -1;
static fd_set fdset;
static int maxfd;

void TsScreen_Init()
{
#ifdef BARCELONA_TARGET
  tsfd = open("/dev/ts", O_RDWR | O_NOCTTY | O_NONBLOCK);
  if (tsfd < 0) 
  {
    tsfd = open("/dev/ts", O_RDONLY | O_NOCTTY | O_NONBLOCK);
  }
  if (tsfd < 0) 
  {
    printf("tomtom_touchscreen: Kon niet geopend worden '/dev/ts'.\n");
    tsfd = -1;
  }

  ioctl(tsfd,TS_SET_RAW_OFF,NULL);
  
  maxfd = tsfd + 1;
  FD_ZERO(&fdset);
  FD_SET(tsfd, &fdset);
#endif // BARCELONA_TARGET
}

void TsScreen_Exit()
{
#ifdef BARCELONA_TARGET
  if (tsfd>=0)
  {
    close(tsfd);
  }
#endif // BARCELONA_TARGET
}

// int TsScreen_pen(int& x,int& y,int& pen)
int TsScreen_pen(int *x,int *y,int *pen)
{
#ifdef BARCELONA_TARGET
  if (tsfd<0)
    return 0;

  int read_len;
  TS_EVENT new_event;
 
  static int have_previous = 0;
  static TS_EVENT prev_event;

  if (!have_previous)
  {
    read_len = read(tsfd, &prev_event, sizeof(TS_EVENT));
    if (read_len == sizeof(TS_EVENT))
    {
      have_previous = 1;
    }
  }
  // if we still don't have an event, there are no events pending, and we can just return
  if (!have_previous)
    return 0;
  
  // We have an event
  memcpy(&new_event, &prev_event,sizeof(TS_EVENT));
  have_previous = 0;

  read_len = read(tsfd, &prev_event, sizeof(TS_EVENT));
  if (read_len == sizeof(TS_EVENT))
  {
    have_previous = 1;
  }

  while (have_previous && (prev_event.pressure != 0) == (new_event.pressure != 0))
  {
    memcpy(&new_event, &prev_event,sizeof(TS_EVENT));
    have_previous = 0;
    read_len = read(tsfd, &prev_event, sizeof(TS_EVENT));
    if (read_len == sizeof(TS_EVENT))
    {
      have_previous = 1;
    }
  }
  *x = new_event.x;
  *y = new_event.y;
  *pen = new_event.pressure;
  return 1;
#endif // BARCELONA_TARGET
  return 0;
}


#endif // __tsscreen_cpp__



unsigned long FixSqrt(unsigned long x)
{
  unsigned long r, nr, m;

  r = 0;
  m = 0x40000000;
  do 
  {
    nr = r + m;
    if (nr <= x)
		{
      x -= nr;
      r = nr + m;
    }
    r >>= 1;
    m >>= 2;
  } while (m != 0);

  if (x > r)
    r++;
  return r;
};

void DrawHorizontalLine(int X, int Y, int width, unsigned short color)
{
  register int w = width;  // in pixels
#define iClipTop 0
#define iClipBottom ScreenHeight
#define iClipMin 0
#define iClipMax ScreenWidth

  if (Y<iClipTop) return;
  if (Y>=ScreenHeight) return;

  if (iClipMin-X>0)      // clip left margin
    { w-=(iClipMin-X); X=iClipMin; }
  if (w>ScreenWidth-X)    // clip right margin
    w=ScreenWidth-X;

  // put the pixels...  
  {
    while ( w-- > 0 )
      FbRender_PutPixel(X++,Y,color);
  }
}



void FillCircle (int cx, int cy, int aRad, unsigned short color)
{
int y;
  for (y=cy-aRad; y<=cy+aRad; ++y)
  {      
    register unsigned long tmp;
    tmp = aRad*aRad - (y - cy)*(y-cy);
    tmp = FixSqrt(tmp);
    DrawHorizontalLine( cx-tmp, y, tmp<<1, color);
  }  
}



void PutLinePoint(int x, int y, unsigned short color, int width)
{  
  if (width>1)
  {
    FillCircle(x,y,width,color);
  }
  else
  {
    FbRender_PutPixel(x, y, color);
  }
}


// Directly puts a pixel at x/y
// expects a gdcolor
// expects coordinates 1..MaxValues
void FB_PutPixel(int x, int y, int color)
{
  int r,g,b;

  x--; y--;
  r=gdTrueColorGetRed(color);
  g=gdTrueColorGetGreen(color);
  b=gdTrueColorGetBlue(color);
  FbRender_PutPixel(x, y, Color(r,g,b));
}


// Directly draws a horizontal line to screen 
// in the stated gdcolor (not alpha)
// expects coordinates 1..MaxValues
void FB_HLine(int x1, int y1, int x2, int color)
{
  int r,g,b;

  x1--; y1--; x2--;
  r=gdTrueColorGetRed(color);
  g=gdTrueColorGetGreen(color);
  b=gdTrueColorGetBlue(color);
  DrawHorizontalLine(x1,y1,x2-x1+1, Color(r,g,b) );
}



// Directly draws a vertical line in the 
// stated gdcolor
// expects coordinates 1..MaxValues
void FB_VLine(int x1,int y1, int y2, int color)
{
 int y;
 int r,g,b;
 
 x1--; y1--; y2--;
 if ( y2>ScreenHeight )
   return;
 r=gdTrueColorGetRed(color);
 g=gdTrueColorGetGreen(color);
 b=gdTrueColorGetBlue(color);
 for ( y=y1 ; y <= y2; y++) {
   FbRender_PutPixel(x1,y,Color(r,g,b));
 }
}




// Directly draws a rectangle in the 
// stated gdcolor
// expects coordinates 1..MaxValues
void FB_Rect(int x1,int y1,int x2, int y2, int color)
{
  FB_VLine( x1,y1,y2 ,color);
  FB_VLine( x2,y1,y2 ,color);
  FB_HLine(x1,y1,x2,color);
  FB_HLine(x1,y2,x2,color);
}




// Directly draws a filled rectangle in the
// stated gdcolor
// expects coordinates 1..MaxValues
void FB_FilledRect(int x1,int y1,int x2, int y2, int color)
{
 int y;
 int r,g,b;

 x1--; y1--; x2--; y2--;
 r=gdTrueColorGetRed(color);
 g=gdTrueColorGetGreen(color);
 b=gdTrueColorGetBlue(color);
 for ( y=y1 ; y <= y2; y++) {
   DrawHorizontalLine(x1,y,x2-x1+1, Color(r,g,b) );
 }
}

int cross(int x,int y)
{
 int pen;

 FB_VLine( x,y-8, y, blackcol);
 FB_VLine( x,y, y+8, blackcol);
 FB_HLine( x-8,y, x, blackcol);
 FB_HLine( x,y, x+8, blackcol);

 FB_VLine( x+1,y-8, y, blackcol);
 FB_VLine( x+1,y, y+8, blackcol);
 FB_HLine( x-8,y+1, x, blackcol);
 FB_HLine( x,y+1, x+8, blackcol);

 FB_VLine( x-1,y-8, y, dodgerbluecol);
 FB_VLine( x-1,y, y+8, dodgerbluecol);
 FB_HLine( x-8,y-1, x, dodgerbluecol);
 FB_HLine( x,y-1, x+8, dodgerbluecol);


 while (TsScreen_pen(&x,&y,&pen));
 return 0;
}


int cross2(int x,int y)
{
 int pen;

 FB_VLine( x,y-8, y, blackcol);
 FB_VLine( x,y, y+8, blackcol);
 FB_HLine( x-8,y, x, blackcol);
 FB_HLine( x,y, x+8, blackcol);

 while (TsScreen_pen(&x,&y,&pen));
 return 0;
}

