/******************************************************************************
    Xplanet 0.43 - render an image of the earth into an X window
    Copyright (C) 1999 Hari Nair <hari@alumni.caltech.edu>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

******************************************************************************/

/*
  text.cc - code for drawing labels and markers.  Most of this is adapted 
  from xearth by Kirk Johnson.
*/

#include <fstream.h>
#include <iostream.h>
#include <math.h>
#include <stdio.h>

#include "image.h"
#include "location.h"
#include "options.h"
#include "planet.h"
#include "text.h"
#include "util.h"
#include "xplanet.h"

static XFontStruct *font;

static Pixmap p;
static GC gc;
static Pixel fg, bg;

int
init_text (Pixmap pixmap)
{
  p = pixmap;
  gc = XCreateGC (disp, window, 0, NULL);

  bg = BlackPixel(disp, screen_num );
  Colormap cmap = DefaultColormap(disp, screen_num);
  XColor xc;      
  
  char *defaultcolor = "red";
  if (opts.color == NULL) opts.color = defaultcolor;

  if (XAllocNamedColor(disp, cmap, opts.color, &xc, &xc) != 0) 
    fg = xc.pixel;

  char *defaultfont = "variable";
  if (opts.font == NULL) opts.font = defaultfont;

  font = XLoadQueryFont (disp, opts.font);
  if (font == NULL)
    {
      cerr << "Can't load font " << opts.font 
	   << ", using default font\n";
      font = XQueryFont(disp, XGContextFromGC(gc));
      if (font == NULL)
	    {
	      cerr << "Can't load any fonts at all\n";
	      return (0);
	    }
    }

  XSetFont (disp, gc, font->fid);
  return (1);
}

void 
draw_outlined_string (int x, int y, char *text)
{
  int len = strlen (text);
  XSetForeground (disp, gc, bg);
  XDrawString(disp, p, gc, x+1, y, text, len);
  XDrawString(disp, p, gc, x-1, y, text, len);
  XDrawString(disp, p, gc, x, y+1, text, len);
  XDrawString(disp, p, gc, x, y-1, text, len);
  XSetForeground(disp, gc, fg);
  XDrawString(disp, p, gc, x, y, text, len);
}

void 
draw_label ()
{
  float sunlatdeg, sunlondeg;
  float obslatdeg, obslondeg;

  if (flipped) 
    {
      sunlon *= -1;
      opts.obslon *= -1;
    }

  sunlondeg = sunlon / deg_to_rad;
  if (sunlondeg > 180) sunlondeg -= 360;
  else if (sunlondeg < -180) sunlondeg += 360;
  sunlatdeg = sunlat / deg_to_rad;
  obslondeg = opts.obslon / deg_to_rad;
  if (obslondeg > 180) obslondeg -= 360;
  else if (obslondeg < -180) obslondeg += 360;
  obslatdeg = opts.obslat / deg_to_rad;

  int x, y;
  int dy = font->ascent + font->descent + 1;

  if (opts.label_mask & XNegative) opts.label_x += window_width;

  if (opts.label_mask & YNegative)
    {
      opts.label_y += (window_height - font->descent);
      if (opts.mode == MERCATOR) 
	opts.label_y -= dy;      // Move up 2 text heights
      else
	opts.label_y -= 3 * dy;  // Move up 4 text heights
    }
  else
    opts.label_y += font->ascent;

  y = opts.label_y;

  char label[64];
  int         direction;
  int         ascent;
  int         descent;
  XCharStruct extents;

  // Write the local time
  strftime (label, sizeof(label), "%a %d %b %Y %H:%M %Z", 
	    localtime (&opts.time.tv_sec));
  XTextExtents(font, label, strlen(label), &direction, &ascent, 
	       &descent, &extents);
  if (opts.label_mask & XNegative) x = opts.label_x - extents.rbearing;
  else x = opts.label_x - extents.lbearing;
  draw_outlined_string (x, y, label);
  y += dy;

  // Write the sun's position
  if (opts.body == 0 || opts.body == 3)
    sprintf (label, "sun %.1f %c %.1f %c",
	     fabs(sunlatdeg), ((sunlatdeg < 0) ? 'S' : 'N'),
	     fabs(sunlondeg), ((sunlondeg < 0) ? 'W' : 'E'));
  else
    {
      if (sunlondeg < 0) sunlondeg += 360;
      sprintf (label, "sun %.1f %c %.1f",
	       fabs(sunlatdeg), ((sunlatdeg < 0) ? 'S' : 'N'),
	       sunlondeg);
    }

  XTextExtents(font, label, strlen(label), &direction, &ascent, 
	       &descent, &extents);
  if (opts.label_mask & XNegative) x = opts.label_x - extents.rbearing;
  else x = opts.label_x - extents.lbearing;
  draw_outlined_string (x, y, label);
  y += dy;

  if (opts.mode == ORTHOGRAPHIC)
    {
      // Write the observer's position
      if (opts.body == 0 || opts.body == 3)
	sprintf (label, "obs %.1f %c %.1f %c",
		 fabs(obslatdeg), ((obslatdeg < 0) ? 'S' : 'N'),
		 fabs(obslondeg), ((obslondeg < 0) ? 'W' : 'E'));
      else
	{
	  if (obslondeg < 0) obslondeg += 360;
	  sprintf (label, "obs %.1f %c %.1f",
		   fabs(obslatdeg), ((obslatdeg < 0) ? 'S' : 'N'),
		   obslondeg);
	}
      
      XTextExtents(font, label, strlen(label), &direction, &ascent, 
		   &descent, &extents);
      if (opts.label_mask & XNegative) x = opts.label_x - extents.rbearing;
      else x = opts.label_x - extents.lbearing;
      draw_outlined_string (x, y, label);
      y += dy;

      // Write percent illumination
      location l1 (sunlatdeg, sunlondeg, 1, NULL);
      location l2 (obslatdeg, obslondeg, 1, NULL);
      l1.spher_to_rect ();
      l2.spher_to_rect ();
      // Find the angle between the two vectors
      float phase = l1.dot(l2);
      phase = 50 * (1 + phase);

      sprintf (label, "illumination %.1f %%", phase);
      XTextExtents(font, label, strlen(label), &direction, &ascent, 
		   &descent, &extents);
      if (opts.label_mask & XNegative) x = opts.label_x - extents.rbearing;
      else x = opts.label_x - extents.lbearing;
      draw_outlined_string (x, y, label);
    }
}

