/*
 * MapInfo.c -- MapInfo interface.
 *
 * Authors		: Patrick Lecoanet.
 * Creation date	: 
 *
 * $Id: MapInfo.c,v 1.22 2004/03/03 16:38:18 lecoanet Exp $
 */

/*
 *  Copyright (c) 1993 - 1999 CENA, Patrick Lecoanet --
 *
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this code; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#ifndef _WIN32
#include <sys/param.h>
#include <netinet/in.h>
#else
#include <winsock2.h>
#endif

#include "MapInfo.h"
#include "tkZinc.h"

#include <memory.h>
#include <math.h>



static const char rcsid[] = "$Id: MapInfo.c,v 1.22 2004/03/03 16:38:18 lecoanet Exp $";
static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $";


/*
 *-----------------------------------------------------------------------
 *
 * New types
 *
 *-----------------------------------------------------------------------
 */
typedef struct {
  ZnPtr			tag;
  ZnMapInfoLineStyle	style;
  int			width;
  ZnPoint		center;
  unsigned int		radius;
  int			start_angle;
  int			extend;
} ZnMapInfoArcStruct, *ZnMapInfoArc;

typedef struct {
  ZnPtr			tag;
  ZnMapInfoLineStyle	style;
  int			width;
  ZnPoint		from;
  ZnPoint		to;
  unsigned int		num_marks;
  ZnPoint		*marks;
} ZnMapInfoLineStruct, *ZnMapInfoLine;

typedef struct {
  ZnPtr			tag;
  ZnPoint		at;
  char          	symbol[2];
} ZnMapInfoSymbolStruct, *ZnMapInfoSymbol;

typedef struct {
  ZnPtr			tag;
  ZnMapInfoTextStyle	text_style;
  ZnMapInfoLineStyle	line_style;
  ZnPoint		at;
  char          	*text;
} ZnMapInfoTextStruct, *ZnMapInfoText;

typedef struct {
  char		*name;
  ZnList	lines;
  ZnList	symbols;
  ZnList	texts;
  ZnList	arcs;
} ZnMapInfoStruct, *ZnMapInfo;


#define	MARKERS_SPACING		80.0	/* 10 nautic miles in 1/8 of a mile */
#define	BASE_ALLOC_SIZE		8


/*
 *-----------------------------------------------------------------------
 *
 * Macros.
 * 
 *-----------------------------------------------------------------------
 */
#define NOT_MARKED_STYLE(style)						\
((style) == ZnMapInfoLineMarked ? ZnMapInfoLineSimple : (style));


/*
 *-----------------------------------------------------------------------
 *
 * ComputeLineMarks --
 *	Add marks to a line in the marks substructure.
 *
 *-----------------------------------------------------------------------
 */

static void
ComputeLineMarks(ZnMapInfoLine	marked_line)
{
  ZnDim	length;
  ZnPos	x_from = marked_line->from.x;
  ZnPos	y_from = marked_line->from.y;
  ZnPos	x_to = marked_line->to.x;
  ZnPos	y_to = marked_line->to.y;
  ZnPos	delta_x = x_from - x_to;
  ZnPos	delta_y = y_from - y_to;
  ZnPos	step_x, step_y;
  unsigned int	j;

  length = sqrt(delta_x * delta_x + delta_y * delta_y);
  step_x = (x_to - x_from) * MARKERS_SPACING / length;
  step_y = (y_to - y_from) * MARKERS_SPACING / length;
  marked_line->num_marks = (int) (length / MARKERS_SPACING);

  /* We don't want markers at ends, so we get rid of the last one
     if it is at an end */
  if (fmod(length, MARKERS_SPACING) == 0.0) {
    (marked_line->num_marks)--;
  }

  if (marked_line->num_marks) {
    marked_line->marks = ZnMalloc(marked_line->num_marks * sizeof(ZnPoint));
  }
  
  for (j = 0; j < marked_line->num_marks; j++) {
    marked_line->marks[j].x = x_from + ((j + 1) * step_x);
    marked_line->marks[j].y = y_from + ((j + 1) * step_y);
  }
}


static ZnMapInfoId
ZnMapInfoCreate(char	*name)
{
  ZnMapInfo	new_map;

  new_map = ZnMalloc(sizeof(ZnMapInfoStruct));
  memset((char *) new_map, 0, sizeof(ZnMapInfoStruct));
  if (!name) {
    name = "";
  }
  new_map->name = (char *) ZnMalloc(strlen(name)+1);
  /*printf("Nouvelle MapInfo: %s\n", name);*/
  strcpy(new_map->name, name);
  
  return((ZnMapInfoId) new_map);
}


static char *
ZnMapInfoName(ZnMapInfoId	map_info)
{
  if (!map_info) {
    return "";
  }
  return ((ZnMapInfo) map_info)->name;
}


static ZnMapInfoId
ZnMapInfoDuplicate(ZnMapInfoId	map_info)
{
  ZnMapInfo	cur_map = map_info;
  ZnMapInfo	new_map;
  unsigned int	i, num_lines, num_texts;
  ZnMapInfoText	new_texts, cur_texts;
  ZnMapInfoLine	new_lines, cur_lines;

  if (!cur_map) {
    return ((ZnMapInfoId) NULL);
  }
  
  new_map = ZnMapInfoCreate(cur_map->name);

  if (cur_map->lines) {
    new_map->lines = ZnListDuplicate(cur_map->lines);

    cur_lines = ZnListArray(cur_map->lines);
    new_lines = ZnListArray(new_map->lines);
    num_lines = ZnListSize(cur_map->lines);

    for (i = 0; i < num_lines; i++) {
      if (new_lines[i].style == ZnMapInfoLineMarked) {
	new_lines[i].marks = ZnMalloc(new_lines[i].num_marks * sizeof(ZnPoint));
	memcpy((char *) new_lines[i].marks,
	       (char *) cur_lines[i].marks,
	       new_lines[i].num_marks * sizeof(ZnPoint));
      }
    }
  }
  if (cur_map->symbols) {
    new_map->symbols = ZnListDuplicate(cur_map->symbols);
  }
  if (cur_map->texts) {
    new_map->texts = ZnListDuplicate(cur_map->texts);

    cur_texts = ZnListArray(cur_map->texts);
    new_texts = ZnListArray(new_map->texts);
    num_texts = ZnListSize(cur_map->texts);
    
    for (i = 0; i < num_texts; i++) {
      new_texts[i].text = ZnMalloc(strlen(cur_texts[i].text) + 1);
      strcpy(new_texts[i].text, cur_texts[i].text);
    }
  }
  if (cur_map->arcs) {
    new_map->arcs = ZnListDuplicate(cur_map->arcs);
  }

  return((ZnMapInfoId) new_map);
}


static void
ZnMapInfoDelete(ZnMapInfoId	map_info)
{
  ZnMapInfo	cur_map = map_info;
  unsigned int	i, num_texts, num_lines;
  ZnMapInfoText	cur_texts;
  ZnMapInfoLine	cur_lines;
  
  if (cur_map) {
    if (cur_map->texts) {
      num_texts = ZnListSize(cur_map->texts);
      cur_texts = ZnListArray(cur_map->texts);

      for (i = 0; i < num_texts; i++) {
	ZnFree(cur_texts[i].text);
      }
      
      ZnListFree(cur_map->texts);
    }

    if (cur_map->lines) {
      num_lines = ZnListSize(cur_map->lines);
      cur_lines = ZnListArray(cur_map->lines);

      for (i = 0; i < num_lines; i++) {
	if (cur_lines[i].style == ZnMapInfoLineMarked) {
	  ZnFree(cur_lines[i].marks);
	}
      }

      ZnListFree(cur_map->lines);
    }

    if (cur_map->symbols) {
      ZnListFree(cur_map->symbols);
    }
    
    if (cur_map->arcs) {
      ZnListFree(cur_map->arcs);
    }
    
    ZnFree(cur_map->name);
    ZnFree(cur_map);
  }
}

static void
ZnMapInfoEmpty(ZnMapInfoId	map_info)
{
  ZnMapInfo cur_map = map_info;

  if (cur_map) {
    if (cur_map->texts) {
      ZnListEmpty(cur_map->texts);
    }
    if (cur_map->lines) {
      ZnListEmpty(cur_map->lines);
    }
    if (cur_map->symbols) {
      ZnListEmpty(cur_map->symbols);
    }
    if (cur_map->arcs) {
      ZnListEmpty(cur_map->arcs);
    }
  }
}


