/*
 * Copyright (c) 1998 Sasha Vasko <sashav@sprintmail.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 "../../configure.h"

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>

#include <stdlib.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>

#include "../../include/aftersteplib.h"
#include "../../include/afterstep.h"
#include "../../include/style.h"
#include "../../include/mystyle.h"
#include "../../include/parser.h"
#include "../../include/confdefs.h"
#include "../../include/background.h"

/*****************************************************************************
 * 
 * This routine is responsible for reading and parsing the asetroot config 
 * file
 *
 ****************************************************************************/

TermDef MyBackgroundTerms[] =
{
  {TF_NO_MYNAME_PREPENDING, "MyBackground", 12, TT_QUOTED_TEXT, BGR_MYBACKGROUND, NULL, NULL},
  {TF_NO_MYNAME_PREPENDING | TF_DONT_SPLIT | TF_DONT_REMOVE_COMMENTS | TF_INDEXED, "Use", 3, TT_QUOTED_TEXT, BGR_USE, NULL, NULL},
  {TF_NO_MYNAME_PREPENDING, "Cut", 3, TT_GEOMETRY, BGR_CUT, NULL, NULL},
  {TF_NO_MYNAME_PREPENDING | TF_DONT_REMOVE_COMMENTS, "Tint", 4, TT_COLOR, BGR_TINT, NULL, NULL},
  {TF_NO_MYNAME_PREPENDING, "Scale", 5, TT_GEOMETRY, BGR_SCALE, NULL, NULL},
  {TF_NO_MYNAME_PREPENDING, "Align", 5, TT_INTEGER, BGR_ALIGN, NULL, NULL},
  {TF_NO_MYNAME_PREPENDING | TF_INDEXED, "Pad", 3, TT_COLOR, BGR_PAD, NULL, NULL},
  {TF_NO_MYNAME_PREPENDING | TF_SYNTAX_TERMINATOR, "~MyBackground", 13, TT_FLAG, BGR_MYBACKGROUND_END, NULL, NULL},
  {0, NULL, 0, 0, 0}
};

SyntaxDef MyBackgroundSyntax =
{
  '\n',
  '\0',
  MyBackgroundTerms,
  0,				/* use default hash size */
  NULL
};


TermDef ASetRootTerms[] =
{
  {TF_INDEXED | TF_DONT_SPLIT, "DeskBack", 8, TT_QUOTED_TEXT, BGR_DESK_BACK, NULL, NULL},
/* including MyStyles definitions processing */
  INCLUDE_MYSTYLE,
  {TF_NO_MYNAME_PREPENDING, "MyBackground", 12, TT_QUOTED_TEXT, BGR_MYBACKGROUND, &MyBackgroundSyntax, NULL},
  {0, NULL, 0, 0, 0}
};

SyntaxDef ASetRootSyntax =
{
  '\n',
  '\0',
  ASetRootTerms,
  0,				/* use default hash size */
  NULL
};

/*****************  Create/Destroy MyBackgroundConfig *****************/
MyBackgroundConfig *
CreateMyBackgroundConfig ()
{
  MyBackgroundConfig *config = (MyBackgroundConfig *) safemalloc (sizeof (MyBackgroundConfig));

  config->name = NULL;
  config->flags = 0;
  config->data = NULL;
  InitMyGeometry (&(config->cut));
  config->tint = NULL;
  InitMyGeometry (&(config->scale));
  config->pad = NULL;
  config->next = NULL;

  return config;
}

void
DestroyMyBackgroundConfig (MyBackgroundConfig ** head)
{
  if (head)
    {
      MyBackgroundConfig *cur = *head, *next;
      while (cur)
	{
	  if (cur->name)
	    free (cur->name);
	  if (cur->data)
	    free (cur->data);
	  if (cur->tint)
	    free (cur->tint);
	  if (cur->pad)
	    free (cur->pad);
	  next = cur->next;
	  free (cur);
	  cur = next;
	}
      *head = NULL;
    }
}

/*****************  Create/Destroy DeskBackConfig     *****************/
DeskBackConfig *
CreateDeskBackConfig ()
{
  DeskBackConfig *config = (DeskBackConfig *) safemalloc (sizeof (DeskBackConfig));

  config->desk = -1;
  config->back_name = NULL;
  config->back = NULL;
  config->next = NULL;

  return config;
}

