/******************************************************************************
    Xplanetbg 0.43 - run Xplanet in the background
    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

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

#include <fstream.h>
#include <iostream.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>

#include "checkfuncs.h"
#include "orbit.h"

#ifdef HAVE_STRPTIME
#include <sys/time.h>
struct timeval tval;
struct tm *tm = NULL;
float timewarp = 1;
#endif

Display *disp=NULL;
Window window=0;
int x=0, y=0;                   // Window position
unsigned int width = 512;
unsigned int height = 512;

char *title;
char *versionstring = "Xplanetbg 0.43";

int num_times = 0;
char *base = NULL, *extension = NULL;

struct s_orbit_info {
  double pos_lat, pos_long, duration, inclination;
};

Window
open_window ()
{
  Window root;
  
  disp = XOpenDisplay (NULL);
  if ( disp == NULL )
    { 
      cerr << "Can't open display\n";
      exit (EXIT_FAILURE);
    }

  int screen_num = DefaultScreen (disp);
  root = RootWindow (disp, screen_num);
  
  window = XCreateSimpleWindow (disp, root, x, y, width, height, 4, 
				WhitePixel (disp, screen_num),
				BlackPixel (disp, screen_num));

  XTextProperty windowName;
  if (title != NULL)
    XStringListToTextProperty (&title, 1, &windowName);
  else
    XStringListToTextProperty (&versionstring, 1, &windowName);
  XSetWMName (disp, window, &windowName);
  XSync (disp, True);
  return (window);
}

void 
usage (const char *msg)
{
  if (msg != NULL) cerr << msg << endl;
  cerr << "Use \"xplanetbg --help\" for a list of valid options\n";
  exit (EXIT_FAILURE);
}

void 
process_args (char *command, int argc, char **argv, int &wait,
	      struct s_orbit_info &orbit_info)
{
  for(int i = 1; i < argc; i++) 
    {
      if (strncmp (argv[i], "--help", 3) == 0) 
	{
	  cout << "valid options:\n";
	  cout << "\t--ba[ckground]         image_file (char *)\n";
	  cout << "\t--bl[end]                                 \n";
	  cout << "\t--bo[dy]               body       (int)   \n";
	  cout << "\t--ce[nter]             x,y        (int, int)\n";
	  cout << "\t--cl[oud_image]        image_file (char *)\n";
	  cout << "\t--co[lor]              colorname  (char *)\n";
	  cout << "\t--dat[e]               string     (char *)\n";
	  cout << "\t--day[side]\n";
	  cout << "\t--demf[ile]            dem_file   (char *)\n";
	  cout << "\t--dems[cale]           scale      (float)\n";
	  cout << "\t--e[arthside]\n";
	  cout << "\t--fo[nt]               fontname   (char *)\n";
	  cout << "\t--fu[zz]               pixels     (int)   \n";
	  cout << "\t--ge[ometry]           string     (char *)\n";
	  cout << "\t--grid                 spacing    (int)   \n";
	  cout << "\t--grids[pace]          spacing    (int)   \n";
	  cout << "\t--h[elp]\n";
	  cout << "\t--i[mage]              image_file (char *)\n";
	  cout << "\t--label                                   \n";
	  cout << "\t--labelpos             string     (char *)\n";
	  cout << "\t--lat[itude]           degrees    (float) \n";
	  cout << "\t--loc[altime]          time       (float) \n";
	  cout << "\t--lon[gitude]          degrees    (float) \n";
	  cout << "\t--markers                                 \n";
	  cout << "\t--marker_[file]        filename   (char *)\n";
	  cout << "\t--me[rcator]\n";
	  cout << "\t--mo[onside]\n";
	  cout << "\t--night_[image]        image_file (char *)\n";
	  cout << "\t--nights[ide]\n";
	  cout << "\t--no[transparency]\n";
	  cout << "\t--nu[m_times]          num_times  (int)   \n";
	  cout << "\t--ob[server]           lon,lat    (float, float)\n";
	  cout << "\t--orb[it] duration:inclination    (float):(float)\n";
	  cout << "\t--ort[hographic]\n";
	  cout << "\t--ou[tput]             filename   (char *)\n";
	  cout << "\t--rad[ius]             percent    (int)   \n";
	  cout << "\t--rand[om]                                \n";
	  cout << "\t--rang[e]              range      (float) \n";
	  cout << "\t--roo[t]\n";
	  cout << "\t--rot[ate]             degrees    (float) \n";
	  cout << "\t--sh[ade]              percent    (int)   \n";
	  cout << "\t--st[ar_den]           density    (float) \n";
	  cout << "\t--su[nrel]     del_lon,del_lat    (float, float)\n";
	  cout << "\t--sw[ap]\n";
	  cout << "\t--t[imewarp]           factor     (float) \n";
	  cout << "\t--v[ersion]\n";
	  cout << "\t--wa[it]               seconds    (int)   \n";
	  cout << "\t--wi[ndow]\n";
	  cout << "\nCharacters inside the square braces are optional\n";
	  exit (EXIT_SUCCESS);
	}
      else if (strncmp (argv[i], "--version",3) == 0) 
	{
	  cout << versionstring << endl;
	  cout << "For more information, see " 
	       << "http://www.alumni.caltech.edu/~hari/xplanet\n";
	  exit (EXIT_SUCCESS);
	}	    
      else if ((strncmp (argv[i], "--blend",4) == 0) ||
	       (strcmp  (argv[i], "--label") == 0) ||
	       (strncmp (argv[i], "--markers",9) == 0) ||
	       (strncmp (argv[i], "--mercator",4) == 0) ||
	       (strncmp (argv[i], "--orthographic",5) == 0) ||
	       (strncmp (argv[i], "--notransparency",4) == 0) ||
	       (strncmp (argv[i], "--random",6) == 0) ||
	       (strncmp (argv[i], "--root",5) == 0) ||
	       (strncmp (argv[i], "--swap",4) == 0))
	{
	  strcat (command,argv[i]);
	  strcat (command, " ");
	}
      else if ((strncmp (argv[i], "--background",4) == 0) ||
	       (strncmp (argv[i], "--body",4) == 0) ||
	       (strncmp (argv[i], "--center",4) == 0) ||
	       (strncmp (argv[i], "--cloud_image",4) == 0) ||
	       (strncmp (argv[i], "--color",4) == 0) ||
	       (strncmp (argv[i], "--demfile",6) == 0) ||
	       (strncmp (argv[i], "--demscale",6) == 0) ||
	       (strncmp (argv[i], "--font",4) == 0) ||
	       (strncmp (argv[i], "--fuzz",4) == 0) ||
	       (strcmp  (argv[i], "--grid") == 0) ||
	       (strncmp (argv[i], "--gridspace",7) == 0) ||
	       (strncmp (argv[i], "--image",3) == 0) ||
	       (strncmp (argv[i], "--labelpos",8) == 0) ||
	       (strncmp (argv[i], "--localtime",5) == 0) ||
	       (strncmp (argv[i], "--night_image",8) == 0) || 
	       (strncmp (argv[i], "--marker_file",9) == 0) ||
	       (strncmp (argv[i], "--observer",4) == 0) ||
	       (strncmp (argv[i], "--radius",5) == 0) ||
	       (strncmp (argv[i], "--range",6) == 0) ||
	       (strncmp (argv[i], "--rotate",5) == 0) ||
	       (strncmp (argv[i], "--shade",4) == 0) ||
	       (strncmp (argv[i], "--star_den",4) == 0) ||
	       (strncmp (argv[i], "--sunrel",4) == 0))
	{
	  strcat (command, argv[i]);
	  strcat (command, " ");
	  i++;
	  if (i >= argc) 
	    {
	      char errmsg[50] = "missing argument to ";
	      usage (strcat (errmsg,argv[i-1]));
	    }
	  strcat (command,argv[i]);
	  strcat (command, " ");
	}	  
      else if (strncmp (argv[i], "--date",5) == 0)
	{
	  i++;
#ifdef HAVE_STRPTIME
	  if (i >= argc) usage ("missing argument to --date");
	  if (tm == NULL) tm = localtime (&tval.tv_sec);
	  strptime (argv[i], "%d %b %Y %T", tm);
	  tval.tv_sec = mktime (tm);
#else
	  cerr << "This system does not have the strptime function.\n";
	  cerr << "The --date option will be ignored.\n";
#endif
	}
      else if (strncmp (argv[i], "--dayside",5) == 0) 
	{
	  strcat (command,argv[i]);
	  strcat (command, " ");
	  char *view = "Dayside view";
	  title = new char[strlen (view) + 1];
	  strcpy (title, view);
	}
      else if (strncmp (argv[i], "--earthside",3) == 0) 
	{
	  strcat (command,argv[i]);
	  strcat (command, " ");
	  char *view = "View from earth";
	  title = new char[strlen (view) + 1];
	  strcpy (title, view);
	}
      else if (strncmp (argv[i], "--geometry",4) == 0)
	{
	  strcat (command,argv[i]);
	  strcat (command, " ");
	  i++;
	  strcat (command,argv[i]);
	  strcat (command, " ");
	  XParseGeometry (argv[i], &x, &y, &width, &height);
	  window = 1;
	}
      else if (strncmp (argv[i], "--latitude",5) == 0)
	{
	  float obslat;
	  i++;
	  if (i >= argc) usage ("missing argument to --latitude");
	  sscanf(argv[i], "%f", &obslat);
	  if (obslat < -90 || obslat > 90) 
	    {
	      while(obslat < -90) obslat =  -180 - obslat;
	      while(obslat > 90) obslat = 180 - obslat;
	      cerr << "latitude set to " << obslat << " degrees\n";
	    }
	  orbit_info.pos_lat=obslat;
	}
      else if (strncmp (argv[i], "--longitude",4) == 0)
	{
	  float obslon;
	  i++;
	  if (i >= argc) usage ("missing argument to --longitude");
	  sscanf(argv[i], "%f", &obslon);
	  if (obslon < 0 || obslon > 360) 
	    {
	      obslon = fmod (obslon, 360.);
	      if (obslon < 0) obslon += 360;
	      cerr << "longitude set to " << obslon << " degrees\n";
	    }
	  orbit_info.pos_long=obslon;
	}
      else if (strncmp (argv[i], "--moonside",4) == 0) 
	{
	  strcat (command,argv[i]);
	  strcat (command, " ");
	  char *view = "View from the moon";
	  title = new char[strlen (view) + 1];
	  strcpy (title, view);
	}
      else if (strncmp (argv[i], "--nightside",8) == 0) 
	{
	  strcat (command,argv[i]);
	  strcat (command, " ");
	  char *view = "Nightside view";
	  title = new char[strlen (view) + 1];
	  strcpy (title, view);
	}
      else if (strncmp (argv[i], "--num_times",4) == 0)
	{
	  i++;
	  if (i >= argc) usage ("missing argument to --num_times");
	  sscanf(argv[i], "%d", &num_times);
	  if (num_times < 1) 
	    {
	      cerr << "--num_times must be positive\n";
	      exit (EXIT_FAILURE);
	    }
	}
      else if (strncmp (argv[i], "--orbit",5) == 0)
	{
	  float duration, inclination;
	  i++;
	  if (i >= argc) usage ("missing argument to --orbit");
	  sscanf(argv[i], "%f:%f", &duration,&inclination);
	  if (duration < 0)
	    {
	      cerr << "duration must be positive\n";
	      exit (EXIT_FAILURE);
	    }
	  orbit_info.duration=duration;
	  orbit_info.inclination=inclination;
	}
      else if (strncmp (argv[i], "--output",4) == 0)
	{
	  char *tmp = NULL;
	  i++;
	  if (i >= argc) usage ("missing argument to --output");
	  base = new char[strlen (argv[i]) + 1];
	  tmp = strchr (argv[i], '.');
	  if (tmp == NULL) 
	    {
	      char *defaultext = ".ppm";
	      cerr << "No extension specified, will use "<< defaultext << endl;
	      extension = new char[strlen (defaultext) + 1];
	      strcpy (extension, defaultext);
	      strcpy (base, argv[i]);
	    }
	  else
	    {
	      extension = new char[strlen (argv[i]) + 1];
	      strcpy (extension, tmp);
	      int iend = strlen (argv[i]) - strlen (extension);
	      strncpy (base, argv[i], iend);
	      base [iend] = '\0';
	    }
	  window = 0;
	}
      else if (strncmp (argv[i], "--timewarp",3) == 0)
	{
	  i++;
#ifdef HAVE_STRPTIME
	  if (i >= argc) usage ("missing argument to --timewarp");
	  sscanf (argv[i], "%f", &timewarp);
	  if (timewarp == 0) {
	    cerr << "timewarp must be non-zero\n";
	    exit (EXIT_FAILURE);
	  }
	  if (tm == NULL) tm = localtime (&tval.tv_sec);
#else
	  cerr << "This system does not have the strptime function.\n";
	  cerr << "The --timewarp option will be ignored.\n";
#endif
	}
      else if (strncmp (argv[i], "--wa[it]",4) == 0)
	{
	  i++;
	  if (i >= argc) usage ("missing argument to --wait");
	  sscanf(argv[i], "%d", &wait);
	}
      else if (strncmp (argv[i], "--window",4) == 0) 
	{
	  window = 1;
	}
      else
	{
	  cerr << "unknown option: " << argv[i] << endl;
	  usage (NULL);
	}
    }
  
  char cbuffer[256];

  /* If we haven't specified an orbit but we do have a 
     --longitude or --latitude argument */
  if (!orbit_info.duration)
    {
      if (orbit_info.pos_lat)
	{
	  sprintf (cbuffer, "--latitude %5.1f ", orbit_info.pos_lat);
	  strcat (command, cbuffer);
	}
      if (orbit_info.pos_long)
	{
	  sprintf (cbuffer, "--longitude %5.1f ", orbit_info.pos_long);
	  strcat (command, cbuffer);
	}
    }

  if (window) 
    {
      sprintf (cbuffer, "--XID %lu ", open_window ());
      strcat (command, cbuffer);
    }
}