static void
ZnMapInfoAddLine(ZnMapInfoId	map_info,
		 unsigned int	index,
		 ZnPtr		tag,
		 ZnMapInfoLineStyle line_style,
		 ZnDim		line_width,
		 ZnPos		x_from,
		 ZnPos		y_from,
		 ZnPos		x_to,
		 ZnPos		y_to)
{
  ZnMapInfo		cur_map = map_info;
  ZnMapInfoLineStruct	line_struct;

  if (cur_map) {
    if (!cur_map->lines) {
      cur_map->lines = ZnListNew(16, sizeof(ZnMapInfoLineStruct));
    }

    line_struct.style = line_style;
    if (line_width == 1.0) {
      line_struct.width = 0;
    }
    else {
      line_struct.width = line_width;
    }
    line_struct.tag = tag;
    line_struct.from.x = x_from;
    line_struct.from.y = y_from;
    line_struct.to.x = x_to;
    line_struct.to.y = y_to;
    /*printf("Ajout de la ligne: %d %d %d %d\n", x_from, y_from, x_to, y_to);*/
    if (line_style == ZnMapInfoLineMarked) {
      ComputeLineMarks(&line_struct);
    }
    
    ZnListAdd(cur_map->lines, &line_struct, index);
  }
}


static void
ZnMapInfoReplaceLine(ZnMapInfoId	map_info,
		     unsigned int	index,
		     ZnPtr		tag,
		     ZnMapInfoLineStyle	line_style,
		     ZnDim		line_width,
		     ZnPos		x_from,
		     ZnPos		y_from,
		     ZnPos		x_to,
		     ZnPos		y_to)
{
  ZnMapInfo	cur_map = map_info;
  ZnMapInfoLine	line_ptr;

  if (cur_map && cur_map->lines) {
    line_ptr = ZnListAt(cur_map->lines, index);
    if (line_ptr) {
      if (line_ptr->style == ZnMapInfoLineMarked) {
	ZnFree(line_ptr->marks);
      }

      line_ptr->style = line_style;
      if (line_width == 1.0) {
        line_ptr->width = 0;
      }
      else {
        line_ptr->width = line_width;
      }
      line_ptr->tag = tag;
      line_ptr->from.x = x_from;
      line_ptr->from.y = y_from;
      line_ptr->to.x = x_to;
      line_ptr->to.y = y_to;

      if (line_ptr->style == ZnMapInfoLineMarked) {
	ComputeLineMarks(line_ptr);
      }
    }
  }
}


static void
ZnMapInfoRemoveLine(ZnMapInfoId		map_info,
		    unsigned int	index)
{
  ZnMapInfo	cur_map = map_info;
  ZnMapInfoLine	line_ptr;

  if (cur_map && cur_map->lines) {
    line_ptr = ZnListAt(cur_map->lines, index);
    if (line_ptr) {
      if (line_ptr->style == ZnMapInfoLineMarked) {
	ZnFree(line_ptr->marks);
      }

      ZnListDelete(cur_map->lines, index);
    }
  }
}


void
ZnMapInfoGetLine(ZnMapInfoId		map_info,
		 unsigned int		index,
		 ZnPtr			*tag,
		 ZnMapInfoLineStyle	*line_style,
		 ZnDim			*line_width,
		 ZnPos			*x_from,
		 ZnPos			*y_from,
		 ZnPos			*x_to,
		 ZnPos			*y_to)
{
  ZnMapInfo	cur_map = map_info;
  ZnMapInfoLine	line_ptr;

  if (cur_map && cur_map->lines) {
    line_ptr = ZnListAt(cur_map->lines, index);
    if (line_ptr) {
      if (tag) {
	*tag = line_ptr->tag;
      }
      if (line_style) {
	*line_style = line_ptr->style;
      }
      if (line_width) {
        if (line_ptr->width == 1.0) {
	  *line_width = 0;
	}
        else {
	  *line_width = line_ptr->width;
	}
      }
      if (x_from) {
	*x_from = line_ptr->from.x;
      }
      if (y_from) {
	*y_from = line_ptr->from.y;
      }
      if (x_to) {
	*x_to = line_ptr->to.x;
      }
      if (y_to) {
	*y_to = line_ptr->to.y;
      }
    }
  }
}


void
ZnMapInfoGetMarks(ZnMapInfoId	map_info,
		  unsigned int	index,
		  ZnPoint 	**marks,
		  unsigned int	*num_marks)
{
  ZnMapInfo	cur_map = map_info;
  ZnMapInfoLine	line_ptr;

  if (cur_map && cur_map->lines) {
    line_ptr = ZnListAt(cur_map->lines, index);
    if (line_ptr && line_ptr->style == ZnMapInfoLineMarked) {
      if (marks) {
	*marks = line_ptr->marks;
      }
      if (num_marks) {
	*num_marks = line_ptr->num_marks;
      }
    }
  }
}


unsigned int
ZnMapInfoNumLines(ZnMapInfoId	map_info)
{
  ZnMapInfo	cur_map = map_info;

  if (cur_map && cur_map->lines) {
    return ZnListSize(cur_map->lines);
  }
  else {
    return 0;
  }
}
 
  
static void
ZnMapInfoAddSymbol(ZnMapInfoId	map_info,
		   unsigned int	index,
		   ZnPtr	tag,
		   ZnPos	x,
		   ZnPos	y,
		   int		symbol)
{
  ZnMapInfo		cur_map = map_info;
  ZnMapInfoSymbolStruct	symbol_struct;

  if (cur_map) {
    if (!cur_map->symbols) {
      cur_map->symbols = ZnListNew(16, sizeof(ZnMapInfoSymbolStruct));
    }
    
    symbol_struct.tag = tag;
    symbol_struct.at.x = x;
    symbol_struct.at.y = y;
    symbol_struct.symbol[0] = symbol;
    symbol_struct.symbol[1] = '\0';
    
    ZnListAdd(cur_map->symbols, &symbol_struct, index);
  }
}


static void
ZnMapInfoReplaceSymbol(ZnMapInfoId	map_info,
		       unsigned int	index,
		       ZnPtr		tag,
		       ZnPos		x,
		       ZnPos		y,
		       int		symbol)
{
  ZnMapInfo		cur_map = map_info;
  ZnMapInfoSymbolStruct	symbol_struct;

  if (cur_map && cur_map->symbols) {
    symbol_struct.tag = tag;
    symbol_struct.at.x = x;
    symbol_struct.at.y = y;
    symbol_struct.symbol[0] = symbol;
    symbol_struct.symbol[1] = '\0';

    ZnListAtPut(cur_map->symbols, &symbol_struct, index);
  }
}


static void
ZnMapInfoRemoveSymbol(ZnMapInfoId	map_info,
		      unsigned int	index)
{
  ZnMapInfo	cur_map = map_info;

  if (cur_map && cur_map->symbols) {
    ZnListDelete(cur_map->symbols, index);
  }
}


void
ZnMapInfoGetSymbol(ZnMapInfoId	map_info,
		   unsigned int	index,
		   ZnPtr	*tag,
		   ZnPos	*x,
		   ZnPos	*y,
		   char		*symbol)
{
  ZnMapInfo	  cur_map = map_info;
  ZnMapInfoSymbol symbol_ptr;

  if (cur_map && cur_map->symbols) {
    symbol_ptr = ZnListAt(cur_map->symbols, index);
    if (symbol_ptr) {
      if (tag) {
	*tag = symbol_ptr->tag;
      }
      if (x) {
	*x = symbol_ptr->at.x;
      }
      if (y) {
	*y = symbol_ptr->at.y;
      }
      if (symbol) {
	*symbol = symbol_ptr->symbol[0];
      }
    }
  }
}