void
DestroyDeskBackConfig (DeskBackConfig ** head)
{
  if (head)
    {
      DeskBackConfig *cur = *head, *next;

      while (cur)
	{
	  if (cur->back_name)
	    free (cur->back_name);

	  next = cur->next;
	  free (cur);
	  cur = next;
	}
      *head = NULL;
    }
}

/*****************  Create/Destroy ASetRootConfig     *****************/

ASetRootConfig *
CreateASetRootConfig ()
{
  ASetRootConfig *config = (ASetRootConfig *) safemalloc (sizeof (ASetRootConfig));
  /* let's initialize ASetRoot config with some nice values: */
  config->my_backs = NULL;
  config->my_desks = NULL;
  config->style_defs = NULL;

  config->more_stuff = NULL;

  return config;
}

void
DestroyASetRootConfig (ASetRootConfig * config)
{
  if (config->my_desks)
    DestroyDeskBackConfig (&(config->my_desks));
  if (config->my_backs)
    DestroyMyBackgroundConfig (&(config->my_backs));
  DestroyMyStyleDefinitions (&(config->style_defs));
  DestroyFreeStorage (&(config->more_stuff));
  free (config);
}

/***********************************************************************/
/*      Actual Parsing                                                 */
/***********************************************************************/

MyBackgroundConfig *
ParseMyBackgroundOptions (FreeStorageElem * Storage, char *myname)
{
  MyBackgroundConfig *config = CreateMyBackgroundConfig ();
  FreeStorageElem *pCurr;
  ConfigItem item;

  item.memory = NULL;
  PrintFreeStorage (Storage);
  for (pCurr = Storage; pCurr; pCurr = pCurr->next)
    {
      if (pCurr->term == NULL)
	continue;

      if (pCurr->term->id == BGR_MYBACKGROUND_END)
	{
	  config->flags |= BGFLAG_COMPLETE;
	  continue;
	}
      if (!ReadConfigItem (&item, pCurr))
	{
	  if (pCurr->term->id == BGR_SCALE)
	    config->flags |= BGFLAG_SCALE;
	  continue;
	}

      switch (pCurr->term->id)
	{
	case BGR_MYBACKGROUND:
	  config->name = item.data.string;
	  break;
	case BGR_USE:
	  config->data = item.data.string;
	  config->flags &= ~(BGFLAG_FILE | BGFLAG_MYSTYLE);
	  if (item.index == 0)
	    config->flags |= BGFLAG_FILE;
	  else if (item.index == 1)
	    config->flags |= BGFLAG_MYSTYLE;
	  break;
	case BGR_CUT:
	  config->cut = item.data.geometry;
	  config->flags |= BGFLAG_CUT;
	  break;
	case BGR_TINT:
	  config->tint = item.data.string;
	  config->flags |= BGFLAG_TINT;
	  break;
	case BGR_SCALE:
	  config->scale = item.data.geometry;
	  config->flags |= BGFLAG_SCALE;
	  break;
	case BGR_ALIGN:
	  config->flags &= ~(BGFLAG_ALIGN_CENTER | BGFLAG_ALIGN_RIGHT | BGFLAG_ALIGN_BOTTOM);
	  config->flags |= BGFLAG_ALIGN;
	  if (item.data.integer & 0x1)
	    config->flags |= BGFLAG_ALIGN_RIGHT;
	  else if (item.data.integer & 0x2)
	    config->flags |= BGFLAG_ALIGN_BOTTOM;
	  item.ok_to_free = 1;
	  break;
	case BGR_PAD:
	  config->pad = item.data.string;
	  config->flags &= ~(BGFLAG_PAD_VERT | BGFLAG_PAD_HOR);
	  config->flags |= BGFLAG_PAD;
	  if (item.index & 0x1)
	    config->flags |= BGFLAG_PAD_HOR;
	  else if (item.index & 0x2)
	    config->flags |= BGFLAG_PAD_VERT;
	  break;
	default:
	  item.ok_to_free = 1;
	}
    }
  ReadConfigItem (&item, NULL);

  if (!config->name)
    {
      fprintf (stderr, "\n%s: Background Definition error: name is empty !", myname);
      config->flags |= BGFLAG_BAD;
    }
  else if (!config->data)
    {
      fprintf (stderr, "\n%s: Background Definition error: [%s] has no data defined !", myname, config->name);
      config->flags |= BGFLAG_BAD;
    }
  else if (!(config->flags & BGFLAG_COMPLETE))
    {
      fprintf (stderr, "\n%s: Background Definition error: [%s] not terminated properly !", myname, config->name);
      config->flags |= BGFLAG_BAD;
    }

  if (config->flags & BGFLAG_BAD)
    DestroyMyBackgroundConfig (&config);
  return config;
}

