//
// SUMMARY:      Xrgraph.c graphics under X11
// USAGE:        
//
// AUTHOR:       Christophe Prud'Homme
// ORG:          Numerica
// ORIG-DATE:     10-Aug-95
// LAST-MOD:     14-Sep-97 at 22:47:52 by Christophe Prud'homme
// DESCRIPTION: routines for X11 and workstations + system dependent routines
// DESCRIP-END.
//

#define _sys_time_h
// C include
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

// c++ include
#include <new.h>
#include <iostream.h>
#include <opclass.h>
#include <rgraph.h>
#include <disk.h>
#include <lexical.h>
#include <syntaxic.h>

#ifdef HPPA
#ifndef __GNUC__
typedef char *caddr_t;
#endif
#endif

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>


/*
 * this is for HP workstations
 * this allow to avoid including setjmp.h
 *
 * SEE THE FUNCTION ERREUR (INT)
 */
#if ( defined (__hp9000s700) )
# ifdef __cplusplus
extern "C" {
# endif /* __cplusplus */
# define _JBLEN 50
  typedef double jmp_buf[_JBLEN/2];
  extern void longjmp (jmp_buf,int);
  extern int setjmp (jmp_buf);
# ifdef __cplusplus
}
# endif /* __cplusplus */
#else /* not __hp9000s700 */
# include <setjmp.h>
#endif /* defined (__hp9000s700) */
 

#define reel float 

jmp_buf context;
extern void parse (const char *); 
extern int NumOfIterations,Iter;
extern triangulation t;
extern float xfmin, xfmax;

static reel echx,echy,rxmin,rxmax,rymin,rymax;
static int screen, width, height, currx, curry;


typedef enum {
  NO_MESSAGE=-2,
  STOP = -1,
  ERROR = 0,
  MESH = 1,
  FUNCTION = 2
} compil_msg;
compil_msg cmsg;

#if defined(KDE_SUPPORT)

#include <GFem.h>


/*
 * global GFem class 
 * provides general services like:
 *  - graphics routines with really simple interfaces
 *  - total encapsulation of the langage/solver/mesh tools
 */
GFem *gf;

void
run_gfem(Canvas * qp, QString *edp, int w, int h)
{
  gf = new GFem(qp, edp, w, h);
  
  /* let's solve the problem now */
  gf->run();
}

void
out_of_memory ()
{
  cerr << "FreeFEM error: operator new failed; not enough memory" << endl;
  exit (-1);
}
void
NEW_HANDLER (void)
{
  set_new_handler (&out_of_memory);
}
void message(char *s)
{
  QString buf(s);
  buf += "\n",
  QMessageBox::message ("Message", 
		      (const char *)buf,
		      "Ok");
}

void erreur(char *s)
{
  extern jmp_buf context;
  message(s);
  cmsg = ERROR;
  longjmp (context, -1);
}

void rflush()
{
  
  gf->flush();
}

void rattente(int waitm)
{
  bool ok;
  
  gf->flush();
  gf->closeGraphic();
  if (waitm)
    ok = gf->wait();
  if (!ok)
    {
      extern jmp_buf context;
      cmsg = STOP;
      longjmp (context, -1);
    }
  gf->initGraphic();
}

int xerror()
{
  fprintf(stderr, "Probleme avec X-Windows\n");
  return 1;
}

void initgraphique()
{
  width = gf->width(), height = gf->height();
  gf->initGraphic();
}

void closegraphique()
{
  gf->closeGraphic();
}

void cadre(reel xmin,reel xmax,reel ymin,reel ymax)
{
  rxmin = xmin;
  rxmax = xmax;
  rymin = ymin;
  rymax = ymax;

  echx = width / (xmax - xmin);
  echy = height / (ymax - ymin);
}

void couleur(int c)
{
/*
  setcolor(lacouleur = c);
*/
  
}

int scalx(reel x)
{
  return (int)((x - rxmin) * echx);
}

int scaly(reel y)
{
  return (int)((rymax - y) * echy);
}