unsigned int
ZnMapInfoNumSymbols(ZnMapInfoId	map_info)
{
  ZnMapInfo cur_map = map_info;

  if (cur_map && cur_map->symbols) {
    return ZnListSize(cur_map->symbols);
  }
  else {
    return 0;
  }
}
 
  
static void
ZnMapInfoAddText(ZnMapInfoId	map_info,
		 unsigned int	index,
		 ZnPtr		tag,
		 ZnMapInfoTextStyle text_style,
		 ZnMapInfoLineStyle line_style,
		 ZnPos		x,
		 ZnPos		y,
		 char		*text)
{
  ZnMapInfo		cur_map = map_info;
  ZnMapInfoTextStruct	text_struct;

  if (cur_map) {
    if (!cur_map->texts) {
      cur_map->texts = ZnListNew(16, sizeof(ZnMapInfoTextStruct));
    }

    text_struct.tag	   = tag;
    text_struct.text_style = text_style;
    text_struct.line_style = NOT_MARKED_STYLE(line_style);
    text_struct.at.x	   = x;
    text_struct.at.y	   = y;
    text_struct.text	   = ZnMalloc(strlen(text) + 1);
    strcpy(text_struct.text, text);
    
    ZnListAdd(cur_map->texts, &text_struct, index);
  }
}


static void
ZnMapInfoReplaceText(ZnMapInfoId	map_info,
		     unsigned int	index,
		     ZnPtr		tag,
		     ZnMapInfoTextStyle	text_style,
		     ZnMapInfoLineStyle	line_style,
		     ZnPos		x,
		     ZnPos		y,
		     char		*text)
{
  ZnMapInfo	cur_map = map_info;
  ZnMapInfoText	text_ptr;

  if (cur_map && cur_map->texts) {
    text_ptr = ZnListAt(cur_map->texts, index);
    if (text_ptr) {
      ZnFree(text_ptr->text);

      text_ptr->tag	   = tag;
      text_ptr->text_style = text_style;
      text_ptr->line_style = NOT_MARKED_STYLE(line_style);
      text_ptr->at.x	   = x;
      text_ptr->at.y	   = y;
      text_ptr->text	   = ZnMalloc(strlen(text) + 1);
      strcpy(text_ptr->text, text);
    }
  }
}


static void
ZnMapInfoRemoveText(ZnMapInfoId		map_info,
		    unsigned int	index)
{
  ZnMapInfo	cur_map = map_info;
  ZnMapInfoText	text_ptr;

  if (cur_map && cur_map->texts) {
    text_ptr = ZnListAt(cur_map->texts, index);
    if (text_ptr) {
      ZnFree(text_ptr->text);

      ZnListDelete(cur_map->texts, index);
    }
  }
}


void
ZnMapInfoGetText(ZnMapInfoId	map_info,
		 unsigned int	index,
		 ZnPtr		*tag,
		 ZnMapInfoTextStyle *text_style,
		 ZnMapInfoLineStyle *line_style,
		 ZnPos		*x,
		 ZnPos		*y,
		 char		**text)
{
  ZnMapInfo	cur_map = map_info;
  ZnMapInfoText	text_ptr;

  if (cur_map && cur_map->texts) {
    text_ptr = ZnListAt(cur_map->texts, index);
    if (text_ptr) {
      if (tag) {
	*tag = text_ptr->tag;
      }
      if (text_style) {
	*text_style = text_ptr->text_style;
      }
      if (line_style) {
	*line_style = text_ptr->line_style;
      }
      if (x) {
	*x = text_ptr->at.x;
      }
      if (y) {
	*y = text_ptr->at.y;
      }
      if (text) {
	*text = text_ptr->text;
      }
    }
  }
}


unsigned int
ZnMapInfoNumTexts(ZnMapInfoId	map_info)
{
  ZnMapInfo cur_map = map_info;

  if (cur_map && cur_map->texts) {
    return ZnListSize(cur_map->texts);
  }
  else {
    return 0;
  }
}


static void
ZnMapInfoAddArc(ZnMapInfoId	map_info,
		unsigned int	index,
		ZnPtr		tag,
		ZnMapInfoLineStyle line_style,
		ZnDim		line_width,
		ZnPos		center_x,
		ZnPos		center_y,
		ZnDim		radius,
		ZnReal		start_angle,
		ZnReal		extend)
{
  ZnMapInfo		cur_map = map_info;
  ZnMapInfoArcStruct	arc_struct;
  
  if (cur_map) {
    if (!cur_map->arcs) {
      cur_map->arcs = ZnListNew(16, sizeof(ZnMapInfoArcStruct));
    }

    arc_struct.style = NOT_MARKED_STYLE(line_style);
    if (line_width == 1.0) {
      arc_struct.width = 0;
    }
    else {
      arc_struct.width = line_width;
    }
    arc_struct.tag = tag;
    arc_struct.center.x = center_x;
    arc_struct.center.y = center_y;
    arc_struct.radius = radius;
    arc_struct.start_angle = start_angle;
    arc_struct.extend = extend;
    
    ZnListAdd(cur_map->arcs, &arc_struct, index);
  }
}


static void
ZnMapInfoReplaceArc(ZnMapInfoId		map_info,
		    unsigned int	index,
		    ZnPtr		tag,
		    ZnMapInfoLineStyle	line_style,
		    ZnDim		line_width,
		    ZnPos		center_x,
		    ZnPos		center_y,
		    ZnDim		radius,
		    ZnReal		start_angle,
		    ZnReal		extend)
{
  ZnMapInfo	cur_map = map_info;
  ZnMapInfoArc	arc_ptr;
  
  if (cur_map && cur_map->arcs) {
    arc_ptr = ZnListAt(cur_map->arcs, index);
    if (arc_ptr) {
      arc_ptr->style = NOT_MARKED_STYLE(line_style);
      if (line_width == 1.0) {
        arc_ptr->width = 0;
      }
      else {
        arc_ptr->width = line_width;
      }
      arc_ptr->tag = tag;
      arc_ptr->center.x = center_x;
      arc_ptr->center.y = center_y;
      arc_ptr->radius = radius;
      arc_ptr->start_angle = start_angle;
      arc_ptr->extend = extend;      
    }
  }
}


static void
ZnMapInfoRemoveArc(ZnMapInfoId	map_info,
		   unsigned int	index)
{
  ZnMapInfo	cur_map = map_info;
  ZnMapInfoArc	arc_ptr;

  if (cur_map && cur_map->arcs) {
    arc_ptr = ZnListAt(cur_map->arcs, index);
    if (arc_ptr) {
      ZnListDelete(cur_map->arcs, index);
    }
  }
}


void
ZnMapInfoGetArc(ZnMapInfoId	map_info,
		unsigned int	index,
		ZnPtr		*tag,
		ZnMapInfoLineStyle *line_style,
		ZnDim		*line_width,
		ZnPos		*center_x,
		ZnPos		*center_y,
		ZnDim		*radius,
		ZnReal		*start_angle,
		ZnReal		*extend)
{
  ZnMapInfo	cur_map = map_info;
  ZnMapInfoArc	arc_ptr;

  if (cur_map && cur_map->arcs) {
    arc_ptr = ZnListAt(cur_map->arcs, index);
    if (arc_ptr) {
      if (tag) {
	*tag = arc_ptr->tag;
      }
      if (line_style) {
	*line_style = arc_ptr->style;
      }
      if (line_width) {
        if (arc_ptr->width == 1.0) {
	  *line_width = 0;
	}
        else {
	  *line_width = arc_ptr->width;
	}
      }
      if (center_x) {
	*center_x = arc_ptr->center.x;
      }
      if (center_y) {
	*center_y = arc_ptr->center.y;
      }
      if (radius) {
	*radius = arc_ptr->radius;
      }
      if (start_angle) {
	*start_angle = arc_ptr->start_angle;
      }
      if (extend) {
	*extend = arc_ptr->extend;
      }
    }
  }
}

unsigned int
ZnMapInfoNumArcs(ZnMapInfoId	map_info)
{
  ZnMapInfo cur_map = map_info;

  if (cur_map && cur_map->arcs) {
    return ZnListSize(cur_map->arcs);
  }
  else {
    return 0;
  }
}

