#include <stdlib.h>
#include "MsgQ.h"
#include "defs.h"
#include "X11Screen.h"
#include "debug.h"
#include "net.h"
#include "server.h"

static void KeyBoard(Xv_Window, Event *, Notify_arg);
static Notify_value ReadNotify(Notify_client, int);
static Notify_value TimerNotify(...);
static void notify_proc(Panel_item, int);
static void PopupNotify(Panel_item, int);

static Frame frame;
static Frame initFrame;
static Panel_item hostItem, numPlayersItem, nameItem;

static Panel_item RestoreButton, NewGameButton;

typedef ReadHandler *ReadHandlerP;

char *ReadEnv(char *name, char *def)
{
  char *str = getenv(name);
  return str == NULL ? def : str;
}

X11Screen::X11Screen(int argc, char **argv,
		     void (*key)(int, int, int, char *)) : Graphics(key)
{
  xv_init(XV_INIT_ARGC_PTR_ARGV, &argc, argv, NULL);

  frame = (Frame)xv_create(XV_NULL, FRAME,
			   FRAME_LABEL, argv[0],
			   NULL);
  dpy = (Display *)xv_get(frame, XV_DISPLAY);
  Panel panel = (Panel)xv_create(frame, PANEL,
				 XV_WIDTH, 320,
				 PANEL_LAYOUT, PANEL_HORIZONTAL,
				 NULL);
  accept = (Panel_item)xv_create(panel, PANEL_BUTTON,
				 PANEL_LABEL_STRING, "Accept",
				 PANEL_NOTIFY_PROC, notify_proc,
				 NULL);
  mesg = (Panel_item)xv_create(panel, PANEL_MESSAGE,
			       PANEL_LABEL_WIDTH, 200,
			       PANEL_LABEL_STRING, "Initializing",
			       NULL);
  window_fit(panel);
  Canvas canvas = (Canvas)xv_create(frame, CANVAS,
				    XV_WIDTH, 320,
				    XV_HEIGHT, 200,
				    WIN_BACKGROUND_COLOR,
				      BlackPixel(dpy, DefaultScreen(dpy)),
				    CANVAS_X_PAINT_WINDOW, TRUE,
				    WIN_BELOW, canvas_paint_window(panel),
				    XV_X, 0,
				    NULL);
  window_fit(frame);
  xv_set(canvas_paint_window(canvas),
	 WIN_EVENT_PROC, KeyBoard,
	 WIN_CONSUME_EVENTS,
	   WIN_NO_EVENTS,
	   WIN_ASCII_EVENTS, WIN_MOUSE_BUTTONS,
	   NULL,
	 NULL);
  mapWin = (Window)xv_get(canvas_paint_window(canvas), XV_XID);

  initFrame = xv_create(frame, FRAME_CMD,
			FRAME_LABEL, "Initialize Network",
			NULL);

  panel = (Panel)xv_get(initFrame, FRAME_CMD_PANEL);
  hostItem = xv_create(panel, PANEL_TEXT,
		       PANEL_LABEL_STRING, "Server Host Name:",
		       PANEL_VALUE, "localhost",
		       PANEL_VALUE_DISPLAY_LENGTH, 50,
		       NULL);
  nameItem = xv_create(panel, PANEL_TEXT,
		       PANEL_LABEL_STRING, "Name:",
		       PANEL_VALUE, ReadEnv("CIV_NAME", ""),
		       PANEL_VALUE_DISPLAY_LENGTH, 20,
		       NULL);
  numPlayersItem = xv_create(panel, PANEL_NUMERIC_TEXT,
			     PANEL_LABEL_STRING, "Number of Players:",
			     PANEL_VALUE, atoi(ReadEnv("CIV_NUMPL", "0")),
			     PANEL_VALUE_DISPLAY_LENGTH, 10,
			     NULL);
  RestoreButton = xv_create(panel, PANEL_BUTTON,
			    PANEL_LABEL_STRING, "Restore",
			    PANEL_NOTIFY_PROC, PopupNotify,
			    NULL);
  NewGameButton = xv_create(panel, PANEL_BUTTON,
			    PANEL_LABEL_STRING, "New Game",
			    PANEL_NOTIFY_PROC, PopupNotify,
			    NULL);
  window_fit(panel);
  window_fit(initFrame);
  xv_set(initFrame, XV_SHOW, TRUE, NULL);

  Xv_Font _font = (Xv_Font)xv_find(frame, FONT,
				   FONT_NAME, "lucidasanstypewriter-8",
				   NULL);
  XFontStruct *font = (XFontStruct *)xv_get(_font, FONT_INFO);

  XGCValues gcvalues;
  gcvalues.font = font->fid;
  gcvalues.foreground = WhitePixel(dpy, DefaultScreen(dpy));
  gcvalues.background = BlackPixel(dpy, DefaultScreen(dpy));
  gcvalues.graphics_exposures = False;
  gcvalues.clip_mask = None;
  gc = XCreateGC(dpy, RootWindow(dpy, DefaultScreen(dpy)),
		 GCForeground | GCBackground | GCFont | GCGraphicsExposures
		 | GCClipMask,
		 &gcvalues);
  colormap = DefaultColormap(dpy, DefaultScreen(dpy));
  readHandlers = new ReadHandlerP[FD_SETSIZE];
  for (int i = 0; i < FD_SETSIZE; ++i)
    readHandlers[i] = NULL;
}