void pointe(reel x, reel y)
{
  gf->drawPoint(scalx(x), scaly(y));
}

void rmoveto(reel x, reel y)
{
  gf->moveTo(scalx(x), scaly(y));
}

void rlineto(reel x, reel y)
{
  gf->lineTo(scalx(x), scaly(y));
}

void 
cadreortho(reel centrex, reel centrey, reel rayon)
{
  
  if (height < width)
  {
    rymin = centrey - rayon;
    rymax = centrey + rayon;
    echx = echy= height / (2 * rayon);
    rxmin= centrex - width / (2 * echx);
    rxmax= centrex + width / (2 * echx);
  }
  else
  {
    rxmin = centrex - rayon;
    rxmax = centrex + rayon;
    echx = echy = width / (2 * rayon);
    rymin = centrey - height / (2 * echy);
    rymax = centrey + height / (2 * echy);
  }
}

void cercle(reel centrex, reel centrey, reel rayon)
{
  
}
void reffecran()
{
  gf->clear();
}

void raffpoly(int n, float *poly)
{
  int i, *poly0;
  
  poly0=new int[2*n];
  if(poly0 != NULL)
  {
    fprintf(stderr, "Erreur d'allocation dans raffpoly\n");
    return;
  }
  for(i=0; i<n; i++)
  {
    poly0[2*i]=scalx(poly[2*i]);
    poly0[2*i+1]=scaly(poly[2*i+1]);
  }
/*  fillpoly(n, poly0); */
  delete [] poly0;
}

int getprog(char* fn,int argc, char **argv)
{
  if (argc != 2) return 0;
  strcpy(fn,argv[1]);
  printf(" file : %s\n",fn);
  return argc;
}

void execute (char * str)
{ 
  system(str);
}



#else




#if defined(XGFEM)
# include <comm.hxx>
extern comm slave;
void SlaveDeconnect ();
void SlaveSendError (int&, char *, int&);
#endif /* XGFEM */

static  Display *display;
static  Window win;
static  XSizeHints size_hints;
static  GC gc;
static  XFontStruct *font_info;

void
out_of_memory ()
{
  cerr << "FreeFEM error: operator new failed; not enough memory" << endl;
#if defined(XGFEM)
  SlaveDeconnect ();
#endif /* XGFEM */
  exit (-1);
}
void
NEW_HANDLER (void)
{
  set_new_handler (&out_of_memory);
}
void message(char *s)
{
  printf("%s \n",s);
}

void erreur(char *s)
{
  extern jmp_buf context;
  message(s);
  cmsg = ERROR;
#if defined(XGFEM)
  SlaveSendError ((int&)cmsg, s, numligne);
  SlaveDeconnect ();
#endif /* XGFEM */
  longjmp (context, -1);
}

void rflush()
{
  XFlush(display);
}

void rattente(int waitm)
{
  char click[] = "Click to continue...";
  char values[256];
  XEvent report;

  if (xfmin != 0.0 && xfmax != 0.0)
    {
      sprintf(values, "Mini/Maxi: [ %f , %f ]", xfmin, xfmax);
      XDrawString (display,
                   win,
                   gc,
                   5,40,
                   values,
                   strlen(values));
    }
  if (NumOfIterations > 0)
    {
      sprintf(values, "Iter: %d", Iter);
      XDrawString (display,
                   win,
                   gc,
                   5,60,
                   values,
                   strlen(values));
    }
  if (waitm)
    {
      XDrawString (display,
                   win,
                   gc,
                   5,20,
                   click,
                   strlen(click));
      do XNextEvent(display, &report);
      while (report.type != ButtonPress && report.type != KeyPress);
    }
  XCheckMaskEvent(display, ButtonPressMask,&report);
  if (report.type == ButtonPress)
    if (report.xbutton.button == Button3)
      {
        extern jmp_buf context;
//        type_msg = STOP;
        cmsg = STOP;
#if defined(XGFEM)
        slave.csend ((int)cmsg);
        cmsg = NO_MESSAGE;
        slave.csend ((int)cmsg);
        slave.cclose(1);
#endif /* XGFEM */
        closegraphique();
        longjmp (context, -1);
      }
  XFlush (display);
}