static void
ZnMapInfoScale(ZnMapInfoId	map_info,
	       ZnReal		factor)
{
  ZnMapInfo	mp = map_info;
  int		i, num;
  ZnMapInfoLine	line_ptr;
  ZnMapInfoSymbol sym_ptr;
  ZnMapInfoText	text_ptr;
  ZnMapInfoArc	arc_ptr;

  if (mp && mp->lines) {
    num = ZnListSize(mp->lines);
    line_ptr = ZnListArray(mp->lines);
    for (i = 0; i < num; i++, line_ptr++) {
      line_ptr->from.x *= factor;
      line_ptr->from.y *= factor;
      line_ptr->to.x *= factor;
      line_ptr->to.y *= factor;
    }
  }
  if (mp && mp->symbols) {
    num = ZnListSize(mp->symbols);
    sym_ptr = ZnListArray(mp->symbols);
    for (i = 0; i < num; i++, sym_ptr++) {
      sym_ptr->at.x *= factor;
      sym_ptr->at.y *= factor;
    }
  }
  if (mp && mp->texts) {
    num = ZnListSize(mp->texts);
    text_ptr = ZnListArray(mp->texts);
    for (i = 0; i < num; i++, text_ptr++) {
      text_ptr->at.x *= factor;
      text_ptr->at.y *= factor;
    }
  }
  if (mp && mp->arcs) {
    num = ZnListSize(mp->arcs);
    arc_ptr = ZnListArray(mp->arcs);
    for (i = 0; i < num; i++, arc_ptr++) {
      arc_ptr->center.x *= factor;
      arc_ptr->center.y *= factor;
      arc_ptr->radius *= factor;
    }
  }
}

static void
ZnMapInfoTranslate(ZnMapInfoId	map_info,
		   ZnPos	x,
		   ZnPos	y)
{
  ZnMapInfo	mp = map_info;
  int		i, num;
  ZnMapInfoLine	line_ptr;
  ZnMapInfoSymbol sym_ptr;
  ZnMapInfoText	text_ptr;
  ZnMapInfoArc	arc_ptr;

  if (mp && mp->lines) {
    num = ZnListSize(mp->lines);
    line_ptr = ZnListArray(mp->lines);
    for (i = 0; i < num; i++, line_ptr++) {
      line_ptr->from.x += x;
      line_ptr->from.y += y;
      line_ptr->to.x += x;
      line_ptr->to.y += y;
    }
  }
  if (mp && mp->symbols) {
    num = ZnListSize(mp->symbols);
    sym_ptr = ZnListArray(mp->symbols);
    for (i = 0; i < num; i++, sym_ptr++) {
      sym_ptr->at.x += x;
      sym_ptr->at.y += y;
    }
  }
  if (mp && mp->texts) {
    num = ZnListSize(mp->texts);
    text_ptr = ZnListArray(mp->texts);
    for (i = 0; i < num; i++, text_ptr++) {
      text_ptr->at.x += x;
      text_ptr->at.y += y;
    }
  }
  if (mp && mp->arcs) {
    num = ZnListSize(mp->arcs);
    arc_ptr = ZnListArray(mp->arcs);
    for (i = 0; i < num; i++, arc_ptr++) {
      arc_ptr->center.x += x;
      arc_ptr->center.y += y;
    }
  }
}



#define TEXT_SIZE		256
#define ntohi(n) ntohl((n))

/*
 *-----------------------------------------------------------------------
 *
 * Videomap record definition. Ints are assumed to be 4 bytes.
 *
 *-----------------------------------------------------------------------
 */

typedef struct {
  int	id;		/* Map id (internal) */
  int	dashed;		/* Tell if vectors are dashed (exclusive with marked) */
  int	expanded;	/* Device coordinates or world coordinates (ignored now) */
  int   marked;         /* Tell if vectors are marked (exclusive with dashed) */
  int   color;          /* drawing color (ignored now) */
  int   elements[50];	/* Element type ('P', 'V', 'T') */
  int   x[50];          /* Coordinates if 'P' or 'V' */
  int   y[50];
  int   symbol[50];	/* Filled if 'P' or 'V' */
  int   text[50];       /* Low order byte is ascii char if 'T' */
  int   num_elements;   /* Number of elements */
} VideoMap;

/*
 *-----------------------------------------------------------------------
 *
 * ReorderVidomap - reorder integers according to the endianess
 *
 *-----------------------------------------------------------------------
 */

static void
ReorderVidomap(VideoMap *vm)
{
  int   loop;
  
  vm->id = ntohi((unsigned int) vm->id);
  vm->dashed = ntohi((unsigned int) vm->dashed);
  vm->expanded = ntohi((unsigned int) vm->expanded);
  vm->marked = ntohi((unsigned int) vm->marked);
  vm->color = ntohi((unsigned int) vm->color);
  for (loop = 0; loop < 50; loop++) {
    vm->elements[loop] = ntohi((unsigned int) vm->elements[loop]);
    vm->x[loop] = ntohi((unsigned int) vm->x[loop]);
    vm->y[loop] = ntohi((unsigned int) vm->y[loop]);
    vm->symbol[loop] = ntohi((unsigned int) vm->symbol[loop]);
    vm->text[loop] = ntohi((unsigned int) vm->text[loop]);
  }
  vm->num_elements = ntohi((unsigned int) vm->num_elements);
}

/*
 *-----------------------------------------------------------------------
 *
 * FillMap - Fill map with data in Videomap record vm.
 *
 *-----------------------------------------------------------------------
 */

static void
FillMap(ZnMapInfoId	map,
	VideoMap	*vm)
{
  int		i;
  ZnBool	has_start_pos = False;
  ZnPos		x_cur=0, y_cur=0;
  char		ch;
  ZnPos		text_x=0, text_y=0;
  char		text[TEXT_SIZE];
  ZnBool	in_text = False;
  ZnBool	in_mod_text = False;
  unsigned int	text_size=0;

  for (i = 0; i < vm->num_elements; i++) {
    switch(vm->elements[i] & 0xFF) {
    case 'p':
    case 'P':
      if (in_text) {
	in_text = in_mod_text = False;
	while (text[text_size - 1] == ' ') {
	  text_size--;
	}
	text[text_size] = (char) 0;
	ZnMapInfoAddText(map, ZnMapInfoNumTexts(map), NULL, ZnMapInfoNormalText,
			 ZnMapInfoLineSimple, text_x, text_y, text);
      }

      x_cur = (int) (short) vm->x[i];
      y_cur = (int) (short) vm->y[i];
      has_start_pos = True;
	
      if (vm->symbol[i]) {
	ZnMapInfoAddSymbol(map, ZnMapInfoNumSymbols(map), NULL, x_cur, y_cur,
			   (char) vm->symbol[i]);
      }
      break;

      /* We gather consecutive 'T' elements in a text. We skip
	 leading and trailing spaces and mod texts (between '@'
	 and now obsolete) */

    case 't':
    case 'T':
      if (!has_start_pos) {
	ZnWarning("Bogus map block, it has been discarded\n");
	return;
      }

      if (in_text == False) {
	ch = (char) vm->text[i] & 0xFF;
	if (ch == '@') {
	  if (in_mod_text == True) {
	    in_mod_text = False;
	  }
	  else {
	    in_mod_text = True;
	  }
	}
	else if (in_mod_text == False) {
	  in_text = True;
	  text_size = 0;
	  text_x = x_cur;
	  text_y = y_cur;
	  text[0] = (char) 0;
	}
      }
      if (in_text) {
	text[text_size] = (char) vm->text[i] & 0xFF;
	text_size++;
      }
      break;

    case 'v':
    case 'V':
      if (!has_start_pos) {
	ZnWarning("Bogus map block, it has been discarded\n");
	return;
      }

      if (in_text) {
	in_text = in_mod_text = False;
	while (text[text_size - 1] == ' ') {
	  text_size--;
	}
	text[text_size] = (char) 0;
	ZnMapInfoAddText(map, ZnMapInfoNumTexts(map), NULL, ZnMapInfoNormalText,
			 ZnMapInfoLineSimple, text_x, text_y, text);
      }

      if (vm->dashed)	{
	ZnMapInfoAddLine(map, ZnMapInfoNumLines(map), NULL, ZnMapInfoLineDashed,
			 0, x_cur, y_cur,
			 (int) (short) vm->x[i], (int) (short) vm->y[i]);
      }
      else if (vm->marked) {
	ZnMapInfoAddLine(map, ZnMapInfoNumLines(map), NULL, ZnMapInfoLineMarked,
			 0, x_cur, y_cur,
			 (int) (short) vm->x[i], (int) (short) vm->y[i]);
      }
      else {
	ZnMapInfoAddLine(map, ZnMapInfoNumLines(map), NULL, ZnMapInfoLineSimple,
			 0, x_cur, y_cur,
			 (int) (short) vm->x[i], (int) (short) vm->y[i]);
      }

      x_cur = (int) (short) vm->x[i];
      y_cur = (int) (short) vm->y[i];

      if (vm->symbol[i]) {
	ZnMapInfoAddSymbol(map, ZnMapInfoNumSymbols(map), NULL, x_cur, y_cur,
			   (char) vm->symbol[i]);
      }
      break;
    }
  }

  if (in_text) {
    in_text = in_mod_text = False;
    while (text[text_size - 1] == ' ') {
      text_size--;
    }
    text[text_size] = (char) 0;
    ZnMapInfoAddText(map, ZnMapInfoNumTexts(map), NULL, ZnMapInfoNormalText,
		     ZnMapInfoLineSimple, text_x, text_y, text);
  }
}

