/*
 * Copyright (c) 1995 Sun Microsystems, Inc.
 * All rights reserved.
 * 
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 * 
 * IN NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
 * OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF SUN
 * MICROSYSTEMS, INC. HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * SUN MICROSYSTEMS, INC. SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THE SOFTWARE PROVIDED
 * HEREUNDER IS ON AN "AS IS" BASIS, AND SUN MICROSYSTEMS, INC. HAS NO
 * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
 * MODIFICATIONS.
 */

#include <stdio.h>
#include <stdlib.h>

#include <tcl.h>
#include <tk.h>
#include "doomarena.h"


/*
 * CmapHack
 */

#define NUM_FREE 19

static unsigned long pixels[256];
static int numPixels = 0;

static int
CMapHackCmd(ClientData clientData, Tcl_Interp *interp,
	    int argc, char **argv)
{
  char c;
  int i, status, freenum;
  unsigned long *freeptr;
  XColor xcolor;
  Visual *visual;

  /* Make sure we are using an 8-bit PseudoColor visual */
  if (Tk_Depth(Tk_MainWindow(interp)) != 8)
    return TCL_OK;

  visual = Tk_Visual(Tk_MainWindow(interp));
  if (visual->class != PseudoColor) {
    sprintf(interp->result, "%s: Visual must be PseudoColor\n", argv[0]);
    return TCL_ERROR;
  }

  if (argc != 2 && argc != 3) {
    sprintf(interp->result,
	    "wrong # args: should be \"%s option ?arg?\"", argv[0]);
    return TCL_ERROR;
  }

  c = argv[1][0];
  if (c == 'a' && !strcmp(argv[1], "alloc")) {
    XColor *colorDefaults;
    int i, pixel, colorCount = 0;

    if (argc == 3) {
      XColor dummy;
      int colorArgc, listArgc;
      char **colorArgv, **listArgv;

      if (Tcl_SplitList(interp, argv[2], &colorArgc, &colorArgv) != TCL_OK)
	return TCL_ERROR;
      colorCount = colorArgc;

      if (colorArgc > 0) {
	colorDefaults = (XColor *)malloc(colorArgc * sizeof(XColor));
	if (!colorDefaults) {
	  perror("malloc");
	  exit(1);
	}
      }

      for (i=0; i<colorArgc; i++) {
	if (Tcl_SplitList(interp, colorArgv[i], &listArgc, &listArgv)
	    != TCL_OK)
	  return TCL_ERROR;

	if (listArgc != 2) {
	  if (listArgc > 0)
	    free(listArgv);
	  free(colorDefaults);
	  free(colorArgv);
	  sprintf(interp->result,
		  "%s: Each color list entry must have exactly two items",
		  argv[0]);
	  return TCL_ERROR;
	}

	if (XLookupColor(Tk_Display(Tk_MainWindow(interp)),
			  Tk_Colormap(Tk_MainWindow(interp)),
			  listArgv[1], &dummy, &(colorDefaults[i]))) {
	  if (Tcl_GetInt(interp, listArgv[0], &pixel)
	      != TCL_OK) {
	    return TCL_ERROR;
	  }
	  colorDefaults[i].pixel = (short)pixel;
	  colorDefaults[i].red -= 256;
	  if (colorDefaults[i].red < 0)
	    colorDefaults[i].red = 0;
	  colorDefaults[i].green -= 256;
	  if (colorDefaults[i].green < 0)
	    colorDefaults[i].green = 0;
	  colorDefaults[i].blue -= 256;
	  if (colorDefaults[i].blue < 0)
	    colorDefaults[i].blue = 0;
	} else {
	  colorDefaults[i].pixel = 0;
	}

	free(listArgv);
      }

      if (colorArgc > 0)
	free(colorArgv);
    }

    /* Allocate all but the last 16 entries */
    xcolor.pixel = 0;
    do {
      xcolor.red = (short)(drand48() * 65535.0);
      xcolor.green = (short)(drand48() * 65535.0);
      xcolor.blue = (short)(drand48() * 65535.0);

      if (colorCount > 0) {
	for (i=0; i<colorCount; i++) {
	  if (colorDefaults[i].pixel == (xcolor.pixel + 1)) {
	    xcolor.red = colorDefaults[i].red;
	    xcolor.green = colorDefaults[i].green;
	    xcolor.blue = colorDefaults[i].blue;
	    break;
	  }
	}
      }

      status = XAllocColor(Tk_Display(Tk_MainWindow(interp)),
			   Tk_Colormap(Tk_MainWindow(interp)),
			   &xcolor);
      pixels[numPixels++] = xcolor.pixel;
    } while(status);
    numPixels--;

    if (numPixels > NUM_FREE) {
      freeptr = &(pixels[numPixels-NUM_FREE]);
      freenum = NUM_FREE;
    }
    else {
      freeptr = pixels;
      freenum = numPixels;
    }

    XFreeColors(Tk_Display(Tk_MainWindow(interp)),
		Tk_Colormap(Tk_MainWindow(interp)),
		freeptr, freenum, 0L);
    numPixels -= freenum;
  }
  else if (c == 'f' && !strcmp(argv[1], "free")) {
    if (argc != 2) {
      sprintf(interp->result,
	      "wrong # args: must be \"%s %s\"", argv[0], argv[1]);
      return TCL_ERROR;
    }

    XFreeColors(Tk_Display(Tk_MainWindow(interp)),
		Tk_Colormap(Tk_MainWindow(interp)),
		pixels, numPixels, 0L);
    numPixels = 0;
  }
  else if (c == 'c' && !strcmp(argv[1], "color")) {
    if (argc != 3) {
      sprintf(interp->result,
	      "wrong # args: must be \"%s %s index\"", argv[0], argv[1]);
      return TCL_ERROR;
    }

    if (Tcl_GetInt(interp, argv[2], (int *)&xcolor.pixel) != TCL_OK)
      return TCL_ERROR;

    XQueryColor(Tk_Display(Tk_MainWindow(interp)),
		Tk_Colormap(Tk_MainWindow(interp)),
		&xcolor);

    sprintf(interp->result, "#%04X%04X%04X",
	    xcolor.red, xcolor.green, xcolor.blue);
  }
  else {
    sprintf(interp->result,
	    "bad option \"%s\": must be alloc, free, or color", argv[1]);
    return TCL_ERROR;
  }

  return TCL_OK;
}