DeskBackConfig *
ParseDeskBackOptions (ConfigItem * item, char *myname)
{
  DeskBackConfig *config = CreateDeskBackConfig ();
  config->desk = item->index;
  config->back_name = item->data.string;
  item->ok_to_free = 0;

  if (config->desk < 0)
    {
      fprintf (stderr, "\n%s: Desk Background Definition error: bad desk number !", myname);
      DestroyDeskBackConfig (&config);
    }
  else if (config->back_name == NULL)
    {
      fprintf (stderr, "\n%s: Desk Background Definition error:  #%d has empty background name!", myname, config->desk);
      DestroyDeskBackConfig (&config);
    }
  return config;
}

void
FixDeskBacks (ASetRootConfig * config)
{
  DeskBackConfig *curr_desk;

  if (!config)
    return;
  for (curr_desk = config->my_desks; curr_desk; curr_desk = curr_desk->next)
    {
      MyBackgroundConfig *curr_back;
      for (curr_back = config->my_backs; curr_back; curr_back = curr_back->next)
	if (mystrcasecmp (curr_desk->back_name, curr_back->name) == 0)
	  {
	    curr_desk->back = curr_back;
	    break;
	  }
      if (curr_desk->back == NULL)
	{			/* need to create new back as plain image file,
				   using name from desk definition */
	  curr_desk->back = CreateMyBackgroundConfig ();
	  curr_desk->back->next = config->my_backs;
	  config->my_backs = curr_desk->back;
	  curr_desk->back->name = (char *) safemalloc (strlen (curr_desk->back_name) + 1);
	  strcpy (curr_desk->back->name, curr_desk->back_name);
	  curr_desk->back->data = (char *) safemalloc (strlen (curr_desk->back_name) + 1);
	  strcpy (curr_desk->back->data, curr_desk->back_name);
	  curr_desk->back->flags |= BGFLAG_FILE | BGFLAG_COMPLETE;
	}
    }
}

ASetRootConfig *
ParseASetRootOptions (const char *filename, char *myname)
{
  ConfigDef *ConfigReader = InitConfigReader (myname, &ASetRootSyntax, CDT_Filename, (void *) filename, NULL);
  ASetRootConfig *config = CreateASetRootConfig ();
  MyBackgroundConfig **backs_tail = &(config->my_backs);
  DeskBackConfig **desks_tail = &(config->my_desks);
  MyStyleDefinition **styles_tail = &(config->style_defs);
  FreeStorageElem *Storage = NULL, *pCurr;
  ConfigItem item;

  if (!ConfigReader)
    return config;

  item.memory = NULL;
  PrintConfigReader (ConfigReader);
  ParseConfig (ConfigReader, &Storage);
  PrintFreeStorage (Storage);

  /* getting rid of all the crap first */
  StorageCleanUp (&Storage, &(config->more_stuff), CF_DISABLED_OPTION);

  for (pCurr = Storage; pCurr; pCurr = pCurr->next)
    {
      if (pCurr->term == NULL)
	continue;

      if (pCurr->term->id == BGR_MYBACKGROUND)
	{
	  if ((*backs_tail = ParseMyBackgroundOptions (pCurr->sub, myname)) != NULL)
	    backs_tail = &((*backs_tail)->next);
	  continue;
	}

      if (!ReadConfigItem (&item, pCurr))
	continue;
      switch (pCurr->term->id)
	{
	case BGR_DESK_BACK:
	  if ((*desks_tail = ParseDeskBackOptions (&item, myname)) != NULL)
	    desks_tail = &((*desks_tail)->next);
	  break;
	case MYSTYLE_START_ID:
	  styles_tail = ProcessMyStyleOptions (pCurr->sub, styles_tail);
	  item.ok_to_free = 1;
	  break;
	default:
	  item.ok_to_free = 1;
	}
    }
  ReadConfigItem (&item, NULL);
  FixDeskBacks (config);

  DestroyConfig (ConfigReader);
  DestroyFreeStorage (&Storage);
  return config;
}
