/*******************************************************************************
+
+  LEDA 3.5
+
+  _x11.c
+
+  This file is part of the LEDA research version (LEDA-R) that can be 
+  used free of charge in academic research and teaching. Any commercial
+  use of this software requires a license which is distributed by the
+  LEDA Software GmbH, Postfach 151101, 66041 Saarbruecken, FRG
+  (fax +49 681 31104).
+
+  Copyright (c) 1991-1997  by  Max-Planck-Institut fuer Informatik
+  Im Stadtwald, 66123 Saarbruecken, Germany     
+  All rights reserved.
+ 
*******************************************************************************/
#include <LEDA/impl/x_window.h>

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


void  define_color(int i, int r, int g, int b) { }


const int screen_width  = 800;
const int screen_height = 600;
const int MAX_COLORS  = 256; 

static Display*      display = NULL;
static Window        win;
static XGCValues     gc_val;
static GC            gc;
static int           screen;
static Colormap      color_map;
static unsigned long color_pix[MAX_COLORS];
static int           color_count= 0;


static int alloc_color(const char* name)
{ XColor xcolor;
  if (DefaultDepth(display,screen)==1 && strcmp(name,"white")!=0) 
     XParseColor(display, color_map, "black", &xcolor);
  else
     XParseColor(display, color_map, name, &xcolor);
  XAllocColor(display,color_map,&xcolor);
  color_pix[color_count] = xcolor.pixel;
  return color_count++;
}



void  init_graphics(int mode, int& width, int& height, int& depth)
{
  width = screen_width;
  height = screen_height;
  depth = MAX_COLORS; 

  if (mode == 0)
    { XCloseDisplay(display); 
      display = NULL;
     }
  else
  { if (display != NULL) return;
    if ((display = XOpenDisplay(0)) == NULL)	
    { fprintf(stderr, "Can\'t open display: \n");
      abort();
     }
    screen    = DefaultScreen(display);
    color_map = DefaultColormap(display,screen);
    color_count = 0;

    alloc_color("white");          // 0: white
    alloc_color("black");          // 1: black
    alloc_color("red");            // 2: red
    alloc_color("green2");         // 3: green
    alloc_color("blue");           // 4: blue
    alloc_color("yellow");         // 5: yellow
    alloc_color("purple");         // 6: violet
    alloc_color("darkorange");     // 7: orange
    alloc_color("cyan");           // 8: cyan
    alloc_color("sienna");         // 9: brown
    alloc_color("magenta");        //10: pink 
    alloc_color("#0cb3a0");        //11: green2
    alloc_color("cornflowerblue"); //12: blue2
    alloc_color("grey86");         //13: grey1
    alloc_color("grey70");         //14: grey2
    alloc_color("grey35");         //15: grey3

    gc_val.background = color_pix[0];
    gc_val.foreground = color_pix[1];
    gc = XCreateGC(display,RootWindow(display,screen),
                      GCBackground | GCForeground, &(gc_val));
  
    XSetLineAttributes(display,gc,1,LineSolid,CapButt,JoinMiter);

    if (DefaultDepth(display,screen)==1) depth = 1;

    // open window
  
    XSetWindowAttributes attrib;
    attrib.backing_store = Always;

    win = XCreateWindow(display, RootWindow(display,screen),
                        0, 0, screen_width,screen_height, 2,
                        DefaultDepth(display,screen), InputOutput, 
                        DefaultVisual(display,screen), 
                        CWBackingStore, &attrib);
  
    XSelectInput(display,win, EnterWindowMask | LeaveWindowMask    |
                              KeyPressMask    | PointerMotionMask  | 
                              ButtonPressMask | ButtonReleaseMask  |
                              ExposureMask    | StructureNotifyMask); 
    
    if (DefaultDepth(display,screen) == 1) 
       XSetWindowBackground(display,win,WhitePixel(display, screen)); 
    else
       XSetWindowBackground(display,win,color_pix[0]);
   
    XStoreName(display,win,"LEDA VGA Window");
    XSetIconName(display,win,"LEDA-VGA");
  
    XMapWindow(display,win);
  
    if (! XDoesBackingStore(XScreenOfDisplay(display,screen)))
    { XEvent e;
      do
         XNextEvent(display, &e);
      while (e.type != Expose);
     }
    
   }
}


