
int drw_aktx       = 0;
int drw_akty       = 0;

uint16_t drw_color = 0xffff;

// Koordinaten, um die die Ausgabe einer Turtlegrafik "verschoben" werden kann
int drw_xofs      = 0;
int drw_yofs      = 0;

/* -------------------------------------------------------------
     drw_putpixel

     zeichnet einen einzelnen Bildpunkt auf das Display

         x, y  : Koordinate, an der ein Bildpunkt gesetzt
                 wird
         col   : Farbe des Bildpunktes
   ------------------------------------------------------------- */

void drw_putpixel(int x, int y, uint16_t col)
{
  putpixel(x + drw_xofs, y + drw_yofs, col);
}

/* -------------------------------------------------------------
     drw_line

     Zeichnet eine Linie von den Koordinaten x0,y0 zu x1,y1
     mit der angegebenen Farbe

        x0,y0 : Koordinate linke obere Ecke
        x1,y1 : Koordinate rechte untere Ecke
        color : Farbwert, der gezeichnet wird
     Linienalgorithmus nach Bresenham (www.wikipedia.org)

   ------------------------------------------------------------- */
void drw_line(int x0, int y0, int x1, int y1, uint16_t color)
{

  //    Linienalgorithmus nach Bresenham (www.wikipedia.org)

  int dx =  abs(x1-x0), sx = x0<x1 ? 1 : -1;
  int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1;
  int err = dx+dy, e2;                                     /* error value e_xy */

  for(;;)
  {

    drw_putpixel(x0, y0, color);
    if (x0==x1 && y0==y1) break;
    e2 = 2*err;
    if (e2 > dy) { err += dy; x0 += sx; }                  /* e_xy+e_x > 0 */
    if (e2 < dx) { err += dx; y0 += sy; }                  /* e_xy+e_y < 0 */
  }
}

/* -------------------------------------------------------------
     drw_ellipse

     Zeichnet eine Ellipse mit Mittelpunt an der Koordinate
     xm,ym mit den Hoehen- Breitenverhaeltnis a:b
     mit der angegebenen Farbe

        xm,ym  : Koordinate des Mittelpunktes der Ellipse
        a,b    : Hoehen- Breitenverhaeltnis
        color  : Farbwert, der gezeichnet wird

     Ellipsenalgorithmus nach Bresenham (www.wikipedia.org)
   ------------------------------------------------------------- */
void drw_ellipse(int xm, int ym, int a, int b, uint16_t color )
{
  // Algorithmus nach Bresenham (www.wikipedia.org)

  int dx = 0, dy = b;                       // im I. Quadranten von links oben nach rechts unten

  long a2 = a*a, b2 = b*b;
  long err = b2-(2*b-1)*a2, e2;             // Fehler im 1. Schritt */

  do
  {

    drw_putpixel(xm+dx, ym+dy,color);                // I.   Quadrant
    drw_putpixel(xm-dx, ym+dy,color);                // II.  Quadrant
    drw_putpixel(xm-dx, ym-dy,color);                // III. Quadrant
    drw_putpixel(xm+dx, ym-dy,color);                // IV.  Quadrant

    e2 = 2*err;
    if (e2 <  (2*dx+1)*b2) { dx++; err += (2*dx+1)*b2; }
    if (e2 > -(2*dy-1)*a2) { dy--; err -= (2*dy-1)*a2; }
  } while (dy >= 0);

  while (dx++ < a)                        // fehlerhafter Abbruch bei flachen Ellipsen (b=1)
  {

    drw_putpixel(xm+dx, ym,color);
    drw_putpixel(xm-dx, ym,color);

  }
}

/* ----------------------------------------------------------
     drw_fastxline

     zeichnet eine Linie in X-Achse mit den X Punkten
     x1 und x2 auf der Y-Achse y1

        x1, x2 : Start-, Endpunkt der Linie
        y1     : Y-Koordinate der Linie
        color  : 16 - Bit RGB565 Farbwert der gezeichnet
                 werden soll
   ---------------------------------------------------------- */
void drw_fastxline(int x1, int y1, int x2, uint16_t color)
{
  int x;

  if (x2 < x1) { x = x1; x1 = x2; x2 = x; }

  for (x= x1; x< (x2+1); x++)
  {
    drw_putpixel(x,y1, color);
  }
}

