// Copyright (c) 1999 Philip A. Hardin (pahardin@cs.utexas.edu)
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License v2 or later.


#include <assert.h>
#include <string.h>      // to get strlen
#include <iostream.h>
#include <X11/Xlib.h>    // to get XEvent, XFontStruct

#ifndef NO_OPENGL
#include <GL/gl.h>
#include <GL/glx.h>
#endif

#include "bbgfxtarget.h"
#include "xpanel3d.h"    // to get MINDIST


#define DEFAULTWINWIDTH 512
#define DEFAULTWINHEIGHT ((int)(DEFAULTWINWIDTH*0.6+6*14+2))


/*======================================================================*/
bbGfxTarget::bbGfxTarget(bool ugl) :
  gfxTarget(ugl),
  gfxSize(pt2d(DEFAULTWINWIDTH,DEFAULTWINWIDTH*0.6)),
  fontSize(pt2d(6,14)),
  cursor(pt2d(0,0)),
  rightmost(0)
{
  cursorOrg= pt2d(8,gfxSize.y);
  textSize= pt2d(gfxSize.x,fontSize.y*6+2);
}


/*----------------------------------------------------------------------*/
void bbGfxTarget::LoadFont(const char *name) {
  font= XLoadQueryFont(disp,name);
  if (font==NULL) {
    cerr << "cannot open font " << name << endl;
    exit(-1);
  }
}


/*----------------------------------------------------------------------*/
void bbGfxTarget::GetGC()
{ unsigned long valuemask= 0;
  XGCValues values;
  
  gc= XCreateGC(disp,win,valuemask,&values);
  XSetLineAttributes(disp,gc,0,LineSolid,CapRound,JoinRound);
  XSetFont(disp,gc,font->fid);

  pmgc= XCreateGC(disp,pm,valuemask,&values);
  XSetLineAttributes(disp,pmgc,0,LineSolid,CapRound,JoinRound);
  XSetFont(disp,pmgc,font->fid);
}


/*----------------------------------------------------------------------*/
// Creates the BB window.  Also sets the .cmap field.
void bbGfxTarget::CreateWindow(int argc, char *argv[]) {
  static short	x=0,
  		y=0;
  x += 48;
  y += 48;

#ifndef NO_OPENGL
  if (UseGL()) {
    int dummy;
    if (!glXQueryExtension(disp,&dummy,&dummy)) {
      cout << "X server does not appear to support OpenGL; using Xlib rendering instead..." <<endl;
      useGL= 0;
    }
  }

  XVisualInfo* vi= NULL;
  if (UseGL()) {
    static int bbBuf[]= {GLX_BUFFER_SIZE, 2, GLX_DOUBLEBUFFER, None};
    vi= glXChooseVisual(disp,DefaultScreen(disp),bbBuf);
    if (vi==NULL) {
      cout << "Can't get the desired X Visual (color index, double-buffered) for OpenGL;\n";
      cout << "using Xlib rendering instead..." <<endl;
      useGL= 0;
    }
  }

  GLXContext ctx= NULL;
  if (UseGL()) {
    ctx= glXCreateContext(disp,vi,None,True);
    if (ctx==NULL) {
      cout << "An error occured while trying to create an OpenGL context;\n";
      cout << "using Xlib rendering instead..." <<endl;
      useGL= 0;
    }
  }
#endif

  XSetWindowAttributes swa;
  swa.border_pixel= 0;
  swa.event_mask=
    ExposureMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask;

  if (UseGL()) {
#ifndef NO_OPENGL
    cmap= XCreateColormap(disp,rootWin,vi->visual,AllocNone);
    swa.colormap= cmap;
    win= XCreateWindow(disp, rootWin, x, y, DEFAULTWINWIDTH, DEFAULTWINHEIGHT,
		       0,vi->depth, InputOutput, vi->visual,
		       CWEventMask|CWColormap|CWBorderPixel, &swa);
    XFree(vi);
#endif
  } else {
    cmap= DefaultColormap(disp,screenNo);
    win= XCreateWindow(disp, rootWin, x, y, DEFAULTWINWIDTH, DEFAULTWINHEIGHT,
		       0,CopyFromParent, InputOutput, CopyFromParent,
		       CWEventMask|CWBorderPixel, &swa);
  }
  XSetStandardProperties(disp,win,"BattleBall","BattleBall",None,argv,argc,
			 NULL);

#ifndef NO_OPENGL
  if (UseGL()) {
    glXMakeCurrent(disp,win,ctx);
    glViewport(0,(GLint)textSize.y,(GLsizei)gfxSize.x,(GLsizei)gfxSize.y);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-0.5,0.5, -0.3,0.3, MINDIST,100000);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    //gluLookAt(0,0,0, 0,0,-1, 0,1,0);
  }
