/* -*-c-*- */
/* This module, and the entire FvwmSaveDesktop program, and the concept for
 * interfacing this module to the Window Manager, are all original work
 * by Robert Nation and Mr. Per Persson <pp@solace.mh.se>
 *
 * The concept to write an function definition instead of an new.xinitrc
 * is from Carsten Paeth <calle@calle.in-berlin.de>
 *
 * Copyright 1994, Robert Nation and Mr. Per Persson.
 *  No guarantees or warantees or anything
 * are provided or implied in any way whatsoever. Use this program at your
 * own risk. Permission to use this program for any purpose is given,
 * as long as the copyright is kept intact.
 *
 * Copyright 1995, Carsten Paeth.
 *  No guarantees or warantees or anything
 * are provided or implied in any way whatsoever. Use this program at your
 * own risk. Permission to use this program for any purpose is given,
 * as long as the copyright is kept intact.
 *
 */

/* 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 "config.h"

#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/wait.h>
#include "libs/ftime.h"
#include <unistd.h>
#include <ctype.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>

#include "libs/Module.h"

#include "FvwmSaveDesk.h"

static ModuleArgs *module;
int fd[2];

struct list *list_root = NULL;

Display *dpy;                   /* which display are we talking to */
int ScreenWidth, ScreenHeight;
int screen;

long Vx, Vy;

long CurDesk = 1;   /* actual Desktop while being called */

/*
 *
 *  Procedure:
 *      main - start of module
 *
 */
int main(int argc, char **argv)
{
  char *display_name = NULL;

  module = ParseModuleArgs(argc,argv,0); /* no alias */
  if (module == NULL)
  {
    fprintf(stderr,"FvwmSaveDesk Version %s should only be executed by fvwm!\n",
            VERSION);
    exit(1);
  }

  /* Open the X display */
  if (!(dpy = XOpenDisplay(display_name)))
    {
      fprintf(stderr,"%s: can't open display %s", module->name,
	      XDisplayName(display_name));
      exit (1);
    }
  screen= DefaultScreen(dpy);
  ScreenHeight = DisplayHeight(dpy,screen);
  ScreenWidth = DisplayWidth(dpy,screen);

  /* We should exit if our fvwm pipes die */
  signal (SIGPIPE, DeadPipe);

  fd[0] = module->to_fvwm;
  fd[1] = module->from_fvwm;

  /* Create a list of all windows */
  /* Request a list of all windows,
   * wait for ConfigureWindow packets */
  SendInfo(fd,"Send_WindowList",0);

  /* tell fvwm we're running */
  SendFinishedStartupNotification(fd);

  Loop(fd);
  return 0;
}



/*
 *
 *  Procedure:
 *      Loop - wait for data to process
 *
 */
void Loop(int *fd)
{
    while(1) {
	FvwmPacket* packet = ReadFvwmPacket(fd[1]);
	if ( packet == NULL )
	    exit(0);
	else
	    process_message( packet->type, packet->body );
    }
}


/*
 *
 *  Procedure:
 *      Process message - examines packet types, and takes appropriate action
 *
 */
void process_message(unsigned long type,unsigned long *body)
{
  switch(type)
    {
    case M_CONFIGURE_WINDOW:
      if(!find_window(body[0]))
	add_window(body[0],body);
      break;
    case M_WINDOW_NAME:
      {
	struct list *l;
	if ((l = find_window(body[0])) != 0) {
	   l->name = (char *)safemalloc(strlen((char *)&body[3])+1);
	   strcpy(l->name,(char *)&body[3]);
	}
      }
      break;
    case M_NEW_PAGE:
      list_new_page(body);
      break;
    case M_NEW_DESK:
      CurDesk = (long)body[0];
      break;
    case M_END_WINDOWLIST:
      do_save();
      break;
    default:
      break;
    }
}



/*
 *
 *  Procedure:
 *      find_window - find a window in the current window list
 *
 */