/*
 *-----------------------------------------------------------------------
 *
 * ZnMapInfoGetVideomap - Load a mapinfo with the content of a videomap
 *	file named 'filename'. Only the sub map 'index' will be loaded.
 *	If successful a new mapinfo is returned, NULL otherwise. The
 *	index is zero based.
 *
 *-----------------------------------------------------------------------
 */
static int
ZnMapInfoGetVideomap(ZnMapInfoId	map,
		     char		*filename,
		     unsigned int	index)
{
  VideoMap	current_vm;
  Tcl_Channel	chan;
  unsigned int	cur_index, cur_id;

  /* Open the specified map file. */
  chan = Tcl_OpenFileChannel(NULL, filename, "r", 0);
  if (chan == NULL) {
    return TCL_ERROR;
  }
  if (Tcl_SetChannelOption(NULL, chan,
			   "-translation", "binary") == TCL_ERROR) {
    return TCL_ERROR;
  }

  /* Load the map */

  /* First skip the leading maps up to index. */
  cur_index = 0;
  if (Tcl_Read(chan, (char *) &current_vm,
	       sizeof(VideoMap)) != sizeof(VideoMap)) {
    goto error;
  }
  cur_id = ntohi((unsigned int) current_vm.id);
  while (cur_index != index) {
    if (Tcl_Read(chan, (char *) &current_vm,
		 sizeof(VideoMap)) != sizeof(VideoMap)) {
      goto error;
    }
    if (cur_id != ntohi((unsigned int) current_vm.id)) {
      cur_index++;
      cur_id = ntohi((unsigned int) current_vm.id);
    }
  };

  /* Then load all the map modules. */
  do {
    ReorderVidomap(&current_vm);
    FillMap(map, &current_vm);
    if ((Tcl_Read(chan, (char *) &current_vm,
		  sizeof(VideoMap)) != sizeof(VideoMap)) &&
	!Tcl_Eof(chan)) {
      goto error;
    }
  }
  while ((cur_id == ntohi((unsigned int) current_vm.id)) &&
	 !Tcl_Eof(chan));

  Tcl_Close(NULL, chan);
  return TCL_OK;

 error:
  Tcl_Close(NULL, chan);
  return TCL_ERROR;
}

/*
 *-----------------------------------------------------------------------
 *
 * ZnMapInfoVideomapIds - Return the list of sub map ids contained in a
 *	videomap file. This makes it possible to iterate through such
 *	a file without stumbling on an error, to know how much maps
 *	are there and to sort them according to their ids.
 *
 *-----------------------------------------------------------------------
 */ 

static ZnList
ZnMapInfoVideomapIds(char	*filename)
{
  Tcl_Channel	chan;
  VideoMap	current_vm;
  unsigned int	cur_id;
  ZnList	ids;
  
  /* Open the specified map file. */
  chan = Tcl_OpenFileChannel(NULL, filename, "r", 0);
  if (chan == NULL) {
    return NULL;
  }
  if (Tcl_SetChannelOption(NULL, chan,
			   "-translation", "binary") == TCL_ERROR) {
    return NULL;
  }

  if (Tcl_Read(chan, (char *) &current_vm,
	       sizeof(VideoMap)) != sizeof(VideoMap)) {
  error:
    Tcl_Close(NULL, chan);
    return NULL;
  }
  cur_id = ntohi((unsigned int) current_vm.id);
  ids = ZnListNew(16, sizeof(int));
  /*printf("id %d\n", cur_id);*/
  ZnListAdd(ids, &cur_id, ZnListTail);
  
  do {
    if (Tcl_Read(chan, (char *) &current_vm,
		 sizeof(VideoMap)) != sizeof(VideoMap)) {
      ZnListFree(ids);
      goto error;
    }
    if (cur_id != ntohi((unsigned int) current_vm.id)) {
      cur_id = ntohi((unsigned int) current_vm.id);
      /*printf("id %d\n", cur_id);*/
      ZnListAdd(ids, &cur_id, ZnListTail);
    }
  }
  while (!Tcl_Eof(chan));

  Tcl_Close(NULL, chan);
  return ids;
}


/*
 *--------------------------------------------------------------------------
 *
 * ZnMapInfo and Videomapstuff that should go eventually in its own file.
 *
 *--------------------------------------------------------------------------
 */

static Tcl_HashTable	mapInfoTable;
static ZnBool		map_info_inited = False;

typedef struct {
  ClientData		client_data;
  ZnMapInfoChangeProc	proc;
} ZnMapInfoClient;

typedef struct {
  ZnMapInfoId	map_info;
  ZnBool	deleted;
  ZnList	clients;
} ZnMapInfoMaster;

static void
ZnMapInfoInit()
{
  Tcl_InitHashTable(&mapInfoTable, TCL_ONE_WORD_KEYS);

  map_info_inited = True;
}

static void
UpdateMapInfoClients(ZnMapInfoMaster	*master)
{
  int		  i, num;
  ZnMapInfoClient *client;

  num = ZnListSize(master->clients);
  client = ZnListArray(master->clients);
  for (i = 0; i < num; i++, client++) {
    (*client->proc)(client->client_data, master->map_info);
  }
}

static int
ZnCreateMapInfo(Tcl_Interp	*interp __unused,
		char		*name,
		ZnMapInfoId	*map_info)
{
  Tk_Uid	  uid = Tk_GetUid(name);
  Tcl_HashEntry	  *entry;
  int		  new;
  ZnMapInfoMaster *master;
  
  if (!map_info_inited) {
    ZnMapInfoInit();
  }
  
  entry = Tcl_CreateHashEntry(&mapInfoTable, uid, &new);
  if (!new) {
    /*
     * Empty the map info if it is not.
     */
    master = (ZnMapInfoMaster *) Tcl_GetHashValue(entry);
    if (master->deleted) {
      master->deleted = False;
    }
    else {
      ZnMapInfoEmpty(master->map_info);
      UpdateMapInfoClients(master);
    }
  }
  else {
    master = (ZnMapInfoMaster *) ZnMalloc(sizeof(ZnMapInfoMaster));
    master->map_info = ZnMapInfoCreate(name);
    master->deleted = False;
    master->clients = ZnListNew(1, sizeof(ZnMapInfoClient));
    Tcl_SetHashValue(entry, master);
  }
  if (map_info) {
    *map_info = master->map_info;
  }
  return TCL_OK;
}

static int
ZnDuplicateZnMapInfo(Tcl_Interp	*interp,
		     char		*name,
		     ZnMapInfoId	map_info)
{
  Tk_Uid	  uid = Tk_GetUid(name);
  Tcl_HashEntry	  *entry;
  int		  new;
  ZnMapInfoMaster *master;
  
  if (!map_info_inited) {
    ZnMapInfoInit();
  }
  
  entry = Tcl_CreateHashEntry(&mapInfoTable, uid, &new);
  if (!new) {
    Tcl_AppendResult(interp, "duplicate mapinfo \"", name, "\" already exists", NULL);
    return TCL_ERROR;
  }
  master = (ZnMapInfoMaster *) ZnMalloc(sizeof(ZnMapInfoMaster));
  master->map_info = ZnMapInfoDuplicate(map_info);
  master->deleted = False;
  master->clients = ZnListNew(1, sizeof(ZnMapInfoClient));
  Tcl_SetHashValue(entry, master);

  return TCL_OK;
}

