/*
 * Copyright (c) 1998 Sasha Vasko <sasha at aftercode.net>
 *
 * 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.
 *
 */
#define LOCAL_DEBUG
#include "../configure.h"
#include "../libAfterStep/asapp.h"
#include "../libAfterStep/afterstep.h"
#include "../libAfterStep/parser.h"
#include "../libAfterStep/background.h"
#include "../libAfterStep/screen.h"
#include "../libAfterImage/afterimage.h"

#include "afterconf.h"

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

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

SyntaxDef MyBackgroundSyntax = {
	'\n',
	'\0',
	MyBackgroundTerms,
	0,														/* use default hash size */
	' ',
	"",
	"\t",
	"Look MyBackground definition",
	"MyBackground",
	"details how background is to be rendered",
	NULL,
	0
};


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

SyntaxDef ASetRootSyntax = {
	'\n',
	'\0',
	ASetRootTerms,
	0,														/* use default hash size */
	' ',
	"",
	"\t",
	"asetroot configuration",
	"Look/DeskBack",
	"AfterStep module for rendering root backgrounds",
	NULL,
	0
};

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

	config->name = NULL;
	config->flags = 0;
	config->data = NULL;
	init_asgeometry (&(config->cut));
	config->tint = NULL;
	init_asgeometry (&(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;
	for (pCurr = Storage; pCurr; pCurr = pCurr->next) {
		if (pCurr->term == NULL)
			continue;

		LOCAL_DEBUG_OUT ("options(%p)->keyword(\"%s\")", pCurr,
										 pCurr->term->keyword);

		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_MIRROR:
			config->mirror = item.data.integer;
			config->flags |= BGFLAG_MIRROR;
			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;
			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;
			if (item.index & 0x2)
				config->flags |= BGFLAG_PAD_VERT;
			break;
		default:
			item.ok_to_free = 1;
		}
	}
	ReadConfigItem (&item, NULL);

	set_flags (config->flags, BGFLAG_BAD);
	if (config->name == NULL)
		show_error ("Background Definition error: name is empty !");
	else if (!(config->flags & BGFLAG_COMPLETE))
		show_error
				("Background Definition error: [%s] not terminated properly !",
				 config->name);
	else
		clear_flags (config->flags, BGFLAG_BAD);

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

DeskBackConfig *ParseDeskBackOptions (ConfigItem * item, char *myname)
{
	DeskBackConfig *config = CreateDeskBackConfig ();
	Bool failed = True;

	config->desk = item->index;
	config->back_name = item->data.string;
	item->ok_to_free = 0;

	if (config->desk < 0)
		show_error ("Desk Background Definition error: bad desk number !");
	else if (config->back_name == NULL)
		show_error
				("Desk Background Definition error:  #%d has empty background name!",
				 config->desk);
	else
		failed = False;

	if (failed)
		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)
{
	ConfigData cd;
	ConfigDef *ConfigReader;
	ASetRootConfig *config = CreateASetRootConfig ();
	MyBackgroundConfig **backs_tail = &(config->my_backs);
	DeskBackConfig **desks_tail = &(config->my_desks);
	FreeStorageElem *Storage = NULL, *pCurr;
	ConfigItem item;

	cd.filename = filename;
	ConfigReader =
			InitConfigReader (myname, &ASetRootSyntax, CDT_Filename, cd, NULL);

	if (!ConfigReader)
		return config;

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

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

	config->style_defs = free_storage2MyStyleDefinitionsList (Storage);

	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;
		default:
			item.ok_to_free = 1;
		}
	}
	ReadConfigItem (&item, NULL);
	FixDeskBacks (config);

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