struct list *find_window(unsigned long id)
{
  struct list *l;

  if(list_root == NULL)
    return NULL;

  for(l = list_root; l!= NULL; l= l->next)
    {
      if(l->id == id)
	return l;
    }
  return NULL;
}



/*
 *
 *  Procedure:
 *      add_window - add a new window in the current window list
 *
 */
void add_window(unsigned long new_win, unsigned long *body)
{
  struct list *t;
  struct ConfigWinPacket *cfgpacket = (void *)body;

  if(new_win == 0)
    return;

  t = (struct list *)safemalloc(sizeof(struct list));
  t->id = new_win;
  t->next = list_root;
  t->frame_height = cfgpacket->frame_height;
  t->frame_width = cfgpacket->frame_width;
  t->base_width = cfgpacket->hints_base_width;
  t->base_height = cfgpacket->hints_base_height;
  t->width_inc  = cfgpacket->hints_width_inc;
  t->height_inc  = cfgpacket->hints_height_inc;
  t->frame_x  = cfgpacket->frame_x;
  t->frame_y  = cfgpacket->frame_y;
  t->title_height  = cfgpacket->title_height;
  t->boundary_width  = cfgpacket->border_width;
  memcpy(&(t->flags), &(cfgpacket->flags), sizeof(t->flags));
  t->gravity = cfgpacket->hints_win_gravity;
  t->desk = cfgpacket->desk;
  t->name = 0;
  list_root = t;
}



/*
 *
 *  Procedure:
 *      list_new_page - capture new-page info
 *
 */
void list_new_page(unsigned long *body)
{
  Vx = (long)body[0];
  Vy = (long)body[1];
}
/*
 *
 *  Procedure:
 *      SIGPIPE handler - SIGPIPE means fvwm is dying
 *
 */
RETSIGTYPE DeadPipe(int nonsense)
{
  exit(0);
  SIGNAL_RETURN;
}


/*
 *
 *  Procedure:
 *      writes a command line argument to file "out"
 *      checks for qoutes and stuff
 *
 */
void write_string(FILE *out, char *line)
{
  int len,space = 0, qoute = 0,i;

  len = strlen(line);

  for(i=0;i<len;i++)
    {
      if(isspace((unsigned char)line[i]))
	space = 1;
      if(line[i]=='\"')
	qoute = 1;
    }
  if(space == 1)
    fprintf(out,"\"");
  if(qoute == 0)
    fprintf(out,"%s",line);
  else
    {
      for(i=0;i<len;i++)
	{
	  if(line[i]=='\"')
	    fprintf(out,"\\\"");
	  else
	    fprintf(out,"%c",line[i]);
	}
    }
  if(space == 1)
    fprintf(out,"\"");
  fprintf(out," ");

}

