/*
 * Copyright (c) 1999 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/loadimg.h"
#include "../../include/parser.h"
#include "../../include/confdefs.h"

#define DEBUG_MYSTYLE

TermDef MyStyleTerms[] =
{
/* including MyStyles definitions here so we can process them correctly */
  MYSTYLE_TERMS,
  {0, NULL, 0, 0, 0}
};

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


/* just a nice error printing function */
void mystyle_error (char *format, char *name, char *text2);

/*****************************************************************************
 * 
 * This routine is responsible for reading and parsing the MyStyle settings 
 *
 ****************************************************************************/

MyStyleDefinition *
AddMyStyleDefinition (MyStyleDefinition ** tail)
{
  MyStyleDefinition *new_def = (MyStyleDefinition *) safemalloc (sizeof (MyStyleDefinition));
  /* let's initialize Base config with some nice values: */
  new_def->name = NULL;
  new_def->inherit = NULL;
  new_def->inherit_num = 0;
  new_def->font = NULL;
  new_def->fore_color = NULL;
  new_def->back_color = NULL;
  new_def->text_style = 0;
  new_def->max_colors = 256;
  new_def->backgradient_type = 0;
  new_def->backgradient_from = NULL;
  new_def->backgradient_to = NULL;

  new_def->back_pixmap_type = 0;
  new_def->back_pixmap = NULL;
  new_def->draw_text_background = 0;
  new_def->set_flags = 0;
  new_def->finished = 0;
  new_def->next = *tail;
  *tail = new_def;

  new_def->more_stuff = NULL;

  return new_def;
}

void
DestroyMyStyleDefinitions (MyStyleDefinition ** list)
{
  if (*list)
    {
      DestroyMyStyleDefinitions (&((*list)->next));
      if ((*list)->name)
	free ((*list)->name);
      if ((*list)->inherit)
	{
	  if ((*list)->inherit[0])
	    free ((*list)->inherit[0]);
	  free ((*list)->inherit);
	}
      if ((*list)->font)
	free ((*list)->font);
      if ((*list)->fore_color)
	free ((*list)->fore_color);
      if ((*list)->back_color)
	free ((*list)->back_color);
      if ((*list)->back_pixmap)
	free ((*list)->back_pixmap);
      if ((*list)->backgradient_from)
	free ((*list)->backgradient_from);
      if ((*list)->backgradient_to)
	free ((*list)->backgradient_to);

      DestroyFreeStorage (&((*list)->more_stuff));

      free (*list);
      *list = NULL;
    }
}

void
PrintMyStyleDefinitions (MyStyleDefinition * list)
{
#ifdef DEBUG_MYSTYLE
  if (list)
    {
      PrintMyStyleDefinitions (list->next);
      if (list->name)
	fprintf (stderr, "\nMyStyle \"%s\"", list->name);
      if (list->inherit)
	if (list->inherit[0])
	  fprintf (stderr, "\n\tInherit %s", list->inherit[0]);
      fprintf (stderr, "\n\tInheritCount %d", list->inherit_num);
      if (list->font)
	fprintf (stderr, "\n\tFont %s", list->font);
      if (list->fore_color)
	fprintf (stderr, "\n\tForeColor %s", list->fore_color);
      if (list->back_color)
	fprintf (stderr, "\n\tBackColor %s", list->back_color);
      fprintf (stderr, "\n\tTextStyle %d", list->text_style);
      fprintf (stderr, "\n\tMaxColors %d", list->max_colors);
      fprintf (stderr, "\n\tBackGradient %d\t%s\t%s", list->backgradient_type, list->backgradient_from, list->backgradient_to);
      if (list->back_pixmap)
	fprintf (stderr, "\n\tBackPixmap %d %s", list->back_pixmap_type, list->back_pixmap);
      else if (list->set_flags & F_BACKPIXMAP)
	fprintf (stderr, "\n\tBackPixmap %d", list->back_pixmap_type);
      fprintf (stderr, "\n\tDrawTextBackground \"%d\"", list->draw_text_background);
      if (list->finished)
	fprintf (stderr, "\n~MyStyle");
    }
#endif
}