X11Screen::~X11Screen()
{
  delete [] readHandlers;
}

void X11Screen::StartTimer(int which, int milli, HandlerFunc func)
{
  if (which != 0) return;
  struct itimerval timer;
  timer.it_value.tv_sec = timer.it_interval.tv_sec = 0;
  timer.it_value.tv_usec = timer.it_interval.tv_usec = milli*1000L;
  Timers[0] = func;
  notify_set_itimer_func(frame, TimerNotify, ITIMER_REAL, &timer, NULL);
}

void X11Screen::StopTimer(int which)
{
  if (which != 0) return;
  notify_remove_itimer_func(frame, TimerNotify, ITIMER_REAL);
}

void X11Screen::Clear()
{
  XClearWindow(dpy, mapWin);
}

void X11Screen::FillRect(int x, int y, int width, int ht, int col)
{
  XSetForeground(dpy, gc, col);
  XFillRectangle(dpy, mapWin, gc, x, y, width, ht);
}

void X11Screen::Rect(int x, int y, int width, int ht, int col)
{
  XSetForeground(dpy, gc, col);
  XDrawRectangle(dpy, mapWin, gc, x, y, width, ht);
}

int X11Screen::AllocColor(char *name)
{
  XColor rgbColor, realColor;
  XLookupColor(dpy, colormap, name, &rgbColor, &realColor);
  XAllocColor(dpy, colormap, &realColor);
  return realColor.pixel;
}

void X11Screen::Refresh()
{
  XFlush(dpy);
}

void X11Screen::DisplayMessage(char *str, int color)
{
  xv_set(mesg, PANEL_LABEL_STRING, str, NULL);
}

void X11Screen::WriteStr(int x, int y, char *str, int color)
{
  XSetForeground(dpy, gc, color);
  XDrawString(dpy, mapWin, gc, x, y+8, str, strlen(str));
}

void X11Screen::WriteChar(int x, int y, int ch, int color)
{
  char str[2];
  str[0] = ch;
  str[1] = '\0';
  XSetForeground(dpy, gc, color);
  XDrawString(dpy, mapWin, gc, x, y+8, str, 1);
}

void X11Screen::BitBlt(int sx, int sy, int w, int h, int dx, int dy)
{
  XCopyArea(dpy, mapWin, mapWin, gc, sx, sy, w, h, dx, dy);
}

long X11Screen::CompilePixmap(char **data)
{
  Pixmap shape, image;
  XpmAttributes xpmatts;
  xpmatts.valuemask = 0;
  XpmCreatePixmapFromData(dpy, mapWin, data, &image, &shape, &xpmatts);
  PixmapInfo *info = new PixmapInfo;
  info->pixmap = image;
  info->mask = shape;
  info->w = xpmatts.width;
  info->h = xpmatts.height;
  return long(info);
}

void X11Screen::FreePixmap(long handle)
{
  PixmapInfo *info = (PixmapInfo *)handle;
  XFreePixmap(dpy, info->pixmap);
  XFreePixmap(dpy, info->mask);
  delete info;
}

void X11Screen::DrawPixmap(int xc, int yc, long handle)
{
  PixmapInfo *info = (PixmapInfo *)handle;
  if (info->mask != None) {
    XSetClipOrigin(dpy, gc, xc, yc);
    XSetClipMask(dpy, gc, info->mask);
  }
  XCopyArea(dpy, info->pixmap, mapWin, gc, 0, 0, info->w, info->h, xc, yc);
  if (info->mask != None)
    XSetClipMask(dpy, gc, None);
}

void X11Screen::GetPixmapInfo(long handle, int &w, int &h)
{
  PixmapInfo *info = (PixmapInfo *)handle;
  w = info->w;
  h = info->h;
}

static void KeyBoard(Xv_Window window, Event *event, Notify_arg arg)
{
  char buf[20];
  switch (event_action(event)) {
  case ACTION_SELECT:
    if (event_is_down(event)) return;
    (*screen->KeyPressed)(MOUSE_LEFT, event_x(event), event_y(event), NULL);
    return;
  case ACTION_MENU:
    if (event_is_down(event)) return;
    (*screen->KeyPressed)(MOUSE_RIGHT, event_x(event), event_y(event), NULL);
    return;
  }
  if (event_xevent(event)->type != KeyPress) return;
  XKeyEvent *key_event = (XKeyEvent *)event_xevent(event);
  XLookupString(key_event, buf, 20, NULL, NULL);
  int ch;
  switch (key_event->keycode) {
  case 97:
    ch = KEY_UPLEFT; break;
  case 98:
    ch = KEY_UP; break;
  case 99:
    ch = KEY_UPRIGHT; break;
  case 100:
    ch = KEY_LEFT; break;
  case 102:
    ch = KEY_RIGHT; break;
  case 103:
    ch = KEY_DOWNLEFT; break;
  case 104:
    ch = KEY_DOWN; break;
  case 105:
    ch = KEY_DOWNRIGHT; break;
  default:
    ch = *buf;
  }
  (*screen->KeyPressed)(KEYBOARD, ch, 0, NULL);
}

