/*
 * Copyright (C) 1999 Sasha Vasko   <sashav@sprintmail.com>
 * Copyright (C) 1999 Ethan Fischer <allanon@crystaltokyo.com>
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/time.h>

#if defined ___AIX || defined _AIX || defined __QNX__ || defined ___AIXV3 || defined AIXV3 || defined _SEQUENT_
#include <sys/select.h>
#endif

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

#include "../configure.h"
#include "../include/aftersteplib.h"
#include "../include/module.h"


char* module_get_socket_property(Window w)
{
  char* data;
  int actual_format;
  Atom name, actual_type;
  unsigned long junk;

  name = XInternAtom (dpy, "_AS_MODULE_SOCKET", False);

  if (XGetWindowProperty(dpy, w, name, 0, ~0, False, AnyPropertyType, &actual_type, &actual_format, &junk, &junk, (unsigned char**) &data) != Success)
    return NULL;

  if (actual_type != XA_STRING || actual_format != 8)
    {
      XFree (data);
      return NULL;
    }

  return data;
}

int module_connect(const char* socket_name)
{
  int fd;

  /* create an unnamed socket */
  if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
    fprintf(stderr, "%s: unable to create UNIX socket: ", MyName);
    perror("");
  }

  /* connect to the named socket */
  if (fd >= 0) {
    struct sockaddr_un name;

    name.sun_family = AF_UNIX;
    strcpy(name.sun_path, socket_name);

    if (connect(fd, (struct sockaddr *)&name, sizeof(struct sockaddr_un))) {
      fprintf(stderr, "%s: unable to connect to socket '%s': ", MyName, name.sun_path);
      perror("");
      close(fd);
      fd = -1;
    }
  }

  return fd;
}

int GetFdWidth (void);

ASMessage*
CheckASMessageFine (int fd, int t_sec, int t_usec)
{
  fd_set in_fdset;
  ASMessage *msg = NULL;
  struct timeval tv;
  static int fd_width = 0 ;

  if( fd_width == 0 ) fd_width = GetFdWidth();
  FD_ZERO (&in_fdset);
  FD_SET (fd, &in_fdset);
  tv.tv_sec  = t_sec ;
  tv.tv_usec = t_usec ;
#ifdef __hpux
  while (select (fd_width, (int *) &in_fdset, 0, 0, (t_sec<0)?NULL:&tv) == -1)
    if (errno != EINTR)
      break;
#else
  while (select (fd_width, &in_fdset, 0, 0, (t_sec<0)?NULL:&tv) == -1)
    if (errno != EINTR)
      break;
#endif
  if (FD_ISSET (fd, &in_fdset)) 
  {
      msg = (ASMessage*)safemalloc(sizeof(ASMessage));
      if( ReadASPacket (fd, msg->header, &(msg->body))<=0 ) 
      {
          free(msg);
	  msg = NULL ;
      }
  }

  return msg ;
}

void DestroyASMessage( ASMessage* msg )
{
    if( msg )
    {
	if( msg->body ) free( msg->body );
	free( msg );
    }
}

/***********************************************************************
 *
 *  Procedure:
 *	SendInfo - send a command back to afterstep 
 *
 ***********************************************************************/
void
SendInfo (int *fd, char *message, unsigned long window)
{
  size_t w;

  if (message != NULL)
    {
      write (fd[0], &window, sizeof (unsigned long));
      w = strlen (message);
      write (fd[0], &w, sizeof (int));
      write (fd[0], message, w);

      /* keep going */
      w = 1;
      write (fd[0], &w, sizeof (int));
    }
}


/* some */
void SetMyName( char* argv0 )
{
 char* temp = strrchr (argv0, '/');
  /* Save our program name - for error messages */
  MyName = temp ? temp + 1 : argv0;
}

void DeadPipe( int nonsense );

int ConnectAfterStep(unsigned long message_mask)
{
  char* temp ;
  char mask_mesg[32];
  int as_fd[2] ;
    /* connect to AfterStep */
    /* Dead pipe == AS died */
    signal (SIGPIPE, DeadPipe);
    temp = module_get_socket_property(RootWindow(dpy, screen));
    as_fd[0] = as_fd[1] = module_connect (temp);
    XFree (temp);

    if(as_fd[0] < 0)
    {
	fprintf (stderr, "%s: unable to establish connection to AfterStep\n", MyName);
        exit (1);
    }

    temp = safemalloc (9 + strlen(MyName) + 1);
    sprintf (temp, "SET_NAME %s", MyName);
    SendInfo (as_fd, temp, None);
    free (temp);
  
    sprintf(mask_mesg, "SET_MASK %lu\n", (unsigned long)message_mask);
    SendInfo(as_fd, mask_mesg, None);
    return as_fd[0] ;
}

void LoadBaseConfig(char* global_config_file, void (*read_base_options_func)(const char*))
{
  if (global_config_file != NULL)
       read_base_options_func(global_config_file);
  else
    {
      char* realconfigfile ;
      char configfile[255];
      int depth = DefaultDepth (dpy, DefaultScreen(dpy));
       sprintf (configfile, "%s/base.%dbpp", AFTER_DIR, depth);
       realconfigfile = (char *) PutHome (configfile);
       if (CheckFile (realconfigfile) == -1)
       {
           free (realconfigfile);
           sprintf (configfile, "%s/base.%dbpp", AFTER_SHAREDIR, depth);
           realconfigfile = PutHome (configfile);
       }
       read_base_options_func(realconfigfile);
       free (realconfigfile);
    }
}

void LoadConfig(char* global_config_file, char* config_file_name, void (*read_options_func)(const char*))
{
    if (global_config_file != NULL) read_options_func( global_config_file );
    else
    {
      char* realconfigfile;
      char configfile[255];
	sprintf( configfile, "%s/%s", AFTER_DIR, config_file_name );
	realconfigfile = (char *) PutHome( configfile );
	if ((CheckFile (realconfigfile)) == -1)
	{
	    free (realconfigfile);
	    sprintf (configfile, "%s/%s", AFTER_SHAREDIR, config_file_name);
	    realconfigfile = PutHome (configfile);
	}
	read_options_func(realconfigfile );
	free (realconfigfile);
    }
}

/**********************************************************************/
/* 		ModuleInfo functions 				      */
/**********************************************************************/
void default_version_func(void)
{
  printf("%s version %s\n", MyName, VERSION);
  exit(0);
}
void (*custom_version_func)(void) = default_version_func;

int ProcessModuleArgs(int argc, char** argv, char**global_config_file, unsigned long *app_window, unsigned long *app_context, void (*custom_usage_func)(void))
{
 int i ;
  for (i = 1 ; i < argc && *argv[i] == '-' ; i++)
    {
      if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help"))
      {	
          if(custom_usage_func ) custom_usage_func(); 
	  else exit(0);
      }else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version"))
      {	
          if(custom_version_func ) custom_version_func(); 	
	  else exit(0);
      }else if (!strcmp(argv[i], "-w") || !strcmp(argv[i], "--window"))
      { 
          i++ ;
          if( app_window ) *app_window = strtol (argv[i], NULL, 16);
      }else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--context"))
      { 
          i++ ;
          if( app_context ) *app_context =  strtol (argv[i], NULL, 16);
      }else if (!strcmp(argv[i], "-f") && i + 1 < argc)
      { 
          i++ ;
          if( global_config_file ) *global_config_file = argv[i]; 
      }else return i ;
      
    }
  return i ;
}


