/*
  Analogue Clock Local Time X11 Desktop Application in C++
  RJM Programming
  September, 2019
  Thanks to ttps://www.linuxjournal.com/article/4879
*/
#include <stdio.h>      /* puts, printf */
#include <stdlib.h>      /* puts, printf */
#include <time.h>       /* time_t, struct tm, time, localtime */
#include <string.h>
#include <unistd.h>
#include <cmath>
#include <iostream>
#include <ctime>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

#define sinDegrees(x) sin((x) * M_PI / 180.0)
#define cosDegrees(x) cos((x) * M_PI / 180.0)

XGCValues gr_values;
XFontStruct *fontinfo;
GC gr_context;
XColor colour, dummy;
double hhang;
double mhang;
double shang;
//Display *display;
Display *dsp=NULL;
Window win;
int origx=50;
int origy=50;
time_t gtime_ptr; 
tm* gtm_local;
int kk=1;

bool doesFileExist(const std::string& name) { // thanks to https://stackoverflow.com/questions/46292764/check-for-file-existence-in-c-without-creating-file
    if (FILE *file = fopen(name.c_str(), "r")) {
        fclose(file);
        return true;
    } else {
        return false;
    }   
}

int redraw(std::string suffix, int jk) {
  try {

  dsp = XOpenDisplay( NULL );
  if( !dsp ){ return 1; }
  
  fontinfo = XLoadQueryFont(dsp,"6x10");

  int screenNumber = DefaultScreen(dsp);
  unsigned long white = WhitePixel(dsp,screenNumber);
  unsigned long black = BlackPixel(dsp,screenNumber);
  
  double seconds=0.0;

  time_t time_ptr; 
  time_t time_ptr2; 
  tm* tm_local2;
  time_ptr = time(NULL); 
  
  // Get the localtime 
  tm* tm_local = localtime(&time_ptr); 

  if (tm_local->tm_hour >= 12) {
  win = XCreateSimpleWindow(dsp,
                               DefaultRootWindow(dsp),
                               origx, origy,   // origin
                               200, 200, // size
                               0, white, // border
                               black );  // backgd
  } else {
  win = XCreateSimpleWindow(dsp,
                               DefaultRootWindow(dsp),
                               origx, origy,   // origin
                               200, 200, // size
                               0, black, // border
                               white );  // backgd
  }
  
  std::string buffer (80, '\0');
  char buff[80];
  strftime(buff,80,"%Z",tm_local);
  //*(buff + 79)=0;
  //puts (buffer);
  
  if (kk > 1) {
  memcpy((buff + strlen(buff)), (-3 + strstr(ctime(&time_ptr), ":")), 21); //, (1 + strlen(suffix)));
  } else if (jk <  strlen("  ... Click to refresh\0")){
  memcpy((buff + strlen(buff)), ((char *)("  ... Click to refresh\0\0\0\0") + jk), (-jk + 2 + strlen("  ... Click to refresh\0"))); //, (1 + strlen(suffix)));
  }
  
  //strcat((char *)buffer, " ");
  //strcat((char *)buffer, suffix);  // " ... Click to refresh");
  
  
  buffer.append(buff);
  //strcat(buff, suffix);
  buffer.append(" ");
  buffer.append(suffix); 
  
  XStoreName( dsp, win, buff );
  XSetIconName( dsp, win, buff );

  XMapWindow( dsp, win );


  long eventMask = StructureNotifyMask;
  XSelectInput( dsp, win, eventMask );

  XEvent evt;
  do {
  time_ptr = time(NULL); 
  
  // Get the localtime 
  tm* tm_local = localtime(&time_ptr); 
    XNextEvent( dsp, &evt );   // calls XFlush
  } while( evt.type != MapNotify );

  XAllocNamedColor(dsp, DefaultColormap(dsp, screenNumber),"red",
                      &colour,&dummy);
  gr_values.font = fontinfo->fid;
  gr_values.foreground = colour.pixel;

  GC gc = XCreateGC( dsp, win,
                     GCFont+GCForeground,        // mask of values
                     &gr_values );   // array of values
  if (tm_local->tm_hour >= 12) {
  XSetForeground( dsp, gc, white );
  hhang=-90.0 + ((tm_local->tm_hour - 12) * 30.0 + tm_local->tm_min * 0.5 + tm_local->tm_sec * 0.00733);  
  mhang=-90.0 + (tm_local->tm_min * 6.0 + tm_local->tm_sec * 0.1);
  shang=-90.0 + (tm_local->tm_sec * 6.0);
  XDrawLine(dsp, win, gc, 100, 100,100 + cosDegrees(hhang) * 40,100 + sinDegrees(hhang) * 40); //from-to
  XDrawLine(dsp, win, gc, 100, 100,100 + cosDegrees(mhang) * 80,100 + sinDegrees(mhang) * 80); //from-to
  XDrawLine(dsp, win, gc, 100, 100,99.3 + cosDegrees(hhang) * 40,99.3 + sinDegrees(hhang) * 40); //from-to
  XDrawLine(dsp, win, gc, 100, 100,99.3 + cosDegrees(mhang) * 80,99.3 + sinDegrees(mhang) * 80); //from-to
  XDrawLine(dsp, win, gc, 100, 100,100.7 + cosDegrees(hhang) * 40,100.7 + sinDegrees(hhang) * 40); //from-to
  XDrawLine(dsp, win, gc, 100, 100,100.7 + cosDegrees(mhang) * 80,100.7 + sinDegrees(mhang) * 80); //from-to

  XDrawLine(dsp, win, gc, 100, 100,99.3 + cosDegrees(hhang) * 40,100.7 + sinDegrees(hhang) * 40); //from-to
  XDrawLine(dsp, win, gc, 100, 100,100.7 + cosDegrees(mhang) * 80,99.3 + sinDegrees(mhang) * 80); //from-to
  XDrawLine(dsp, win, gc, 100, 100,100.7 + cosDegrees(hhang) * 40,99.3 + sinDegrees(hhang) * 40); //from-to
  XDrawLine(dsp, win, gc, 100, 100,99.3 + cosDegrees(mhang) * 80,100.7 + sinDegrees(mhang) * 80); //from-to

  XDrawLine(dsp, win, gc, 100, 100,100 + cosDegrees(shang) * 20,100 + sinDegrees(shang) * 20); //from-to
  } else {
  XSetForeground( dsp, gc, black );
  hhang=-90.0 + ((tm_local->tm_hour - 0) * 30.0 + tm_local->tm_min * 0.5 + tm_local->tm_sec * 0.00733);
  mhang=-90.0 + (tm_local->tm_min * 6.0 + tm_local->tm_sec * 0.1);
  shang=-90.0 + (tm_local->tm_sec * 6.0);
  XDrawLine(dsp, win, gc, 100, 100,100 + cosDegrees(hhang) * 40,100 + sinDegrees(hhang) * 40); //from-to
  XDrawLine(dsp, win, gc, 100, 100,100 + cosDegrees(mhang) * 80,100 + sinDegrees(mhang) * 80); //from-to
  XDrawLine(dsp, win, gc, 100, 100,99.3 + cosDegrees(hhang) * 40,99.3 + sinDegrees(hhang) * 40); //from-to
  XDrawLine(dsp, win, gc, 100, 100,99.3 + cosDegrees(mhang) * 80,99.3 + sinDegrees(mhang) * 80); //from-to
  XDrawLine(dsp, win, gc, 100, 100,100.7 + cosDegrees(hhang) * 40,100.7 + sinDegrees(hhang) * 40); //from-to
  XDrawLine(dsp, win, gc, 100, 100,100.7 + cosDegrees(mhang) * 80,100.7 + sinDegrees(mhang) * 80); //from-to

  XDrawLine(dsp, win, gc, 100, 100,99.3 + cosDegrees(hhang) * 40,100.7 + sinDegrees(hhang) * 40); //from-to
  XDrawLine(dsp, win, gc, 100, 100,100.7 + cosDegrees(mhang) * 80,99.3 + sinDegrees(mhang) * 80); //from-to
  XDrawLine(dsp, win, gc, 100, 100,100.7 + cosDegrees(hhang) * 40,99.3 + sinDegrees(hhang) * 40); //from-to
  XDrawLine(dsp, win, gc, 100, 100,99.3 + cosDegrees(mhang) * 80,100.7 + sinDegrees(mhang) * 80); //from-to

  XDrawLine(dsp, win, gc, 100, 100,100 + cosDegrees(shang) * 20,100 + sinDegrees(shang) * 20); //from-to
  }

  //XDrawLine(dsp, win, gc, 10, 10,190,190); //from-to
  //XDrawLine(dsp, win, gc, 10,190,190, 10); //from-to
  XDrawLine(dsp, win, gc, 100, 10,100,190); //from-to
  XDrawLine(dsp, win, gc, 10, 100,190,100); //from-to
  //XDrawLine(dsp, win, gc, 55, 10,145,190); //from-to
  //XDrawLine(dsp, win, gc, 145, 10,55,190); //from-to

  //XDrawLine(dsp, win, gc, 10, 55,190,145); //from-to
  //XDrawLine(dsp, win, gc, 10, 145,190,55); //from-to

  XDrawLine(dsp, win, gc, 42, 10,158,190); //from-to
  XDrawLine(dsp, win, gc, 158, 10,42,190); //from-to

  XDrawLine(dsp, win, gc, 10, 42,190,158); //from-to
  XDrawLine(dsp, win, gc, 10, 158,190,42); //from-to


  eventMask = ResizeRedirectMask|StructureNotifyMask|ButtonPressMask|ButtonReleaseMask;
  XSelectInput(dsp,win,eventMask); // override prev
  
  int i=0;

  do {
    i++;
    XNextEvent( dsp, &evt );   // calls XFlush()
    usleep(200);
    time_ptr2 = time(NULL);
    tm_local2 = localtime(&time_ptr2); 
    seconds = difftime(time(NULL), time_ptr);
    XResizeWindow(dsp, win, 220 + (i % 11), 220 + (i % 11));
  } while( evt.type != ButtonRelease && seconds < 1.0 ); //ButtonRelease );
  //usleep(1000);

  XDestroyWindow( dsp, win );
  XCloseDisplay( dsp );
  dsp = NULL;

  }
  catch (int ez) { if (1 == 2) { printf("%i\n", ez); } }

  return 0;
}