void do_save_command(FILE *out, struct list *t, int *curdesk,
				int emit_wait, int *isfirstline)
{
  char tname[200],loc[30];
  char **command_list;
  int dwidth,dheight,xtermline = 0;
  int x1,x2,y1,y2,i,command_count;
  long tVx, tVy;

  tname[0]=0;

  x1 = t->frame_x;
  x2 = ScreenWidth - x1 - t->frame_width - 2;
  if(x2 < 0)
    x2 = 0;
  y1 = t->frame_y;
  y2 = ScreenHeight - y1 -  t->frame_height - 2;
  if(y2 < 0)
    y2 = 0;
  dheight = t->frame_height - t->title_height - 2*t->boundary_width;
  dwidth = t->frame_width - 2*t->boundary_width;
  dwidth -= t->base_width ;
  dheight -= t->base_height ;
  dwidth /= t->width_inc;
  dheight /= t->height_inc;

  if (IS_STICKY_ACROSS_PAGES(t))
  {
    tVx = 0;
    tVy = 0;
  }
  else
  {
    tVx = Vx;
    tVy = Vy;
  }
  sprintf(tname,"%dx%d",dwidth,dheight);
  if ((t->gravity == EastGravity) ||
      (t->gravity == NorthEastGravity) ||
      (t->gravity == SouthEastGravity))
    sprintf(loc,"-%d",x2);
  else
    sprintf(loc,"+%d",x1+(int)tVx);
  strcat(tname, loc);

  if((t->gravity == SouthGravity)||
     (t->gravity == SouthEastGravity)||
     (t->gravity == SouthWestGravity))
    sprintf(loc,"-%d",y2);
  else
    sprintf(loc,"+%d",y1+(int)tVy);
  strcat(tname, loc);

  if ( XGetCommand( dpy, t->id, &command_list, &command_count ) )
  {
    if (*curdesk != t->desk)
    {
      fprintf( out, "%s\t\"I\" Desk 0 %ld\n", *isfirstline ? "" : "+",
	       t->desk);
      fflush ( out );
      if (*isfirstline) *isfirstline = 0;
      *curdesk = t->desk;
    }

    fprintf( out, "%s\t\t\"I\" Exec ",  *isfirstline ? "" : "+");
    if (*isfirstline) *isfirstline = 0;
    fflush ( out );
    for (i=0; i < command_count; i++)
    {
      if ( strncmp( "-geo", command_list[i], 4) == 0)
      {
	i++;
	continue;
      }
      if ( strncmp( "-ic", command_list[i], 3) == 0)
	continue;
      if ( strncmp( "-display", command_list[i], 8) == 0)
      {
	i++;
	continue;
      }
      write_string(out,command_list[i]);
      fflush ( out );
      if(strstr(command_list[i], "xterm"))
      {
	fprintf( out, "-geometry %s ", tname );
	if (IS_ICONIFIED(t))
	  fprintf(out, "-ic ");
	xtermline = 1;
	fflush ( out );
      }
    }
    if ( command_count > 0 )
    {
      if ( xtermline == 0 )
      {
	if (IS_ICONIFIED(t))
	  fprintf(out, "-ic ");
	fprintf( out, "-geometry %s &\n", tname);
      }
      else
      {
	fprintf( out, "&\n");
      }
    }
    if (emit_wait) {
      if (t->name)
	fprintf( out, "+\t\t\"I\" Wait %s\n", t->name);
      else fprintf( out, "+\t\t\"I\" Wait %s\n", command_list[0]);
      fflush( out );
    }
    XFreeStringList( command_list );
    xtermline = 0;
  }
}


/*
 *
 *  Procedure:
 *      checks to see if we are supposed to take some action now,
 *      finds time for next action to be performed.
 *
 */
void do_save(void)
{
  struct list *t;
  char fnbuf[200];
  FILE *out;
  int maxdesk = 0;
  int actdesk = -1;
  int curdesk;
  int isfirstline = 1;

  for (t = list_root; t != NULL; t = t->next)
     if (t->desk > maxdesk)
	maxdesk = t->desk;

  sprintf(fnbuf, "%s/.fvwm2desk", getenv( "HOME" ) );
  out = fopen( fnbuf, "w" );

  fprintf( out, "AddToFunc InitFunction");
  fflush ( out );

  /*
   * Generate all Desks except 'CurDesk'
   */
  for (curdesk = 0; curdesk <= maxdesk; curdesk++)
    {
	for (t = list_root; t != NULL; t = t->next)
	   {
	       if (t->desk != CurDesk && curdesk == t->desk)
		  do_save_command(out, t, &actdesk, 1, &isfirstline);
	   }
    }
  /*
   * Generate 'CurDesk'
   */
  for (t = list_root; t != NULL; t = t->next)
     {
	 if (t->desk == CurDesk)
	    do_save_command(out, t, &actdesk, 0, &isfirstline);
     }

  if (actdesk != CurDesk)
     fprintf( out, "+\t\"I\" Desk 0 %ld\n", CurDesk);

  fflush( out );
  fclose( out );
  exit(0);

}