MyStyleDefinition **
ProcessMyStyleOptions (FreeStorageElem * options, MyStyleDefinition ** tail)
{
  ConfigItem item;
  item.memory = NULL;

  for (; options; options = options->next)
    {
      if (options->term->id < MYSTYLE_ID_START || options->term->id > MYSTYLE_ID_END)
	continue;

      if (options->term == NULL)
	continue;
      if (options->term->type == TT_FLAG)
	{
	  switch (options->term->id)
	    {
	    case MYSTYLE_DONE_ID:
	      if (*tail != NULL)
		if ((*tail)->name != NULL)
		  {
		    (*tail)->finished = 1;
		    while (*tail)
		      tail = &((*tail)->next);	/* just in case */
		    break;
		  }
	      mystyle_error ("Unexpected MyStyle definition termination. Ignoring.", "Unknown", NULL);
	      DestroyMyStyleDefinitions (tail);
	      break;
	    default:
	      ReadConfigItem (&item, NULL);
	      return tail;
	    }
	  continue;
	}

      if (!ReadConfigItem (&item, options))
	continue;

      if (*tail == NULL)
	AddMyStyleDefinition (tail);
#define SET_SET_FLAG1(f)  SET_SET_FLAG((**tail),f)
      switch (options->term->id)
	{
	case MYSTYLE_START_ID:
	  if ((*tail)->name != NULL)
	    {
	      mystyle_error ("Previous MyStyle definition [%s] was not terminated correctly, and will be ignored.", item.data.string, (*tail)->name);
	      DestroyMyStyleDefinitions (tail);
	      AddMyStyleDefinition (tail);
	    }
	  (*tail)->name = item.data.string;
	  break;
	case MYSTYLE_INHERIT_ID:
	  AddStringToArray (&((*tail)->inherit_num), &((*tail)->inherit), item.data.string);
	  item.ok_to_free = 1;
	  break;
	case MYSTYLE_FONT_ID:
	  REPLACE_STRING (((*tail)->font), item.data.string);
	  SET_SET_FLAG1 (F_FONT);

	  break;
	case MYSTYLE_FORECOLOR_ID:
	  REPLACE_STRING (((*tail)->fore_color), item.data.string)
	    SET_SET_FLAG1 (F_FORECOLOR);
	  break;
	case MYSTYLE_BACKCOLOR_ID:
	  REPLACE_STRING (((*tail)->back_color), item.data.string)
	    SET_SET_FLAG1 (F_BACKCOLOR);
	  break;
	case MYSTYLE_TEXTSTYLE_ID:
	  (*tail)->text_style = item.data.integer;
	  SET_SET_FLAG1 (F_TEXTSTYLE);
	  break;
	case MYSTYLE_MAXCOLORS_ID:
	  (*tail)->max_colors = item.data.integer;
	  SET_SET_FLAG1 (F_MAXCOLORS);
	  break;
	case MYSTYLE_BACKGRADIENT_ID:
	  {
	    (*tail)->backgradient_type = item.data.integer;
	    if ((*tail)->backgradient_from)
	      free ((*tail)->backgradient_from);
	    if ((*tail)->backgradient_to)
	      free ((*tail)->backgradient_to);
	    if (options->argc > 1 && options->argv[1])
	      (*tail)->backgradient_from = mystrdup (options->argv[1]);
	    else
	      (*tail)->backgradient_from = NULL;

	    if (options->argc > 2 && options->argv[2])
	      (*tail)->backgradient_to = mystrdup (options->argv[2]);
	    else
	      (*tail)->backgradient_to = NULL;

	    SET_SET_FLAG1 (F_BACKGRADIENT);
	  }
	  break;
	case MYSTYLE_BACKPIXMAP_ID:
	  (*tail)->back_pixmap_type = item.data.integer;
	  if ((*tail)->back_pixmap)
	    free ((*tail)->back_pixmap);
	  if (options->argc > 1 && options->argv[1])
	    (*tail)->back_pixmap = mystrdup (options->argv[1]);
	  else
	    (*tail)->back_pixmap = NULL;
	  SET_SET_FLAG1 (F_BACKPIXMAP);
	  break;
	case MYSTYLE_DRAWTEXTBACKGROUND_ID:
	  (*tail)->draw_text_background = item.data.integer;
	  SET_SET_FLAG1 (F_DRAWTEXTBACKGROUND);
	  break;
	default:
	  item.ok_to_free = 1;
	  ReadConfigItem (&item, NULL);
	  return tail;
	}
    }
  ReadConfigItem (&item, NULL);
  return tail;
}

void mystyle_create_from_definition (MyStyleDefinition * def, const char *PixmapPath);

/*
 * this function process a linked list of MyStyle definitions
 * and create MyStyle for each valid definition
 * this operation is destructive in the way that all
 * data members of definitions that are used in MyStyle will be
 * set to NULL, so to prevent them being deallocated by destroy function,
 * and/or being used in other places
 * ATTENTION !!!!!
 * MyStyleDefinitions become unusable as the result, and get's destroyed
 * pointer to a list becomes NULL !
 */
void
ProcessMyStyleDefinitions (MyStyleDefinition ** list, const char *PixmapPath)
{
  MyStyleDefinition *pCurr;

  if (list)
    if (*list)
      {
	for (pCurr = *list; pCurr; pCurr = pCurr->next)
	  mystyle_create_from_definition (pCurr, PixmapPath);

	DestroyMyStyleDefinitions (list);
	mystyle_fix_styles ();
      }
}