/* -------------------------------------------------------------
     drw_rectangle

     Zeichnet ein Rechteck von den Koordinaten x0,y0 zu x1,y1
     mit der angegebenen Farbe

        x0,y0  : Koordinate linke obere Ecke
        x1,y1  : Koordinate rechte untere Ecke
        color  : Farbwert, der gezeichnet wird
   ------------------------------------------------------------- */
void drw_rectangle(int x1, int y1, int x2, int y2, uint16_t color)
{
  if (x2 < x1) { int t = x1; x1 = x2; x2 = t; }
  if (y2 < y1) { int t = y1; y1 = y2; y2 = t; }

  drw_line(x1,y1,x2,y1, color);
  drw_line(x2,y1,x2,y2, color);
  drw_line(x1,y2,x2,y2, color);
  drw_line(x1,y1,x1,y2, color);
}

/* -------------------------------------------------------------
     drw_fillrectangle

     Zeichnet ein gefuelltes Rechteck von den Koordinaten
     x0,y0 zu x1,y1 mit der angegebenen Farbe

        x0,y0  : Koordinate linke obere Ecke
        x1,y1  : Koordinate rechte untere Ecke
        color  : Farbwert, der gezeichnet wird
   ------------------------------------------------------------- */
void drw_fillrectangle(int x1, int y1, int x2, int y2, uint16_t color)
{
  if (x2 < x1) { int t = x1; x1 = x2; x2 = t; }
  if (y2 < y1) { int t = y1; y1 = y2; y2 = t; }

  int y;

  for (y = y1; y <= y2; y++)
  {
    drw_fastxline(x1, y, x2, color);
  }
}

/* -------------------------------------------------------------
     drw_circle

     Zeichnet einen Kreis mit Mittelpunt an der Koordinate xm,ym
     und dem Radius r mit der angegebenen Farbe

        x ,y   : Koordinate des Mittelpunktes der Ellipse
        r      : Radius des Kreises
        color  : Farbwert, der gezeichnet wird
   ------------------------------------------------------------- */
void drw_circle(int x, int y, int r, uint16_t color )
{
  drw_ellipse(x,y,r,r,color);
}

/* -------------------------------------------------------------
     drw_fillellipse

     Zeichnet eine ausgefuellte Ellipse mit Mittelpunt an der
     Koordinate xm,ym mit den Hoehen- Breitenverhaeltnis a:b
     mit der angegebenen Farbe

        xm,ym  : Koordinate des Mittelpunktes der Ellipse
        a,b    : Hoehen- Breitenverhaeltnis
        color  : Farbwert, der gezeichnet wird

   Ellipsenalgorithmus nach Bresenham (www.wikipedia.org)
   ------------------------------------------------------------- */
void drw_fillellipse(int xm, int ym, int a, int b, uint16_t color )
{
  // Algorithmus nach Bresenham (www.wikipedia.org)

  int dx = 0, dy = b;                       // im I. Quadranten von links oben nach rechts unten
  long a2 = a*a, b2 = b*b;
  long err = b2-(2*b-1)*a2, e2;             // Fehler im 1. Schritt */

  do
  {

    drw_fastxline(xm+dx, ym+dy,xm-dx, color);            // I. und II.   Quadrant
    drw_fastxline(xm-dx, ym-dy,xm+dx, color);            // III. und IV. Quadrant

    e2 = 2*err;
    if (e2 <  (2*dx+1)*b2) { dx++; err += (2*dx+1)*b2; }
    if (e2 > -(2*dy-1)*a2) { dy--; err -= (2*dy-1)*a2; }
  } while (dy >= 0);

  while (dx++ < a)                        // fehlerhafter Abbruch bei flachen Ellipsen (b=1)
  {
    drw_putpixel(xm+dx, ym,color);
    drw_putpixel(xm-dx, ym,color);
  }
}

/* -------------------------------------------------------------
     drw_fillcircle

     Zeichnet einen ausgefuellten Kreis mit Mittelpunt an der
     Koordinate xm,ym und dem Radius r mit der angegebenen Farbe

        x,y    : Koordinate des Mittelpunktes der Ellipse
        r      : Radius des Kreises
        color  : Farbwert, der gezeichnet wird
   ------------------------------------------------------------- */