int xerror()
{
  fprintf(stderr, "Probleme avec X-Windows\n");
  return 1;
}

void initgraphique()
{
  XEvent report;
  XSetWindowAttributes attrs;
  unsigned long attrs_mask;

  display = XOpenDisplay(NULL);



  if ((font_info = XLoadQueryFont(display, "7x13")) == NULL)
   {
    fprintf (stderr, "FreeFEM: cannot open 7x13 font\n");
    fprintf (stderr, "FreeFEM: I am going to try an other one.\n");
    if ((font_info = XLoadQueryFont(display, "9x15")) == NULL)
	{
	  fprintf (stderr, "FreeFEM: cannot open 9x15 font\n");
          fprintf (stderr, "FreeFEM: AArgghh no misc font.\n");
        }
   }

  XSetErrorHandler((XErrorHandler)xerror);
  XSetIOErrorHandler((XIOErrorHandler)xerror);
  screen = DefaultScreen(display);
  width = DisplayWidth(display, screen) - 100;
  height = DisplayHeight(display, screen) - 160;
  win = XCreateSimpleWindow(display, RootWindow(display, screen), 50, 80,
                            width, height, 4, 
                            BlackPixel(display, screen), WhitePixel(display, screen));

  size_hints.flags = PPosition | PSize;
  size_hints.x = 0;
  size_hints.y = 0;
  size_hints.width = width;
  size_hints.height = height;

  XSetStandardProperties(display, win, "ploth", NULL, 0, NULL, 0, &size_hints);
  XSelectInput(display, win, ExposureMask | ButtonPressMask);
  gc = XCreateGC(display, win, 0, NULL);
  XSetFont(display, gc, font_info->fid);
  XSetForeground(display, gc, BlackPixel(display, screen)); 
  XMapWindow(display, win);

  //
  // enable backing store
  // WARNING: not all display are able to do backing 
  //
  attrs.backing_store = DoesBackingStore (ScreenOfDisplay(display,screen));
  attrs_mask = CWBackingStore;
  XChangeWindowAttributes (display, win, attrs_mask, &attrs);
    
  do XNextEvent(display, &report); while (report.type != Expose);
}

void closegraphique()
{
  XUnloadFont(display, font_info->fid);
  XFreeGC(display, gc);
  XCloseDisplay(display);
}

void cadre(reel xmin,reel xmax,reel ymin,reel ymax)
{
  rxmin = xmin;
  rxmax = xmax;
  rymin = ymin;
  rymax = ymax;

  echx = width / (xmax - xmin);
  echy = height / (ymax - ymin);
}

void couleur(int c)
{
/*
  setcolor(lacouleur = c);
*/
}

int scalx(reel x)
{
  return (int)((x - rxmin) * echx);
}

int scaly(reel y)
{
  return (int)((rymax - y) * echy);
}

void pointe(reel x, reel y)
{
  XDrawPoint(display, win, gc, scalx(x), scaly(y));
}

void rmoveto(reel x, reel y)
{
  currx = scalx(x);
  curry = scaly(y);
}

void rlineto(reel x, reel y)
{
  int newx = scalx(x), newy = scaly(y);
  XDrawLine(display, win, gc, currx, curry, newx, newy);
  currx = newx; curry = newy;
/*   XFlush(display); */
}

void cadreortho(reel centrex, reel centrey, reel rayon)
{

  if (height < width)
  {
    rymin = centrey - rayon;
    rymax = centrey + rayon;
    echx = echy= height / (2 * rayon);
    rxmin= centrex - width / (2 * echx);
    rxmax= centrex + width / (2 * echx);
  }
  else
  {
    rxmin = centrex - rayon;
    rxmax = centrex + rayon;
    echx = echy = width / (2 * rayon);
    rymin = centrey - height / (2 * echy);
    rymax = centrey + height / (2 * echy);
  }
}

