/*
 * Copyright (c) 1993, 1999 Alexandre Wennmacher (wennmach@geo.Uni-Koeln.DE)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 * This product includes software developed by Alexandre Wennmacher.
 * 4. The name of Alexandre Wennmacher may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY ALEXANDRE WENNMACHER AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL ALEXANDRE WENNMACHER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <sys/types.h>
#include <sys/signal.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "const.h"
#include "Sphere.h"
#include "SphereDim.h"
#include "SphereData.h"
#include "color.h"
#include "level.h"

/* Solaris hack */
#ifndef MAP_FILE
#define MAP_FILE 0x0
#endif

#define SIZE       150
#define MIN_SIZE    50
#define X_POS       +0
#define Y_POS       +0
#define BORDER       5
#define SLEEP      120

#define MIN(x,y)   (((x)<(y))?(x):(y)) 

char *MainTitle[] = {"Hello, world"};
char *IconTitle[] = {"xworld"};

XWMHints	xwmh = {
    (InputHint|StateHint),
    False,
    NormalState,
    0,
    0,
    0, 0,
    0,
    0,
};

int GetSpherePoint(Sphere *, SPHERETYPE *, double, double);
long int JulianDate(int, int, int);
double EqunofTime(double, double *, double *);
char *getmyabspath(char *, char *);
int usage(void);
int main(int, char **);

int
usage()
{
    fprintf(stderr, "usage: xworld [-geometry [=]widthxheight+x+y]"
      " [-display dpy]\n");
    exit(1);
}


static char *
alloc_image(XImage *image)
{
    char *Map;
    int ix, iy, j;
    Map = (char *)malloc(image->height * image->bytes_per_line);
    if (Map == NULL) {
	fprintf(stderr, "xworld: not enough memory\n");
	exit(1);
    }
    image->data = Map; 
    
    for(j = 0, ix = 0; ix < image->width; ix++)
	for (iy = 0; iy < image->height; iy++)
	    XPutPixel(image, ix, iy, j++ % NCOLORS);
    
    return Map;
}