/*
void draw_scan_segment(unsigned char* data, int x, int y, int n)
{ for (int i=0; i<n; i++)
  { unsigned char c = data[i];
    gc_val.foreground = color_pix[c];
    XChangeGC(display,gc,GCForeground,&(gc_val));
    XDrawPoint(display,win,gc,x++,y);
   }
}
*/



void draw_scan_segment(unsigned char* data, int x, int y, int n)
{ 
  if (n == 1) // single pixel
  { gc_val.foreground = color_pix[data[0]];
    XChangeGC(display,gc,GCForeground,&(gc_val));
    XDrawPoint(display,win,gc,x,y);
    return;
   }


  XPoint points[800];

  if (n > screen_width) n = 800;

  unsigned char* stop = data + n;

  int max_c = 0;
  int max_n = 0;

  int col_min = 16;
  int col_max = -1;

  for(unsigned char col=0; col<16; col++)
  { int count = 0;
    for (unsigned char* p = data;  p<stop; p++) 
           if (*p == col) count++;

    if (count == 0) continue;

    if (col < col_min) col_min = col;
    if (col > col_max) col_max = col;

    if (count > max_n)
    { max_c = col;
      max_n = count;
     }
   }

  gc_val.foreground = color_pix[max_c];
  XChangeGC(display,gc,GCForeground,&(gc_val));
  XDrawLine(display,win,gc,x,y,x+n-1,y);

  if (max_n < n)
    for(unsigned char col=col_min; col<=col_max; col++)
    { if (col == max_c) continue;
      int count = 0;
      for (int i=0; i<n; i++)
      { if (data[i] == col)
        { points[count].x = (short)(x+i); 
          points[count].y = (short)y;
          count++;
         }
       }
      if (count == 0 ) continue;
      gc_val.foreground = color_pix[col];
      XChangeGC(display,gc,GCForeground,&(gc_val));
      XDrawPoints(display,win,gc,points,count,CoordModeOrigin);
     }

  XFlush(display);

}

void draw_pixel(int x, int y, unsigned char c)
{ gc_val.foreground = color_pix[c];
  XChangeGC(display,gc,GCForeground,&(gc_val));
  XDrawPoint(display,win,gc,x,y);
}




//------------------------------------------------------------------------------
// event handling
//------------------------------------------------------------------------------

XEvent event;

int check_next_event(int& val, int& x, int& y, int block)
{
  int kind = no_event;

  if (block)
      XNextEvent(display, &event);
  else
  { if (XCheckMaskEvent(display,
                      EnterWindowMask | LeaveWindowMask    |
                      KeyPressMask    | PointerMotionMask  |
                      ButtonPressMask | ButtonReleaseMask  |
                      ExposureMask    | StructureNotifyMask, &event) == 0)
    return no_event;
   }


  switch (event.type) {

  case ButtonPress: val = event.xbutton.button;
                    x = event.xbutton.x;
                    y = event.xbutton.y;
                    kind = button_press_event;
                    if (event.xbutton.state & Mod1Mask)    val = 2; 
                    if (event.xbutton.state & ShiftMask)   val |= 256; 
                    if (event.xbutton.state & ControlMask) val |= 512;
                    XUngrabPointer(display,CurrentTime);
                    break;

  case ButtonRelease: val = event.xbutton.button;
                      x = event.xbutton.x;
                      y = event.xbutton.y;
                      if (event.xbutton.state & Mod1Mask)    val = 2; 
                      if (event.xbutton.state & ShiftMask)   val |= 256; 
                      if (event.xbutton.state & ControlMask) val |= 512;
                      kind = button_release_event;
                      break;

  case MotionNotify: x = event.xmotion.x;
                     y = event.xmotion.y;
                     kind = motion_event;
                     break;

  case KeyPress: { char c = 0;
                   KeySym keysym;
                   XComposeStatus status;
                   XLookupString((XKeyEvent*)&event,&c,1, &keysym, &status);
                   val = (c) ? c : keysym;
                   x = event.xmotion.x;
                   y = event.xmotion.y;
                   kind = key_press_event;
                   break;
                  }
   }


  //printf("x11:  k = %d   x = %d   y = %d\n",kind,x,y);

  return kind;
}