static ZnMapInfoMaster *
LookupMapInfoMaster(Tcl_Interp	*interp,
		    char	*name)
{
  Tk_Uid	  uid = Tk_GetUid(name);
  Tcl_HashEntry	  *entry;
  ZnMapInfoMaster *master;
  
  if (!map_info_inited) {
    ZnMapInfoInit();
  }
  
  entry = Tcl_FindHashEntry(&mapInfoTable, uid);
  if (entry == NULL) {
  mp_error:
    Tcl_AppendResult(interp, "mapinfo \"", name, "\" doesn't exist", NULL);
    return NULL;
  }
  master = (ZnMapInfoMaster *) Tcl_GetHashValue(entry);
  if (master->deleted) {
    goto mp_error;
  }
  return master;
}

static int
ZnDeleteMapInfo(Tcl_Interp	*interp __unused,
		char		*name)
{
  ZnMapInfoMaster *master;
  Tk_Uid	  uid = Tk_GetUid(name);
  Tcl_HashEntry	  *entry;
  
  if (!map_info_inited) {
    ZnMapInfoInit();
  }
  
  entry = Tcl_FindHashEntry(&mapInfoTable, uid);
  if (entry == NULL) {
    return TCL_ERROR;
  }
  
  master = (ZnMapInfoMaster *) Tcl_GetHashValue(entry);
  if (ZnListSize(master->clients) != 0) {
    master->deleted = True;
    ZnMapInfoEmpty(master->map_info);
    UpdateMapInfoClients(master);
  }
  else {
    ZnMapInfoDelete(master->map_info);
    ZnListFree(master->clients);
    Tcl_DeleteHashEntry(entry);
    ZnFree(master);
  }

  return TCL_OK;
}

ZnMapInfoId
ZnGetMapInfo(Tcl_Interp		*interp,
	     char		*name,
	     ZnMapInfoChangeProc proc,
	     ClientData		client_data)
{
  ZnMapInfoMaster	*master;
  ZnMapInfoClient	client;
  
  master = LookupMapInfoMaster(interp, name);
  if (master == NULL) {
    return NULL;
  }
  client.proc = proc;
  client.client_data = client_data;
  ZnListAdd(master->clients, &client, ZnListTail);

  return master->map_info;
}

void
ZnFreeMapInfo(ZnMapInfoId	map_info,
	      ZnMapInfoChangeProc proc,
	      ClientData	client_data)
{
  Tk_Uid	uid = Tk_GetUid(ZnMapInfoName(map_info));
  Tcl_HashEntry	*entry;
  ZnMapInfoMaster *master;
  ZnMapInfoClient *client;
  unsigned int	num, i;
  
  if (!map_info_inited) {
    ZnMapInfoInit();
  }
  
  entry = Tcl_FindHashEntry(&mapInfoTable, uid);
  if (entry == NULL) {
    return;
  }
  master = (ZnMapInfoMaster *) Tcl_GetHashValue(entry);
  client = ZnListArray(master->clients);
  num = ZnListSize(master->clients);
  for (i = 0; i < num; i++, client++) {
    if ((client->client_data == client_data) &&
	(client->proc == proc)) {
      ZnListDelete(master->clients, i);
      return;
    }
  }
}

static void
ZnUpdateMapInfoClients(ZnMapInfoId	map_info)
{
  Tk_Uid	  uid = Tk_GetUid(ZnMapInfoName(map_info));
  Tcl_HashEntry	  *entry;
  ZnMapInfoMaster *master;

  if (!map_info_inited) {
    ZnMapInfoInit();
  }
  
  entry = Tcl_FindHashEntry(&mapInfoTable, uid);
  if (entry == NULL) {
    return;
  }
  master = (ZnMapInfoMaster *) Tcl_GetHashValue(entry);
  UpdateMapInfoClients(master);
}


/*
 * These arrays must be kept in sync with the ZnMapInfoLineStyle
 * and ZnMapInfoTextStyle enums.
 */
static char *line_style_strings[] = {
  "simple",
  "dashed",
  "dotted",
  "mixed",
  "marked",
};

static char *text_style_strings[] = {
  "normal",
  "underlined"
};

static char *
ZnMapInfoLineStyleToString(ZnMapInfoLineStyle	line_style)
{
  return line_style_strings[line_style];
}

static int
ZnMapInfoLineStyleFromString(Tcl_Interp		*interp,
			     char		*str,
			     ZnMapInfoLineStyle	*line_style)
{
  int	i, num = sizeof(line_style_strings)/sizeof(char *);

  for (i = 0; i < num; i++) {
    if (strcmp(str, line_style_strings[i]) == 0) {
      *line_style = i;
      return TCL_OK;
    }
  }
  Tcl_AppendResult(interp, " incorrect mapinfo line style \"",
		   str,"\"", NULL);
  return TCL_ERROR;
}

static char *
ZnMapInfoTextStyleToString(ZnMapInfoTextStyle	text_style)
{
  return text_style_strings[text_style];
}

static int
ZnMapInfoTextStyleFromString(Tcl_Interp		*interp,
			     char			*str,
			     ZnMapInfoTextStyle	*text_style)
{
  int	i, num = sizeof(text_style_strings)/sizeof(char *);

  for (i = 0; i < num; i++) {
    if (strcmp(str, text_style_strings[i]) == 0) {
      *text_style = i;
      return TCL_OK;
    }
  }
  Tcl_AppendResult(interp, " incorrect mapinfo text style \"",
		   str,"\"", NULL);
  return TCL_ERROR;
}