void drw_fillcircle(int x, int y, int r, uint16_t color )
{
  drw_fillellipse(x,y,r,r,color);
}

/* -------------------------------------------------------------
     cmd_getonearg

     liest einen Parameter als Integer ab der Stringposition 0
     und gibt als Funktionsargument einen Zeiger auf den dem
     Argument folgenden String zurueck.

        *str   : Zeiger auf String
        *value : Zeiger Speicherplatz, der den im String ent-
                 haltenen Wert als Integer aufnimmt

     Rueckgabe :
        Zeiger auf den String, der dem Parameter folgt

   ------------------------------------------------------------- */
const char *cmd_getonearg(const char *str, int *value)
{
  char ch, negativesign;
  char loop;
  int  v;

  loop= 1;
  v= 0;

  negativesign= 0;
  while(*str && loop)
  {
    ch= *str;
    if (ch == '-')
    {
      negativesign= 1;
      str++;
      ch= *str;
    }
    if ((ch >= '0') && (ch <= '9'))
    {
      v  = v*10;
      v += ch-'0';
      str++;
    }
    else
    {
      loop= 0;
    }
  }
  *value= v;
  if (negativesign) { *value= *value * (-1); }
  return str;
}

/* -------------------------------------------------------------
     cmd_parse

     Interpretiert einen String als Anweisungen fuer einen
     Zeichenstift

     Kommandos innerhalb des Strings:

       P   : bewegt den Zeichenstift auf eine absolue
             Koordinatenposition ohne etwas zu zeicnen
       p   : zeichnet eine Line zur angegebenen absoluten
             Koordinatenposition
       M   : bewegt den Zeichenstift auf eine relative
             Koordinatenposition ohne etwas zu zeicnen
       m   : zeichnet eine Line zur angegebenen relative
             Koordinatenposition
       r   : zeichnet eine Linie nach rechts mit einer ange-
             gebenen Anzahl Pixel (right)
       l   : zeichnet eine Linie nach links mit einer ange-
             gebenen Anzahl Pixel (left)
       u   : zeichnet eine Linie nach oben mit einer ange-
             gebenen Anzahl Pixel (up)
       d   : zeichnet eine Linie nach unten mit einer ange-
             gebenen Anzahl Pixel (down)
       k   : zeichnet einen Kreis mit einem angegebenen
             Radius
       K   : zeichnet einen gefuellten Kreis mit einem
             angegebenen Radius
       t   : zeichnet ein Recheck von der aktuellen Zeichen-
             position mit angegebener Pixelbreite und Pixel-
             hoehe
       T   : zeichnet ein gefuelltes Recheck von der aktuellen
             Zeichenposition mit angegebener Pixelbreite und
             Pixelhoehe
       c   : setzt die Zeichenfarbe mit der Farbe in der
             EGA-Palette (1 Argument)
       C   : setzt die Zeichenfarbe mit Werten fuer
             rot, gruen, blau


   ------------------------------------------------------------- */