int
main(int argc, char **argv)
{
    Display                  *display;
    char                     *displayname = NULL;
    Window                    win;
    GC                        gc;
    Visual                   *visual;
    unsigned int              class;
    unsigned int              depth;
    int                       screen;
    XEvent                    event;
    XConfigureEvent          *ConfigureEvent;
    Mask                      InputSelectMask;
    XSizeHints                xsh;
    XSetWindowAttributes      xswa;
    unsigned int              xswamask;
    XTextProperty             xtptitle;
    XColor                    xcolor[NCOLORS];
    Colormap                  cmap;
    int                       format;
    int                       bitmap_pad;
    XImage                   *image;
    unsigned long             foreground, background;
    int                       mask;
    int                       x_return, y_return;
    unsigned int              width_return, height_return;

    unsigned int              size, old_size;
    unsigned int              x_pos, y_pos;
    unsigned int              border = 1;
    int                       n;
    int                       r1, r2;

    int                       jr, m, d, hr, min, sec;
    double                    juldat, sunra, sundec;
    double                    daypart, phi;
    struct tm                *gmt;
    time_t                    clock;

    int                       i, j, ix, iy;
    double                    tTheta, tPhi;
    double                    x, y, z;
    double                    x2, y2, z2;
    double                    xp, yp, zp;
    double                    cost, sint;
    double                    cosp, sinp;
    double                    m11, m12, m13, m21, m22, m23, m32, m33;
    SPHERETYPE                Dummy = 1;
    SPHERETYPE                Value;
    Sphere                    Earth;
    char                     *Map;

    char                     *progname;
    FILE                     *f_o;
    register_t               *addr;
    const size_t              len = NPOINTS*sizeof(register_t);
    off_t                     offset;
    char                    **argp;
    char                     *geometry;
    int                       opt_geometry = 0;
    int                       opt_display = 0;

/*
 * Check if xworld is installed correctly.
 * Determine the absolute path with which xworld was invoked,
 * open and mmap the data portion, appended to the executable.
 */
    progname = getmyabspath(argv[0], getenv("PATH"));
    if (progname == NULL) {
        fprintf(stderr, "xworld: could not determine my absolute filename.\n"
          "Abort.\n");
        exit(1);
    }
    f_o   = fopen(progname, "r");
    if (f_o == NULL) {
        perror("xworld: could not open myself");
        exit(1);
    }
    offset = lseek(fileno(f_o), (off_t)0, SEEK_END) - len;
    addr = (register_t *)mmap(NULL, len, PROT_READ, MAP_FILE | MAP_SHARED,
      fileno(f_o), offset);
    if (addr == (register_t *)MAP_FAILED) {
        perror("xworld: could not mmap");
        exit(1);
    }

/* Next, parse command line */
    argp = argv;
    while (*++argp != NULL) {
       if (strcmp(*argp, "-geometry") == 0) {
           geometry = *++argp;
           if (geometry == NULL) usage();
           mask = XParseGeometry(geometry, &x_return, &y_return,
               &width_return, &height_return);
           if (mask == 0) usage();
           if (mask & WidthValue && mask & HeightValue)
               if (width_return != height_return) {
                   fprintf(stderr, "xworld: error: width must equal height!\n");
                   exit(1);
               }
           opt_geometry++;
           break;
       }
       if (strcmp(*argp, "-display") == 0) {
           displayname = *++argp;
           if (displayname == NULL) usage();
           opt_display++;
           break;
       }
       usage();
    }


/* Open display */
    if ((display = XOpenDisplay(displayname)) == NULL) {
        fprintf(stderr, "xworld: can't open display %s\n",
            XDisplayName(displayname));
        exit(1);
    }

    screen = DefaultScreen(display);
    depth = DefaultDepth(display, screen);
    if (depth == 1) { 
        fprintf(stderr, "xworld: Sorry, I can't yet work with a display of"
            " depth 1\n");
        exit(1);
    } else { 
        format = ZPixmap;
        bitmap_pad = 8;
        if (depth > 8) bitmap_pad = 32; 
    }

    visual = DefaultVisual(display, screen);

/* Set default values */
    size  =  SIZE;
    x_pos =  X_POS;
    y_pos =  Y_POS;

    if (opt_geometry > 0) {
        if (mask & WidthValue) size = width_return;
        if (mask & HeightValue) size = height_return;
        if (size < MIN_SIZE) {
            fprintf(stderr, "xworld: warning: using minumum size of %d in"
              " lieu of user supplied value.\n", MIN_SIZE);
            size = MIN_SIZE;
        }
        if (mask & XValue) {
            if (mask & XNegative) 
              x_pos = DisplayWidth(display, screen) - size + x_return;
            else
              x_pos = x_return;
        }
        if (mask & YValue) {
            if (mask & YNegative)
              y_pos = DisplayHeight(display, screen) - size + y_return;
            else
              y_pos = y_return;
        }
    }
    old_size = size;
    n = size - 2*BORDER;
    r1 = n/2;
    r2 = size/2;

    foreground = WhitePixel(display, screen);
    background = BlackPixel(display, screen);

    image = XCreateImage(display, visual, depth, format, 0, 0, size, size,
      bitmap_pad, 0);
    if (image == 0) {
        fprintf(stderr, "xworld: can't create XImage\n");
        exit(1);
    }

    xswa.event_mask = ExposureMask | StructureNotifyMask;
    xswa.background_pixel = BlackPixel(display, screen);
    xswamask = CWEventMask | CWBackPixel;

    class   = InputOutput;
    win = XCreateWindow(display, RootWindow(display, screen), x_pos, y_pos,
      size, size, border, depth, class, visual, xswamask, &xswa);

    if (XStringListToTextProperty(MainTitle, 1, &xtptitle))
      XSetWMName(display, win, &xtptitle);

    if (XStringListToTextProperty(IconTitle, 1, &xtptitle))
      XSetWMIconName(display, win, &xtptitle);

    xsh.flags = USPosition | USSize | PPosition | PSize | PMinSize | PAspect;
    xsh.x = x_pos;
    xsh.y = y_pos;
    xsh.min_width = MIN_SIZE;
    xsh.base_width = size;
    xsh.min_height = MIN_SIZE;
    xsh.base_height = size;
    xsh.min_aspect.x = 1;
    xsh.min_aspect.y = 1;
    xsh.max_aspect.x = 1;
    xsh.max_aspect.y = 1;
   
    XSetWMHints(display, win, &xwmh);
    XSetWMNormalHints(display, win, &xsh);
   
/* Allocate colors */
    cmap = DefaultColormap(display, screen);
    for (i = 0; i < NCOLORS; i++) {
        xcolor[i].red = color[i].red;
        xcolor[i].green = color[i].green;
        xcolor[i].blue = color[i].blue;
        if (XAllocColor(display, cmap, &xcolor[i]) == 0) {
            fprintf(stderr, "xworld: can't allocate colors\n");
#if 0
            exit(-1);
#endif
        }
    } 

/*
 * generate startup image
 */
    Map = alloc_image(image);

    gc = XCreateGC(display, win, 0, 0);

    InputSelectMask = ExposureMask | StructureNotifyMask;
    XSelectInput(display, win, InputSelectMask);
    XPutImage(display, win, gc, image, 0, 0, 0, 0, size, size); 
    XMapWindow(display, win);

    Earth.SphereBuffer = addr;
    Earth.CoLatitude = CoLatitude;
    Earth.PointSeparation = PointSeparation;
    Earth.FirstPointOfLatitude = FirstPointOfLatitude;
    Earth.PointsPerLatitude = PointsPerLatitude;
    Earth.DeltaLatitude = PI/NINTERVALS;
    Earth.LastPoint = Earth.FirstPointOfLatitude[NINTERVALS];
    Earth.Dummy = Dummy;

/*
 * begin of main loop -- xworld will run until the process is killed
 */
    for ( ; ; ) {

/*
 * read the clock
 */
        clock = time((time_t *)NULL);
        if (clock == (time_t)-1) {
            fprintf(stderr, "xworld: can't read clock\n");
            exit(1);
        }

/*
 * deal with gm time
 */
        gmt = gmtime(&clock);
        jr = gmt->tm_year + 1900;
        m = gmt->tm_mon + 1;
        d = gmt->tm_mday;
        hr = gmt->tm_hour;
        min = gmt->tm_min;
        sec = gmt->tm_sec;

/*
 * do some basic astronomy
 */
        juldat = (double)JulianDate(jr, m, d) - 0.5;
        daypart = (sec + 60*(min + 60*hr))/86400.0;
        juldat += daypart;
        phi = daypart*TWO_PI + EqunofTime(juldat, &sunra, &sundec);

        sint = sin(sundec);
        cost = cos(sundec); 
        sinp = sin(PI - phi);
        cosp = cos(PI - phi);
        m11 = -sinp;
        m12 = -cosp*sint;
        m13 =  cosp*cost;
        m21 =  cosp;
        m22 = -sinp*sint;
        m23 =  sinp*cost;
/*      m31 =  0.0;  therfore we omit it here */
        m32 =  cost;
        m33 =  sint;

        j = -1;
        for (iy = 0; iy < size; iy++) {
            y = (double)(r2 - iy)/r1;
            y2 = y*y;
            for (ix = 0; ix < size; ix++) {
                j++;
                x = (double)(ix - r2)/r1;
                x2 = x*x;
                z2 = (double)1.0 - (x2 + y2);
                if (z2 >= (double)0.0) {
                    z = sqrt(z2);
                    xp = m11*x + m12*y + m13*z;
                    yp = m21*x + m22*y + m23*z;
                    zp =         m32*y + m33*z;
                    tTheta = acos(zp);
                    tPhi = atan2(yp, xp);
                    i = GetSpherePoint(&Earth, &Value, tTheta, tPhi);

/*
 * Map elevations to colors
 */
                    if (i != SOK) {
			XPutPixel(image, ix, iy, xcolor[BLACK].pixel);
                        continue;
                    }
                    if (Value > level[NLEVELS - 1]) {
			XPutPixel(image, ix, iy, xcolor[HIGH].pixel);
                        continue;
                    }
                    for (i = 0; i <= NLEVELS - 1; i++) {
                        if (Value <= level[i]) {
			    XPutPixel(image, ix, iy, xcolor[i].pixel);
                            break;
                        }
                    }
                } else
		    XPutPixel(image, ix, iy, xcolor[BLACK].pixel);
            }
        }
        XPutImage(display, win, gc, image, 0, 0, 0, 0, size, size);
        for (i = 0; i < SLEEP; i++) {
            sleep(1);
            if (XCheckMaskEvent(display, InputSelectMask, &event)) {
                if (event.type == Expose && event.xexpose.count == 0) {
                    while (XCheckTypedEvent(display, Expose, &event));
                    XPutImage(display, win, gc, image, 0, 0, 0, 0, size, size);
                }
                if (event.type == ConfigureNotify) {
                    while (XCheckTypedEvent(display, ConfigureNotify, &event));
                    ConfigureEvent = (XConfigureEvent *)&event;
                    size = ConfigureEvent->width;
                    if (ConfigureEvent->height != size) {
#if 0
                        fprintf(stderr, "xworld: error: width != height\n");
                        exit(1);
#else
			size = MIN(size, ConfigureEvent->height);
#endif
                    }
                    if (size != old_size) {
                        old_size = size;
                        n = size - 2*BORDER;
                        r1 = n/2;
                        r2 = size/2;
                        XDestroyImage(image);
                        image = XCreateImage(display, visual, depth, format,
                          0, 0, size, size, bitmap_pad, 0);
                        if (image == NULL) {
                            fprintf(stderr, "xworld: can't create XImage\n");
                            exit(1);
                        }
			Map = alloc_image(image);
 
                        XPutImage(display, win, gc, image, 0, 0, 0, 0,
                          size, size);
                        break;
                    }
                }
            }
        }
    }
}


syntax highlighted by Code2HTML, v. 0.9.1