int
ZnMapInfoObjCmd(ClientData	client_data __unused,
		Tcl_Interp	*interp,	/* Current interpreter. */
		int		argc,		/* Number of arguments. */
		Tcl_Obj	*CONST	args[])
{
  ZnPos		  x, y;
  int		  index, index2, result;
  ZnMapInfoMaster *master;
  Tcl_Obj	  *l;
#ifdef PTK_800
  static char *sub_cmd_strings[] =
#else
  static CONST char *sub_cmd_strings[] =
#endif
  {
    "add", "count", "create", "delete", "duplicate",
    "get", "remove", "replace", "scale", "translate", NULL
  };
#ifdef PTK_800
  static char *e_type_strings[] =
#else
  static CONST char *e_type_strings[] =
#endif
  {
    "arc", "line", "symbol", "text", NULL
  };
  enum		sub_cmds {
    ZN_MI_ADD, ZN_MI_COUNT, ZN_MI_CREATE, ZN_MI_DELETE, ZN_MI_DUPLICATE,
    ZN_MI_GET, ZN_MI_REMOVE, ZN_MI_REPLACE, ZN_MI_SCALE, ZN_MI_TRANSLATE
  };
  enum		e_types {
    ZN_E_ARC, ZN_E_LINE, ZN_E_SYMBOL, ZN_E_TEXT
  };
  

  if (argc < 3) {
    Tcl_WrongNumArgs(interp, 1, args, "mapInfo/name subCmd ?args?");
    return TCL_ERROR;
  }

  if (Tcl_GetIndexFromObj(interp, args[2], sub_cmd_strings,
			  "subCmd", 0, &index) != TCL_OK) {
    return TCL_ERROR;
  }
  result = TCL_OK;
  /*printf("mapinfo command \"%s\", argc=%d\n",
    Tcl_GetString(args[2]), argc);*/

  switch((enum sub_cmds) index) {
    /*
     * create
     */
  case ZN_MI_CREATE:
    {
      if (argc != 3) {
	Tcl_WrongNumArgs(interp, 1, args, "name create");
	return TCL_ERROR;
      }
      if (ZnCreateMapInfo(interp, Tcl_GetString(args[1]), NULL) == TCL_ERROR) {
	return TCL_ERROR;
      }
    }
    break;
    /*
     * delete
     */
  case ZN_MI_DELETE:
    {
      if (argc != 3) {
	Tcl_WrongNumArgs(interp, 1, args, "mapInfo delete");
	return TCL_ERROR;
      }
      if (ZnDeleteMapInfo(interp, Tcl_GetString(args[1])) == TCL_ERROR) {
	return TCL_ERROR;
      }
    }
    break;
    /*
     * duplicate
     */
  case ZN_MI_DUPLICATE:
    {
      if (argc != 4) {
	Tcl_WrongNumArgs(interp, 1, args, "mapInfo duplicate name");
	return TCL_ERROR;
      }
      master = LookupMapInfoMaster(interp, Tcl_GetString(args[1]));
      if (master == NULL) {
	return TCL_ERROR;
      }
      if (ZnDuplicateZnMapInfo(interp, Tcl_GetString(args[3]),
			       master->map_info) == TCL_ERROR) {
	return TCL_ERROR;
      }
    }
    break;
    /*
     * add/replace
     */
  case ZN_MI_ADD:
  case ZN_MI_REPLACE:
    {
      ZnMapInfoLineStyle line_style;
      ZnMapInfoTextStyle text_style;
      int		 i, insert, val;
      ZnPos		 coords[6];
      ZnBool		 add_cmd = (enum sub_cmds) index == ZN_MI_ADD;
      int		 num_param = add_cmd ? 4 : 5;
      
      if (argc < num_param) {
	Tcl_WrongNumArgs(interp, 3, args,
			 add_cmd ? "elementType ?args?" : "elementType index ?args?");
	return TCL_ERROR;
      }
      master = LookupMapInfoMaster(interp, Tcl_GetString(args[1]));
      if (master == NULL) {
	return TCL_ERROR;
      }
      if (!add_cmd) {
	if (Tcl_GetIntFromObj(interp, args[4], &insert) == TCL_ERROR) {
	  return TCL_ERROR;
	}
      }
      if (Tcl_GetIndexFromObj(interp, args[3], e_type_strings,
			      "elementType", 0, &index2) != TCL_OK) {
	return TCL_ERROR;
      }
      switch ((enum e_types) index2) {
      case ZN_E_LINE:
	{
	  if (argc != (num_param+6)) {
	    Tcl_WrongNumArgs(interp, 4, args,
			     add_cmd ? "style width x1 y1 x2 y2" : "index style width x1 y1 x2 y2");
	    return TCL_ERROR;
	  }
	  if (ZnMapInfoLineStyleFromString(interp, Tcl_GetString(args[num_param]),
					   &line_style) == TCL_ERROR) {
	    return TCL_ERROR;
	  }
	  for (i = 0; i < 5; i++) {
	    if (Tcl_GetDoubleFromObj(interp, args[num_param+i+1], &coords[i]) == TCL_ERROR) {
	      return TCL_ERROR;
	    }
	  }
	  if (coords[0] < 0) {
	    coords[0] = 0;
	  }
	  if (add_cmd) {
	    ZnMapInfoAddLine(master->map_info, ZnListTail, NULL, line_style,
			     coords[0], coords[1], coords[2], coords[3], coords[4]);
	  }
	  else {
	    ZnMapInfoReplaceLine(master->map_info, insert, NULL, line_style,
				 coords[0], coords[1], coords[2], coords[3], coords[4]);
	  }
	}
	break;
      case ZN_E_SYMBOL:
	{
	  if (argc != (num_param+3)) {
	    Tcl_WrongNumArgs(interp, 4, args,
			     add_cmd ? "x y intVal" : "index x y intVal");
	    return TCL_ERROR;
	  }
	  for (i = 0; i < 2; i++) {
	    if (Tcl_GetDoubleFromObj(interp, args[num_param+i], &coords[i]) == TCL_ERROR) {
	      return TCL_ERROR;
	    }
	  }
	  if (Tcl_GetIntFromObj(interp, args[num_param+2], &val) == TCL_ERROR) {
	    return TCL_ERROR;
	  }
	  if (val < 0) {
	    val = 0;
	  }
	  if (add_cmd) {
	    ZnMapInfoAddSymbol(master->map_info, ZnListTail, NULL, coords[0],
			       coords[1], (char) val);
	  }
	  else {
	    ZnMapInfoReplaceSymbol(master->map_info, insert, NULL, coords[0],
				   coords[1], (char) val);
	  }
	}
	break;
      case ZN_E_TEXT:
	{
	  if (argc != (num_param+5)) {
	    Tcl_WrongNumArgs(interp, 4, args,
			     add_cmd ? "textStyle lineStyle x y string" : "index textStyle lineStyle x y string");
	    return TCL_ERROR;
	  }
	  if (ZnMapInfoTextStyleFromString(interp, Tcl_GetString(args[num_param]),
					   &text_style) == TCL_ERROR) {
	    return TCL_ERROR;
	  }
	  if (ZnMapInfoLineStyleFromString(interp, Tcl_GetString(args[num_param+1]),
					   &line_style) == TCL_ERROR) {
	    return TCL_ERROR;
	  }
	  for (i = 0; i < 2; i++) {
	    if (Tcl_GetDoubleFromObj(interp, args[num_param+i+2], &coords[i]) == TCL_ERROR) {
	      return TCL_ERROR;
	    }
	  }
	  if (add_cmd) {
	    ZnMapInfoAddText(master->map_info, ZnListTail, NULL, text_style,
			     line_style, coords[0], coords[1],
			     Tcl_GetString(args[num_param+4]));
	  }
	  else {
	    /*printf("replace text ts %d ls %d %g %g %s\n", text_style,
	      line_style, coords[0], coords[1], Tcl_GetString(args[num_param+4]));*/
	    ZnMapInfoReplaceText(master->map_info, insert, NULL, text_style,
				 line_style, coords[0], coords[1],
				 Tcl_GetString(args[num_param+4]));
	  }
	}
	break;
      case ZN_E_ARC:
	{
	  if (argc != (num_param+7)) {
	    Tcl_WrongNumArgs(interp, 4, args,
			     add_cmd ? "style width cx cy radius start extent" : "index style width cx cy radius start extent");
	    return TCL_ERROR;
	  }
	  if (ZnMapInfoLineStyleFromString(interp, Tcl_GetString(args[num_param]),
					   &line_style) == TCL_ERROR) {
	    return TCL_ERROR;
	  }
	  for (i = 0; i < 6; i++) {
	    if (Tcl_GetDoubleFromObj(interp, args[num_param+i+1], &coords[i]) == TCL_ERROR) {
	      return TCL_ERROR;
	    }
	  }
	  if (coords[0] < 0) {
	    coords[0] = 0;
	  }
	  if (coords[3] < 0) {
	    coords[3] = 0;
	  }
	  if (add_cmd) {
	    ZnMapInfoAddArc(master->map_info, ZnListTail, NULL, line_style, coords[0],
			    coords[1], coords[2], coords[3], coords[4], coords[5]);
	  }
	  else {
	    ZnMapInfoReplaceArc(master->map_info, insert, NULL, line_style, coords[0],
				coords[1], coords[2], coords[3], coords[4], coords[5]);
	  }
	}
	break;
      }
      UpdateMapInfoClients(master);
    }
    break;
    /*
     * count
     */
  case ZN_MI_COUNT:
    {
      int	count = 0;
      if (argc != 4) {
	Tcl_WrongNumArgs(interp, 1, args, "mapInfo count type");
	return TCL_ERROR;
      }
      master = LookupMapInfoMaster(interp, Tcl_GetString(args[1]));
      if (master == NULL) {
	return TCL_ERROR;
      }
      if (Tcl_GetIndexFromObj(interp, args[3], e_type_strings,
			      "elementType", 0, &index2) != TCL_OK) {
	return TCL_ERROR;
      }
      switch ((enum e_types) index2) {
      case ZN_E_LINE:
	count = ZnMapInfoNumLines(master->map_info);
	break;
      case ZN_E_SYMBOL:
	count = ZnMapInfoNumSymbols(master->map_info);
	break;
      case ZN_E_TEXT:
	count = ZnMapInfoNumTexts(master->map_info);
	break;
      case ZN_E_ARC:
	count = ZnMapInfoNumArcs(master->map_info);
	break;
      }
      l = Tcl_NewIntObj(count);
      Tcl_SetObjResult(interp, l);
    }
    break;
    /*
     * get
     */
  case ZN_MI_GET:
    {
      int   insert;

      if (argc != 5) {
	Tcl_WrongNumArgs(interp, 1, args, "mapInfo get type index");
	return TCL_ERROR;
      }
      master = LookupMapInfoMaster(interp, Tcl_GetString(args[1]));
      if (master == NULL) {
	return TCL_ERROR;
      }
      if (Tcl_GetIntFromObj(interp, args[4], &insert) == TCL_ERROR) {
	return TCL_ERROR;
      }
      if (insert < 0) {
	insert = 0;
      }
      if (Tcl_GetIndexFromObj(interp, args[3], e_type_strings,
			      "elementType", 0, &index2) != TCL_OK) {
	return TCL_ERROR;
      }
      switch ((enum e_types) index2) {
      case ZN_E_LINE:
	{
	  ZnMapInfoLineStyle line_style;
	  ZnDim	line_width;
	  ZnPos	x_to, y_to;
	  ZnMapInfoGetLine(master->map_info, insert, NULL, &line_style,
			   &line_width, &x, &y, &x_to, &y_to);
	  l = Tcl_GetObjResult(interp);
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewStringObj(ZnMapInfoLineStyleToString(line_style), -1));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(line_width));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(x));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(y));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(x_to));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(y_to));
	}
	break;
      case ZN_E_SYMBOL:
	{
	  char	symbol;
	  ZnMapInfoGetSymbol(master->map_info, insert, NULL, &x, &y, &symbol);
	  l = Tcl_GetObjResult(interp);
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(x));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(y));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(symbol));
	}
	break;
      case ZN_E_TEXT:
	{
	  char *text;
	  ZnMapInfoTextStyle text_style;
	  ZnMapInfoLineStyle line_style;
	  ZnMapInfoGetText(master->map_info, insert, NULL, &text_style, &line_style,
			   &x, &y, &text);
	  l = Tcl_GetObjResult(interp);
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(x));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(y));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewStringObj(ZnMapInfoTextStyleToString(text_style), -1));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewStringObj(ZnMapInfoLineStyleToString(line_style), -1));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewStringObj(text, -1));
	}
	break;
      case ZN_E_ARC:
	{
	  ZnMapInfoLineStyle	line_style;
	  ZnDim	line_width, radius;
	  ZnPos	start, extent;
	  ZnMapInfoGetArc(master->map_info, insert, NULL, &line_style, &line_width,
			  &x, &y, &radius, &start, &extent);
	  l = Tcl_GetObjResult(interp);
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewStringObj(ZnMapInfoLineStyleToString(line_style), -1));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(line_width));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(x));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(y));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(radius));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(start));
	  Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(extent));
	}
	break;
      }
    }
    break;
    /*
     * remove
     */
  case ZN_MI_REMOVE:
    {
      int insert;
      if (argc != 5) {
	Tcl_WrongNumArgs(interp, 1, args, "mapInfo remove type index");
	return TCL_ERROR;
      }
      master = LookupMapInfoMaster(interp, Tcl_GetString(args[1]));
      if (master == NULL) {
	return TCL_ERROR;
      }
      if (Tcl_GetIntFromObj(interp, args[4], &insert) == TCL_ERROR) {
	return TCL_ERROR;
      }
      if (insert < 0) {
	insert = 0;
      }
      if (Tcl_GetIndexFromObj(interp, args[3], e_type_strings,
			      "elementType", 0, &index2) != TCL_OK) {
	return TCL_ERROR;
      }
      switch ((enum e_types) index2) {
      case ZN_E_LINE:
	ZnMapInfoRemoveLine(master->map_info, insert);
	break;
      case ZN_E_SYMBOL:
	ZnMapInfoRemoveSymbol(master->map_info, insert);
	break;
      case ZN_E_TEXT:
	ZnMapInfoRemoveText(master->map_info, insert);
	break;
      case ZN_E_ARC:
	ZnMapInfoRemoveArc(master->map_info, insert);
	break;
      }
      UpdateMapInfoClients(master);
    }
    break;
    /*
     * scale
     */
  case ZN_MI_SCALE:
    {
      double factor;
      
      if (argc != 4) {
	Tcl_WrongNumArgs(interp, 1, args, "mapInfo scale factor");
	return TCL_ERROR;
      }
      master = LookupMapInfoMaster(interp, Tcl_GetString(args[1]));
      if (master == NULL) {
	return TCL_ERROR;
      }
      if (Tcl_GetDoubleFromObj(interp, args[3], &factor) == TCL_ERROR) {
	return TCL_ERROR;
      }
      ZnMapInfoScale(master->map_info, factor);
      UpdateMapInfoClients(master);
    }
    break;
    /*
     * translate
     */
  case ZN_MI_TRANSLATE:
    {
      if (argc != 5) {
	Tcl_WrongNumArgs(interp, 1, args, "mapInfo translate xAmount yAmount");
	return TCL_ERROR;
      }
      master = LookupMapInfoMaster(interp, Tcl_GetString(args[1]));
      if (master == NULL) {
	return TCL_ERROR;
      }
      if (Tcl_GetDoubleFromObj(interp, args[3], &x) == TCL_ERROR) {
	return TCL_ERROR;
      }
      if (Tcl_GetDoubleFromObj(interp, args[4], &y) == TCL_ERROR) {
	return TCL_ERROR;
      }
      ZnMapInfoTranslate(master->map_info, x, y);
      UpdateMapInfoClients(master);
    }
    break;
  }

  return TCL_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * VideomapObjCmd --
 *
 *
 *----------------------------------------------------------------------
 */