void cmd_parse(const char *str)
{
  char ch;
  int  arg1, arg2, arg3;

  while(*str)
  {
   ch= *str;
   str++;
   switch(ch)
   {
     // ----------------------------------------------
     //   EGA-Farbwahl
     // ----------------------------------------------
     case 'c' :
       {
         str= cmd_getonearg(str, &arg1);
         drw_color= rgbfromega(arg1);
         break;
       }

     // ----------------------------------------------
     //   VGA-Farbwahl
     // ----------------------------------------------
     case 'C' :
       {
         str= cmd_getonearg(str, &arg1);
         if(*str == ',') str++;
         str= cmd_getonearg(str, &arg2);
         if(*str == ',') str++;
         str= cmd_getonearg(str, &arg3);
         drw_color= rgbfromvalue(arg1, arg2, arg3);
         break;
       }

     // ----------------------------------------------
     //   zeichne nach rechts / right draw
     // ----------------------------------------------
     case 'r' :
       {
         str= cmd_getonearg(str, &arg1);
         drw_line(drw_aktx, drw_akty, drw_aktx+arg1, drw_akty, drw_color);
         drw_aktx += arg1;
         break;
       }

     // ----------------------------------------------
     //   zeichne nach links / left draw
     // ----------------------------------------------       
     case 'l' :                 
       {
         str= cmd_getonearg(str, &arg1);
         drw_line(drw_aktx, drw_akty, drw_aktx-arg1, drw_akty, drw_color);
         drw_aktx -= arg1;
         break;
       }

     // ----------------------------------------------
     //    zeichne nach oben / up draw
     // ----------------------------------------------       
     case 'u' :                 
     {
       str= cmd_getonearg(str, &arg1);
       drw_line(drw_aktx, drw_akty, drw_aktx, drw_akty-arg1, drw_color);
       drw_akty -= arg1;
       break;
     }

     // ----------------------------------------------
     //   zeichne nach unten / down draw
     // ----------------------------------------------       
     case 'd' :                 
     {
       str= cmd_getonearg(str, &arg1);
       drw_line(drw_aktx, drw_akty, drw_aktx, drw_akty+arg1, drw_color);
       drw_akty += arg1;
       break;
     }

     // ----------------------------------------------
     //   zeichne Kreis / circle draw
     // ----------------------------------------------       
     case 'k' :                 
     {
       str= cmd_getonearg(str, &arg1);
       drw_circle(drw_aktx, drw_akty, arg1, drw_color);
       break;
     }

     // ----------------------------------------------
     //   zeichne ausgefuellten Kreis / filled
     //   circle draw
     // ----------------------------------------------       
     case 'K' :                 
     {
       str= cmd_getonearg(str, &arg1);
       drw_fillcircle(drw_aktx, drw_akty, arg1, drw_color);
       break;
     }

     // ----------------------------------------------
     //   zeichne Rechteck / recTangle draw
     //   aktuelle Position ist linkes oberes Eck,
     //   Argument 1 entspricht Breite, Argument 2
     //   entspricht Hoehe
     // ----------------------------------------------       
     case 't' :                 
       {
         str= cmd_getonearg(str, &arg1);
         str++;
         str= cmd_getonearg(str, &arg2);
         drw_rectangle(drw_aktx, drw_akty, drw_aktx+arg1, drw_akty+arg2, drw_color);
         break;
       }

     // ----------------------------------------------
     //   zeichne ausgefuelltes Rechteck / filled
     //   rectangle draw. Akuelle Position ist 
     //   linkes oberes Eck, Argument 1 entspricht
     //   Breite, Argument 2 entspricht Hoehe
     // ----------------------------------------------       
     case 'T' :                
       {
         str= cmd_getonearg(str, &arg1);
         str++;
         str= cmd_getonearg(str, &arg2);
         drw_fillrectangle(drw_aktx, drw_akty, drw_aktx+arg1, drw_akty+arg2, drw_color);
         break;
       }

     // ----------------------------------------------
     //   postioniert Zeichenstift zu einer absoluten
     //   Koordinate ohne zu zeichnen
     // ----------------------------------------------       
     case 'P' :                
       {
         str= cmd_getonearg(str, &arg1);
         str++;
         str= cmd_getonearg(str, &arg2);
         drw_aktx= arg1;
         drw_akty= arg2;
         break;
       }

     // ----------------------------------------------
     //   zeichnet eine Linie zu einer absoluten
     //   Koordinate
     // ----------------------------------------------       
     case 'p' :     
       {
         str= cmd_getonearg(str, &arg1);
         str++;
         str= cmd_getonearg(str, &arg2);
         drw_line(drw_aktx, drw_akty, arg1, arg2, drw_color);
         drw_aktx= arg1;
         drw_akty= arg2;
         break;
       }

     // ----------------------------------------------
     //   positioniert den Zeichenstift zu einer
     //   relativen Koordinate ohne zu zeichnen
     // ----------------------------------------------       
     case 'M' :                
       {
         str= cmd_getonearg(str, &arg1);
         str++;
         str= cmd_getonearg(str, &arg2);
         drw_aktx += arg1;
         drw_akty += arg2;
         break;
       }

     // ----------------------------------------------
     //   zeichnet eine Linie zu einer relativen
     //   Koordinate
     // ----------------------------------------------       
     case 'm' :     
       {
         str= cmd_getonearg(str, &arg1);
         str++;
         str= cmd_getonearg(str, &arg2);
         drw_line(drw_aktx, drw_akty, drw_aktx + arg1, drw_akty + arg2, drw_color);
         drw_aktx += arg1;
         drw_akty += arg2;
         break;
       }
     default : break;
   }
  }
}