void
mystyle_create_from_definition (MyStyleDefinition * def, const char *PixmapPath)
{
  int i;
  MyStyle *style;

  if (def == NULL)
    return;
  if (def->finished == 0 || def->name == NULL)
    return;

  if ((style = mystyle_find (def->name)) == NULL)
    {
      style = mystyle_new ();
      style->name = def->name;
      def->name = NULL;		/* so it wont get deallocated */
    }

  if (def->inherit)
    for (i = 0; i < def->inherit_num; ++i)
      {
	MyStyle *parent;
	if (def->inherit[i])
	  {
	    if ((parent = mystyle_find (def->inherit[i])) != NULL)
	      mystyle_merge_styles (parent, style, True, False);
	    else
	      mystyle_error ("unknown style to inherit: %s\n", style->name, def->inherit[i]);
	  }
      }
  if (def->font)
    {
      style->inherit_flags &= ~F_FONT;
      if (style->user_flags & F_FONT)
	unload_font (&style->font);

      if (load_font (def->font, &style->font) == True)
	style->user_flags |= F_FONT;
      else
	style->user_flags &= ~F_FONT;
    }

  if (def->fore_color)
    {
      style->inherit_flags &= ~F_FORECOLOR;
      style->colors.fore = GetColor (def->fore_color);
      style->user_flags |= F_FORECOLOR;
    }
  if (def->back_color)
    {
      style->inherit_flags &= ~F_BACKCOLOR;
      style->texture_type = 0;
      style->colors.back = GetColor (def->back_color);
      style->relief.fore = GetHilite (style->colors.back);
      style->relief.back = GetShadow (style->colors.back);
      style->user_flags |= F_BACKCOLOR;
    }
  if (def->set_flags & F_TEXTSTYLE)
    {
      style->text_style = def->text_style;
      style->inherit_flags &= F_TEXTSTYLE;
      style->user_flags |= ~F_TEXTSTYLE;
    }
#ifndef NO_TEXTURE
  if (def->set_flags & F_MAXCOLORS)
    {
      style->max_colors = def->max_colors;
      style->user_flags |= F_MAXCOLORS;
      style->inherit_flags &= ~F_MAXCOLORS;
    }
  if (def->set_flags & F_BACKGRADIENT)
    {
      style->inherit_flags &= ~F_BACKGRADIENT;
      if (def->backgradient_from && def->backgradient_to)
	{
	  int type = def->backgradient_type;
	  gradient_t gradient;
	  if ((type = mystyle_parse_old_gradient (type, def->backgradient_from, def->backgradient_to, &gradient)) >= 0)
	    {
	      if (style->user_flags & F_BACKGRADIENT)
		{
		  free (style->gradient.color);
		  free (style->gradient.offset);
		}
	      style->gradient = gradient;
	      style->texture_type = type;
	      style->user_flags |= F_BACKGRADIENT;
	    }
	  else
	    mystyle_error ("bad gradient.\n", style->name, NULL);
	}
      else
	mystyle_error ("incomplete gradient definition.\n", style->name, NULL);

    }
  if ((def->set_flags & F_BACKPIXMAP))
    {
      int colors = -1;
      if (style->set_flags & F_MAXCOLORS)
	colors = style->max_colors;

      if (def->back_pixmap_type == 129)
	{
	  style->texture_type = def->back_pixmap_type;
	  style->tint.pixel = 0;
	  if (def->back_pixmap)
	    if (strlen (def->back_pixmap))
	      style->tint.pixel = GetColor (def->back_pixmap);
	  if (style->tint.pixel == 0)	/* use no tinting by default */
	    style->tint.pixel = GetColor ("white");

	  XQueryColor (dpy, DefaultColormap (dpy, DefaultScreen(dpy)), &style->tint);
	  style->user_flags |= F_BACKPIXMAP;
	}
      else if (def->back_pixmap)
	{
	  char *path;
	  Pixmap pix, mask;
	  if ((path = findIconFile (def->back_pixmap, PixmapPath, R_OK)) != NULL &&
	      (pix = LoadImageWithMask (dpy, RootWindow (dpy, DefaultScreen(dpy)), colors, path, &mask)) != None)
	    {
	      Window r;
	      int d;
	      XGetGeometry (dpy, pix, &r, &d, &d, &style->back_icon.width, &style->back_icon.height, &d, &d);
	      if (style->user_flags & F_BACKPIXMAP)
		{
		  if (style->back_icon.mask != None)
		    UnloadMask (style->back_icon.mask);
		  UnloadImage (style->back_icon.pix);
		}
	      style->back_icon.pix = pix;
	      style->back_icon.mask = mask;
	      style->texture_type = def->back_pixmap_type;
	      style->user_flags |= F_BACKPIXMAP;
	    }
	  else
	    mystyle_error ("unable to load pixmap: '%s'\n", style->name, def->back_pixmap);

	  if (path != NULL)
	    free (path);
	}
      style->inherit_flags &= ~F_BACKPIXMAP;
    }
#endif
  if (def->set_flags & F_DRAWTEXTBACKGROUND)
    {
      style->user_flags |= F_DRAWTEXTBACKGROUND;
      style->inherit_flags &= ~F_DRAWTEXTBACKGROUND;
      if (def->draw_text_background == 0)
	style->flags &= ~F_DRAWTEXTBACKGROUND;
    }

  style->set_flags = style->inherit_flags | style->user_flags;
}