int
ZnVideomapObjCmd(ClientData	client_data __unused,
		 Tcl_Interp	*interp,	/* Current interpreter. */
		 int		argc,		/* Number of arguments. */
		 Tcl_Obj	*CONST	args[])
{
  ZnList	ids;
  int		index;
  int		*id_array, id_num, i;
  Tcl_Obj	*l;
#ifdef PTK_800
  static char *sub_cmd_strings[] =
#else
  static CONST char *sub_cmd_strings[] =
#endif
  {
    "ids", "load", NULL
  };
  enum		sub_cmds {
    ZN_V_IDS, ZN_V_LOAD
  };
  

  if (argc < 2) {
    Tcl_WrongNumArgs(interp, 1, args, "?subCmd? filename $args?");
    return TCL_ERROR;
  }

  if (Tcl_GetIndexFromObj(interp, args[1], sub_cmd_strings,
			  "subCmd", 0, &index) != TCL_OK) {
    return TCL_ERROR;
  }

  switch((enum sub_cmds) index) {
    /*
     * ids
     */
  case ZN_V_IDS:
    {
      if (argc != 3) {
	Tcl_WrongNumArgs(interp, 1, args,"ids filename");
	return TCL_ERROR;
      }
      ids = ZnMapInfoVideomapIds(Tcl_GetString(args[2]));
      if (ids == NULL) {
	Tcl_AppendResult(interp, "unable to look at videomap file \"",
			 Tcl_GetString(args[2]), "\"", NULL);
	return TCL_ERROR;
      }
      id_array = ZnListArray(ids);
      id_num = ZnListSize(ids);
      l = Tcl_GetObjResult(interp);
      for (i = 0; i < id_num; i++) {
	Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(id_array[i]));
      }
      ZnListFree(ids);
    }
    break;
    /*
     * load
     */
  case ZN_V_LOAD:
    {
      ZnMapInfoId	   map_info;
      int insert;
      
      if (argc != 5) {
	Tcl_WrongNumArgs(interp, 1, args, "load filename index mapInfo");
	return TCL_ERROR;
      }
      if (Tcl_GetIntFromObj(interp, args[3], &insert) == TCL_ERROR) {
	return TCL_ERROR;
      }
      if (insert < 0) {
	insert = 0;
      }
      if (ZnCreateMapInfo(interp, Tcl_GetString(args[4]), &map_info) == TCL_ERROR) {
	return TCL_ERROR;
      }
      if (ZnMapInfoGetVideomap(map_info, Tcl_GetString(args[2]), insert) == TCL_ERROR) {
	Tcl_AppendResult(interp, "unable to load videomap file \"",
			 Tcl_GetString(args[2]), ":",
			 Tcl_GetString(args[3]), "\"", NULL);
	return TCL_ERROR;
      }
      ZnUpdateMapInfoClients(map_info);
    }
    break;
  }
  
  return TCL_OK;
}