/*
 * wmprop tracing
 */

static char *
WmPropTrace(ClientData clientData, Tcl_Interp *interp,
	    char *name1, char *name2, int flags)
{
  Display *display;
  Window win;
  char *propname = name2;
  char *propvalue = Tcl_GetVar2(interp, name1, name2, TCL_GLOBAL_ONLY);
  Atom property, type, value;

  if (!propname || !propvalue)
    return "Error in WmPropTrace";

  Tk_MakeWindowExist(Tk_MainWindow(interp));
  display = Tk_Display(Tk_MainWindow(interp));
  win = Tk_WindowId(Tk_MainWindow(interp));

  property = XInternAtom(display, propname, False);
  type = XInternAtom(display, "ATOM", False);
  value = XInternAtom(display, propvalue, False);
  XChangeProperty(display, win, property, type, 32, PropModeReplace,
		  (const unsigned char *)(&value), 1);

  return NULL;
}


/*
 * ScreenCount
 */

static int
ScreenCountCmd(ClientData clientData, Tcl_Interp *interp,
	       int argc, char **argv)
{
  if (argc != 1) {
    sprintf(interp->result,
	    "wrong # args: should be \"%s\"", argv[0]);
    return TCL_ERROR;
  }

  sprintf(interp->result, "%d",
	  ScreenCount(Tk_Display(Tk_MainWindow(interp))));
  return TCL_OK;
}

/*
 * KeySym command
 */

#define KEYSYM_TO_STRING 1
#define STRING_TO_KEYSYM 2

static int
KeySymCmd(ClientData clientData, Tcl_Interp *interp,
	  int argc, char **argv)
{
  if (argc != 2) {
    sprintf(interp->result,
	    "wrong # args: should be \"%s keysym\"",
	    argv[0]);
    return TCL_ERROR;
  }

  if ((int)clientData == KEYSYM_TO_STRING) {
    char *name;
    int number;

    if (Tcl_GetInt(interp, argv[1], &number) != TCL_OK)
      return TCL_ERROR;

    name = XKeysymToString(number);
    if (name)
      interp->result = name;
  }
  else {
    int number;
    number = XStringToKeysym(argv[1]);
    sprintf(interp->result, "%d", number);
  }

  return TCL_OK;
}

/*
 * Init
 */

int
Doomarena_Init(Tcl_Interp *interp)
{
  Tcl_CreateCommand(interp, "gethostbyname", GetHostCmd,
		    (ClientData)BY_NAME, NULL);
  Tcl_CreateCommand(interp, "gethostbyaddr", GetHostCmd,
		    (ClientData)BY_ADDR, NULL);

  Tcl_CreateCommand(interp, "getpwnam", PWCmd, (ClientData)PW_NAM, NULL);
  Tcl_CreateCommand(interp, "getpwuid", PWCmd, (ClientData)PW_UID, NULL);
  Tcl_CreateCommand(interp, "getrlimit", RLimitCmd,
		    (ClientData)RLIMIT_GET, NULL);
  Tcl_CreateCommand(interp, "setrlimit", RLimitCmd,
		    (ClientData)RLIMIT_SET, NULL);
  Tcl_CreateCommand(interp, "sysconf", SysConfCmd, NULL, NULL);
  Tcl_CreateCommand(interp, "sysinfo", SysInfoCmd, NULL, NULL);
  Tcl_CreateCommand(interp, "poll", PollCmd, NULL, NULL);

  Tcl_CreateCommand(interp, "cmaphack", CMapHackCmd, NULL, NULL);
  Tcl_CreateCommand(interp, "screencount", ScreenCountCmd, NULL, NULL);
  Tcl_CreateCommand(interp, "KeysymToString", KeySymCmd,
		    (ClientData)KEYSYM_TO_STRING, NULL);
  Tcl_CreateCommand(interp, "StringToKeysym", KeySymCmd,
		    (ClientData)STRING_TO_KEYSYM, NULL);
  Tcl_TraceVar(interp, "wmprop", TCL_TRACE_WRITES | TCL_GLOBAL_ONLY,
	       WmPropTrace, NULL);

  Tcl_CreateCommand( interp, "audio", AudioCmd, NULL, NULL);

  Tcl_CreateCommand(interp, "tcp_server_start", ServerStartCmd, NULL, NULL);
  Tcl_CreateCommand(interp, "tcp_server_accept", ServerAcceptCmd, NULL, NULL);
  Tcl_CreateCommand(interp, "tcp_server_connect",
		    ServerConnectCmd, NULL, NULL);

  Tcl_CreateCommand(interp, "DoomStats", DoomStatsCmd, NULL, NULL);

  return TCL_OK;
}