void cercle(reel centrex, reel centrey, reel rayon)
{
  int r = (int)(rayon * echx);
  XDrawArc(display, win, gc,
     scalx(centrex) - r, scaly(centrey) - r, width, height, 0, 360 * 64);
  XFlush(display);
}
void reffecran()
{
 XClearWindow(display,win);
}

void raffpoly(int n, float *poly)
{
  int i, *poly0;
  
  poly0=new int[2*n];
  if(poly0 != NULL)
  {
    fprintf(stderr, "Erreur d'allocation dans raffpoly\n");
    return;
  }
  for(i=0; i<n; i++)
  {
    poly0[2*i]=scalx(poly[2*i]);
    poly0[2*i+1]=scaly(poly[2*i+1]);
  }
/*  fillpoly(n, poly0); */
  delete [] poly0;
}

int getprog(char* fn,int argc, char **argv)
{
  if (argc != 2) return 0;
  strcpy(fn,argv[1]);
  printf(" file : %s\n",fn);
  return argc;
}

void execute (char * str)
{ 
  system(str);
}

/*
 * Main program:
 *   - read the file and store it in prog
 *   - parse prog using the routine parse (see syntaxic)
 */
int
main (int argc, char **argv)
{
  char       *prog;
  char       *fname;
  extern char errbuf[];

  NEW_HANDLER (); // see dependent system files ({pc,x,mac}rgraph.{h,cpp})
  
  fname = new char[256];


#if !defined(XGFEM)
  argc = getprog (fname, argc, argv);
  OPTION = 0;
  if (argc == 2)
    {
      prog = readprog (fname);
      delete [] fname;
      /*cerr << " PROGRAM:\n" 
	<< prog << "\n";*/
#endif /* not XGFEM */
      if (setjmp (context) == 0)
        {
          // parse prog buffer
          parse (prog); 
          delete [] prog;
          /* 
           * Freeing the triangulation
           */
          delete [] t.rp;
          delete [] t.tr;
          delete [] t.ng;
          delete [] t.ngt;
          
        }
      else
        {
          switch (cmsg)
            {
            case ERROR:
              printf ("FreeFEM: an error has occured.\n");
              break;
            case STOP:
              printf ("FreeFEM: simulation stopped (right button pressed)\n");
              break;
            }
        }

#if !defined(XGFEM)
    }
  else
    {
      cerr << "Usage:\n"
           << "  freefem filename.pde\n";
    }
#endif /* XGFEM */
  
  cerr << "PASS: Test " << prog << " Ok";
  return 0;
}

#if defined(XGFEM)
void
SlaveConnect (char *&buffer, parameter& adapt_param)
{
  slave.init("slave", SOCK_STREAM,1);
  slave.cconnect();
  slave.crecv (buffer);
  slave.crecv (adapt_param);
}
void
SlaveDeconnect ()
{
  extern compil_msg cmsg;
  cmsg = NO_MESSAGE;
  slave.csend ((int)cmsg);
  slave.cclose(1);
}
void
SlaveSendMesh (triangulation& t)
{
  extern compil_msg cmsg;
  cmsg = MESH;
  int nb_p = t.np;
  int nb_t = t.nt;
  int i, total;

  slave.csend ((int)cmsg);
  slave.csend(nb_p);
  slave.csend(nb_t);
  
  for (i = 0;i < nb_p;i++)
    {
      slave.csend (t.rp[i]);
      slave.csend (t.ng[i]);
    }
  for (i = 0;i < nb_t;i++)
    {
      slave.csend (t.tr[i]);
      slave.csend (t.ngt[i]);
    }
}
void
SlaveSendFunction(int nquad, char *name, float *func)
{
  int i;
  
  extern compil_msg cmsg;
  cmsg = FUNCTION;
  slave.csend ((int)cmsg);
  slave.csend (nquad);
  slave.csend (name);
  for (i = 0;i < nquad;i++)
    slave.csend (func[i]);
}
void
SlaveSendError(int& msg, char *str, int& nl)
{
  slave.csend (cmsg);
  slave.csend (str);
  slave.csend (nl);
}
#endif /* XGFEM */

#endif /* KDE_SUPPORT */