void myback_parse (char *tline, FILE * fd, char **myname, int *mylook)
{
	FilePtrAndData fpd;
	ConfigDef *ConfigReader;
	MyBackgroundConfig *back_config = NULL;
	FreeStorageElem *Storage = NULL, *more_stuff = NULL;
	MyLook *look = (MyLook *) mylook;
	MyBackground *myback = NULL;
	ConfigData cd;

	if (look == NULL)
		look = get_screen_look (NULL);

	fpd.fp = fd;
	fpd.data = safemalloc (12 + 1 + strlen (tline) + 1 + 1);
	sprintf (fpd.data, "MyBackground %s\n", tline);
	LOCAL_DEBUG_OUT ("fd(%p)->tline(\"%s\")->fpd.data(\"%s\")", fd, tline,
									 fpd.data);
	cd.fileptranddata = &fpd;
	ConfigReader =
			InitConfigReader ((char *)myname, &MyBackgroundSyntax,
												CDT_FilePtrAndData, cd, NULL);
	free (fpd.data);

	if (!ConfigReader)
		return;

	PrintConfigReader (ConfigReader);
	ParseConfig (ConfigReader, &Storage);

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

	back_config = ParseMyBackgroundOptions (Storage, (char *)myname);

	DestroyConfig (ConfigReader);
	DestroyFreeStorage (&Storage);

	if (back_config == NULL)
		return;

	/* now we have to turn MyBackgroundConfig into MyBackground structure : */
	myback = create_myback (back_config->name);

	if (get_flags (back_config->flags, BGFLAG_FILE))
		myback->type = MB_BackImage;
	else if (get_flags (back_config->flags, BGFLAG_MYSTYLE))
		myback->type = MB_BackMyStyle;
	else
		myback->type = MB_BackCmd;

	myback->data = back_config->data;
	back_config->data = NULL;
	if (get_flags (back_config->flags, BGFLAG_CUT))
		myback->cut = back_config->cut;
	if (get_flags (back_config->flags, BGFLAG_SCALE)) {
		myback->scale = back_config->scale;
		if (!get_flags (back_config->scale.flags, WidthValue))
			myback->scale.width = get_screen_width (NULL);
		if (!get_flags (back_config->scale.flags, HeightValue))
			myback->scale.height = get_screen_height (NULL);
		set_flags (myback->scale.flags, WidthValue | HeightValue);
	}

	myback->tint = TINT_LEAVE_SAME;
	if (get_flags (back_config->flags, BGFLAG_TINT) && back_config->tint)
		parse_argb_color (back_config->tint, &(myback->tint));
	myback->pad_color = ARGB32_Black;
	if (get_flags (back_config->flags, BGFLAG_PAD) && back_config->pad)
		parse_argb_color (back_config->pad, &(myback->pad_color));

	myback->align_flags = NO_ALIGN;
	if (get_flags (back_config->flags, BGFLAG_PAD_HOR)) {
		if (get_flags (back_config->flags, BGFLAG_ALIGN_RIGHT))
			myback->align_flags = ALIGN_RIGHT;
		else if (get_flags (back_config->flags, BGFLAG_ALIGN_CENTER))
			myback->align_flags = ALIGN_HCENTER;
		else
			myback->align_flags = ALIGN_LEFT;
	}
	myback->mirror = 0;
	if (get_flags (back_config->flags, BGFLAG_MIRROR)) {
			myback->mirror = back_config->mirror;
	}
	if (get_flags (back_config->flags, BGFLAG_PAD_VERT)) {
		if (get_flags (back_config->flags, BGFLAG_ALIGN_BOTTOM))
			myback->align_flags |= ALIGN_BOTTOM;
		else if (get_flags (back_config->flags, BGFLAG_ALIGN_CENTER))
			myback->align_flags |= ALIGN_VCENTER;
		else
			myback->align_flags = ALIGN_TOP;
	}
	LOCAL_DEBUG_OUT ("myback added: name(\"%s\")->type(%d)->data(\"%s\")",
									 myback->name, myback->type, myback->data);
	add_myback (look, myback);

	/* final cleanup : */
	DestroyMyBackgroundConfig (&back_config);
}