#endif

  LoadFont("6x13");
  XWindowAttributes attr;
  XGetWindowAttributes(disp,win,&attr);
  pm= XCreatePixmap(disp,win,attr.width,attr.height,attr.depth);

  GetGC();
  XMapWindow(disp,win);
}


/*----------------------------------------------------------------------*/
void bbGfxTarget::AllocColors(char *colorNames[], int numColors) {
  forii(numColors)
    GetColor(colorNames[i],colors[i]);
}


/*----------------------------------------------------------------------*/
void bbGfxTarget::GetColor(char *name, ulong& colorPixel) {
  XColor	color1,color2;
  XAllocNamedColor(disp,cmap,name,&color1,&color2);
  colorPixel= color1.pixel;
}


/*----------------------------------------------------------------------*/
void bbGfxTarget::HandleResize(XEvent *event, bool refit) {
  gfxSize.x= ((XConfigureEvent *)event)->width;
  textSize.x= gfxSize.x;
  gfxSize.y= 0.6*gfxSize.x;
  cursorOrg.y= gfxSize.y;
  if (refit)
    ResizeWindow(gfxSize +pt2d(0,textSize.y));
  else
    gfxTarget::HandleResize();
#ifndef NO_OPENGL
  if (UseGL())
    glViewport(0,(GLint)textSize.y,(GLsizei)gfxSize.x,(GLsizei)gfxSize.y);
#endif
}


/*----------------------------------------------------------------------*/
void bbGfxTarget::DoubleBufferEnd() {
  gfxTarget::DoubleBufferEnd();
#ifndef NO_OPENGL
  if (UseGL())
    XCopyArea(disp,pm,win,gc,
	      0,(int)gfxSize.y, (int)textSize.x,(int)textSize.y,
	      0,(int)gfxSize.y);
#endif
}


/*----------------------------------------------------------------------*/
// Clear the gfx area of the game window
void bbGfxTarget::ClearGfx() const {
  if (UseGL()) {
#ifndef NO_OPENGL
    glClearIndex(White());
    glClear(GL_COLOR_BUFFER_BIT);
#endif
  } else {
    SetForeground(White());
    FillRectangle(pt2d(0,0),gfxSize);
  }
}

void bbGfxTarget::ClearStatus() const {
  SetForeground(White());
  FillRectangle(pt2d(0,gfxSize.y),textSize);
  SetForeground(Black());
  DrawRectangle(pt2d(0,gfxSize.y),textSize -pt2d(1,1));
}

/*----------------------------------------------------------------------*/
// pt = character position in text window
// Returns the pixel position in the text window corresponding to 'pt'
pt2d bbGfxTarget::PixelPos(const pt2d& pt) {
  return pt2d(pt.x*fontSize.x, pt.y*fontSize.y+gfxSize.y);
}


/*----------------------------------------------------------------------*/
bbGfxTarget& bbGfxTarget::operator<<(char *s) {
  int len= strlen(s);
  DrawString(cursorOrg +cursor*fontSize +pt2d(0,fontSize.y-2),s);
  if (cursor.x+len >rightmost)
    rightmost= (int) cursor.x+len;
  cursor.y++;
  return *this;
}


/*-------------------------------------------------------------------------*/
bbGfxTarget& bbGfxTarget::Box(int x, int y, int width, int height,
			      bool highlight) {
  DrawRectangle(cursorOrg +pt2d(x+cursor.x,y)*fontSize +pt2d(-3,1),
		pt2d(width,height)*fontSize +pt2d(4,-2));
  if (highlight) {
    DrawRectangle(cursorOrg +pt2d(x+cursor.x,y)*fontSize +pt2d(-4,0),
		  pt2d(width,height)*fontSize +pt2d(6,0));
  }
  return *this;
}


/*-------------------------------------------------------------------------*/
bbGfxTarget& bbGfxTarget::Tab(int spaces, int newy) {
  cursor.x= rightmost +spaces;
  cursor.y= newy;
  return *this;
}