int 
main (int argc, char **argv)
{
  int wait = 300;  // time, in seconds, between updates

  // Initialize to current time
#ifdef HAVE_STRPTIME
  gettimeofday (&tval, NULL);
#endif

  char stat_command[300] = "xplanet ";
  char dyn_command[300], cbuffer[256];
  struct s_orbit_info obi = {0,0,0,0};
  process_args (stat_command, argc, argv, wait, obi);

  Orbit orb (obi.duration, obi.pos_lat, obi.pos_long, obi.inclination);
 
  double prev_lat = obi.pos_lat;
  double prev_lon = obi.pos_long;

  int current_time = 0;

  while (1)
    {
      strcpy (dyn_command, stat_command);
      
      if (num_times != 0) 
	{
	  current_time++;
	  if (current_time > num_times) break;
	  if (base != NULL)
	    {
	      int digits = int (log10 ((double) num_times) + 1);
	      sprintf (cbuffer, "--output %s%.*d%s ", base, digits, 
		       current_time, extension);
	      strcat (dyn_command, cbuffer);
	    }
	}
      else
	{
	  if (base != NULL)
	    {
	      sprintf (cbuffer, "--output %s%s ", base, extension);
	      strcat (dyn_command, cbuffer);
	    }
	}	      
      
      if (obi.duration > 0)
	{
	  double orblat = orb.Lat();
	  double orblon = orb.Long();
	  sprintf (cbuffer, "--latitude %5.1f ", orblat);
	  strcat (dyn_command, cbuffer);
	  sprintf (cbuffer, "--longitude %5.1f ", orblon);
	  strcat (dyn_command, cbuffer);
	  if (prev_lat - orblat > 0 && fabs (prev_lat - orblat) > 1e-5)
	    {
	    sprintf (cbuffer, "--rotate 180");
	    strcat (dyn_command, cbuffer);
	    }
	  prev_lat = orblat;
	  prev_lon = orblon;
	}
      
#ifdef HAVE_STRPTIME
      if (tm != NULL) 
	{
	  strftime (cbuffer, sizeof(cbuffer), "%d %b %Y %T", tm);
	  strcat (dyn_command, " --date \"");
	  strcat (dyn_command, cbuffer);
	  strcat (dyn_command, "\" ");
	  tval.tv_sec += (time_t) timewarp * wait;
	  tm = localtime (&tval.tv_sec);
	}
#endif
      
      if (base != NULL) cout << dyn_command << endl;
      
      if (system (dyn_command) != 0) break;
      
      if (obi.duration > 0) orb.WaitTime (timewarp * wait/3600.0);
      
      if (window)
	{
	  Atom wmDeleteWindow = XInternAtom (disp, "WM_DELETE_WINDOW", False);
	  XSetWMProtocols (disp, window, &wmDeleteWindow, 1);
	  XEvent report;
	  for (int i = 0; i < wait; i++)
	    {
	      if ((XCheckTypedWindowEvent (disp, window, 
					   ClientMessage, &report) == True) 
		  && ((unsigned int) report.xclient.data.l[0] 
		      == wmDeleteWindow))
		exit (EXIT_SUCCESS);
	      else
		sleep (1);
	    }
	}
      else
	sleep (wait);

     } 
  exit (EXIT_FAILURE);
}