void 
draw_marker (location m)
{
  int ilon, ilat;

  int         direction;
  int         ascent;
  int         descent;
  XCharStruct extents;

  if (opts.mode == MERCATOR) 
    {
      ilon = int ((m.lon + M_PI)/(TWO_PI) * (image_width - 1));
      ilat = int ((M_PI_2 - m.lat)/(M_PI) * (image_height - 1));
    }
  else
    {
      // Check if we're on the far side of the globe
      if (m.x < 0) return;    

      ilon = opts.centx + int (m.y * opts.radius);
      ilat = opts.centy - int (m.z * opts.radius);
    }

  XSetForeground(disp, gc, bg);
  XDrawArc(disp, p, gc, ilon-3, ilat-3, 6, 6, 0, 360*64);
  XDrawArc(disp, p, gc, ilon-1, ilat-1, 2, 2, 0, 360*64);
  XSetForeground(disp, gc, fg);
  XDrawArc(disp, p, gc, ilon-2, ilat-2, 4, 4, 0, 360*64);

  XTextExtents(font, m.name, strlen(m.name), 
	       &direction, &ascent, &descent, &extents);

  ilon += (extents.lbearing + 3);
  ilat += (font->ascent + font->descent) / 3;

  draw_outlined_string (ilon, ilat, m.name);
}

int 
delimiter (char c)
{
  return (c == ' ' || c == '\t' || c == ',' || c == '/');
}

void 
read_markers ()
{
  if (opts.markerfile == NULL) 
    {
      opts.markerfile = new char [300];
      strcpy (opts.markerfile, prefix);
      strcat (opts.markerfile, current_body);
      strcat (opts.markerfile, defaultmarkerfile);
    }

  ifstream infile;
  infile.open (opts.markerfile);

  if (infile.bad()) 
    {
      cerr << "Can't open marker file " << opts.markerfile << endl;
      return;
    }
  
  double rot[3][3];
  if (flipped) opts.obslon *= -1;
  rotate_zyx (rot, -opts.rotate, 
	      -opts.obslat, opts.obslon);

  int len=200;
  char *line = new char[len];
  while (infile.getline (line, len, '\n') != NULL)
    {
      for(int i = 0; i < len; i++)
	{
	  while (delimiter (line[i])) i++;
	  if (line[i] == '#' || line[i] == '\0') break;
	  double lat, lon;
	  int istart = i;
	  sscanf (line + i, "%lf", &lat);
	  while (!delimiter (line[i])) i++;
	  while (delimiter (line[i])) i++;
	  sscanf (line + i, "%lf", &lon);
	  while (line[i] != '"') i++;
	  istart = ++i;
	  while (line[i] != '"') i++;
	  char *name = new char[i - istart + 1];
	  strncpy (name, (line + istart), i - istart);
	  name[i-istart] = '\0';

	  if (flipped) lon *= -1;

	  double radius = 1;
	  if (opts.dem != NULL)
	    {
	      int ilon = int ((lon + 180)/360 * (image_width - 1));
	      int ilat = int ((90 - lat)/180 * (image_height - 1));
	      radius += (opts.demscale * 
			 elevation [ilat * image_width + ilon]/255.);
	    }
	  location m (lat, lon, radius, name);
	  m.spher_to_rect ();
	  if (opts.obslon || opts.obslat || opts.rotate) m.rotate (rot);
	  m.rect_to_spher ();
	  draw_marker (m);  
	  break;
	}
    }
}