int main(int argc, char* argv[]) {
 int status=0;
 int ostatus=256;
 int jj=0;
 char datehuh[80]="TZ=                                                                       date\0";
 char gbuff[81]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
 kk=argc;
 gtime_ptr = time(NULL); 
 gtm_local = localtime(&gtime_ptr); 
 strftime(gbuff,80,"%Z",gtm_local); 
 std::string suff (" ... Click to refresh as required\0");
 //suff.assign((std::string)" ... Click to refresh as required");
 if (kk <= 1) { if (doesFileExist("get_tz.php")) { status=std::system("php -f get_tz.php"); ostatus=status; } } else { if (strstr(argv[1], "/")) {  usleep(1100);  if (doesFileExist("stop_x1.x1")) {  remove("stop_x1.x1");  } usleep(1100);  memcpy((datehuh + 3), argv[1], strlen(argv[1])); } else { kk=1; } } 
 while (!doesFileExist("stop_x1.x1")) {
   if (dsp) {
  XDestroyWindow( dsp, win );
  XCloseDisplay( dsp );
   }
   redraw(suff.substr (jj), jj);
   jj = ((jj + 1) % suff.length());
   if (kk <= 1) { std::system("date"); } else { gtime_ptr = time(NULL);  printf("%s%s\n", ctime(&gtime_ptr), gbuff);  }
   //usleep(200);
 }
 if (doesFileExist("stop_x1.x1")) {
   remove("stop_x1.x1");
   exit(EXIT_FAILURE);
 }
 return 0;
}