void X11Screen::AddReadNotify(int fd, void (*func)(MsgQ *, int, int),
			      int info, int q)
{
  ReadHandler *ptr = new ReadHandler;
  readHandlers[fd] = ptr;
  ptr->fd = fd;
  ptr->func = func;
  ptr->q = q;
  ptr->info = info;
  notify_set_input_func(frame, (Notify_func)ReadNotify, fd);
}

// got data, read the queue and hand it to the guy who wants it
static Notify_value ReadNotify(Notify_client client, int fd)
{
  extern MsgQ *RecvQ(int);
  ReadHandler *ptr = screen->readHandlers[fd];
  if (ptr == NULL) return NOTIFY_DONE;
  MsgQ *q = NULL;
  if (ptr->q)
    q = RecvQ(fd);
  (*ptr->func)(q, fd, ptr->info);
  return NOTIFY_DONE;
}

static Notify_value TimerNotify(...)
{
  (*screen->Timers[0])(0);
  return NOTIFY_DONE;
}

int X11Screen::MainLoop()
{
  xv_main_loop(frame);
  return 0;
}

static void notify_proc(Panel_item item, int value)
{
  (*screen->KeyPressed)(KEYBOARD, 'a', 0, NULL);
}

static void PopupNotify(Panel_item item, int value)
{
  char *server = (char *)xv_get(hostItem, PANEL_VALUE);
  char *name = (char *)xv_get(nameItem, PANEL_VALUE);
  numPlayers = (int)xv_get(numPlayersItem, PANEL_VALUE);
  myName = strdup(name);
  xv_set(initFrame, XV_SHOW, FALSE, NULL);
  Debug('i', "Startup as %d players, name %s, server %s\n", numPlayers,
	name, server);
  CivInit(item == RestoreButton ? RestoreSaveFile : StartNewGame,
	  strdup(server));
}

void ChoiceEvent(Panel_item item, char *string, caddr_t client_data,
		 Panel_list_op op)
{
  if (op != 1) return;
  (*screen->KeyPressed)(SELECT, 0, 0, string);
  Panel panel = (Panel)xv_get(item, XV_OWNER, NULL);
  Frame frame = (Frame)xv_get(panel, XV_OWNER, NULL);
  xv_destroy_safe(frame);
}

void X11Screen::CreateChoiceFrame(char *mesg, List<charp> choices)
{
  Frame cFrame;
  Panel panel;
  Panel_item list;
  cFrame = (Frame)xv_create(frame, FRAME_CMD, FRAME_LABEL, "Select ...",
			    XV_X, (int)xv_get(frame, XV_X, NULL)+40,
			    XV_Y, (int)xv_get(frame, XV_Y, NULL)+20,
			    NULL);
  panel = (Panel)xv_get(cFrame, FRAME_CMD_PANEL, NULL);
  list = (Panel_item)xv_create(panel, PANEL_LIST,
			       PANEL_LIST_ROW_HEIGHT, 16,
			       PANEL_LIST_DISPLAY_ROWS, 8,
			       PANEL_LIST_TITLE, mesg,
			       PANEL_NOTIFY_PROC, ChoiceEvent,
			       NULL);
  Lister<charp> choicel(choices);
  int row = 0;
  while (choicel) {
    xv_set(list, PANEL_LIST_INSERT, row, NULL);
    xv_set(list, PANEL_LIST_STRING, row, choicel.Elem(), NULL);
    ++row;
    choicel.Next();
  }
  window_fit(panel);
  window_fit(cFrame);
  xv_set(cFrame, XV_SHOW, TRUE, NULL);
}

static void MessageNotify(Panel_item item, int value)
{
  (*screen->KeyPressed)(MESG_READ, 0, 0, NULL);
  Panel panel = (Panel)xv_get(item, XV_OWNER, NULL);
  Frame frame = (Frame)xv_get(panel, XV_OWNER, NULL);
  xv_destroy_safe(frame);
}

void X11Screen::MessageBox(char *mesg)
{
  Frame cFrame;
  Panel panel;
  cFrame = (Frame)xv_create(frame, FRAME_CMD,
			    XV_X, (int)xv_get(frame, XV_X, NULL)+58,
			    XV_Y, (int)xv_get(frame, XV_Y, NULL)+20,
			    NULL);
  panel = (Panel)xv_get(cFrame, FRAME_CMD_PANEL, NULL);
  xv_create(panel, PANEL_MESSAGE,
	    PANEL_LABEL_WIDTH, 204,
	    PANEL_LABEL_STRING, mesg,
	    NULL);
  xv_create(panel, PANEL_BUTTON,
	    PANEL_LABEL_STRING, "Ok",
	    PANEL_NOTIFY_PROC, MessageNotify,
	    NULL);
  window_fit(panel);
  window_fit(cFrame);
  xv_set(cFrame, XV_SHOW, TRUE, NULL);
  delete mesg;
}
