#ifndef XCLASSES_CONFIG_CODE
#define XCLASSES_CONFIG_CODE

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: config.cxx,v 1.7 2003/02/16 18:22:21 schmitzj Exp $)
 * 
 * Copyright (C) 1998 Jürgen Schmitz
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 *-----------------------------------------------------------------------
 */

#include <Xclasses/config.h>
#include "include/config_p.h"
#include "../include/defaults.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "types.h"

config::config(char *nme)
{
	while (!(p=new pconfig))
	{
		outOfMemory("config");
	}
	p->o=this;

	p->name=strdup(nme);
}
config::~config()
{
	free(p->name);
	if (p) delete p;
}
pconfig::pconfig() : extralines()
{
	dsp=NULL;
	leaveunknown=0;
}
pconfig::~pconfig()
{
	struct node *n1;

	while (n1=extralines.RemHead())
	{
		free(n1->text);
		delete n1;
	}
}
void config::LeaveUnknownLines(void)
{
	p->leaveunknown=1;
}
void config::SetItems(configopts *c)
{
	p->opts=c;
}
/*
 *  Optionen:
 *   -MC - int Anzahl Felder
 *   -MV - Wert
 *   -O  - alter Wert, wird nicht mehr geschrieben
 *
 *  S-MC:name (int*)
 *  S100-MV:name (char*)
 *  S100-MV:name (char*)
 */
char pconfig::Opts(char *p)
{
	int opton=0;
	while (*p)
	{
		if (*p==':')
			return 0;
		if (*p=='-') // Optionen je mit "-"
		{
			opton=1;
		}
		else if (opton)
		{
			if (toupper(*p)=='M')
			{
				return toupper(*(p+1));
			}
			else if (toupper(*p)=='O')
			{
				return 'O';
			}
			else if (*p!=' ')
			{
				opton=0;
			}
		}
		p++;
	}
	return 0;
}
char *pconfig::Command(char *p)
{
	char *n=p;
	while (*n)
	{
		if (*n==':') return n+1;
		n++;
	}
	return p;
}
char *pconfig::T2S(char *p,char *d)
{ // Wert in File-Wort
	static char str[8000];
	
	str[0]=0;
	switch (tolower(*p))
	{
		case 's': // String
			sprintf(str,"%s=%s\n",Command(p),(char*)d);
			break;
		case 't': // String
			if (d && strlen(d))
			{
				sprintf(str,"%s=%s\n",Command(p),(char*)d);
			}
			break;
		case 'i': // int
			sprintf(str,"%s=%d\n",Command(p),*(int*)d);
			break;
		case 'b': // bool "true"/"false"
			sprintf(str,"%s=",Command(p));
			if (*(int*)d) strcat(str,"true\n"); else strcat(str,"false\n");
			break;
		case 'w': // switch - da=true
			if (*(int*)d) sprintf(str,"%s=\n",Command(p));
			break;
		case 'e': // enum
			{
				char *com=p+1,*c=NULL;
				int l=0;
				for(int i=0;i<=*(int*)d && *com && *com!=':';i++)
				{
					c=com; l=0;
					while (*com && *com!=':' && *com!=',')
					{
						com++;
						l++;
					}
					if (*com==',') com++;
				}
				if (l>0)
				{
					char buf[l+1];
					strncpy(buf,c,l);
					buf[l]=0;
					sprintf(str,"%s=%s\n",Command(p),buf);
				}
			}
			break;
	}
	return str;
}
int pconfig::S2T(char *com,char *p)
{ // File-Wort in Wert
	configopts *n=opts;
	char *nn;
	int l,*cc=NULL,ic=0;
	char m;
	int r=0;

	while (n->name)
	{
		if (!strcmp(Command(n->name),com))
		{
			r=1;
			m=Opts(n->name);
			if (m=='C')
			{
				cc=(int*)n->data;
				ic=0;
			}
			else if (m=='V' && ic<*cc)
			{
				ic++;
			}
			else
			{
				if (m=='V') (*cc)++;
				nn=n->data;
				switch (tolower(n->name[0]))
				{
					case 's': // String
					case 't':
						if (sscanf(&n->name[1],"%d",&l))
						{
							strncpy(nn,p,l);
							nn[l-1]=0;
						}
						else
						{
							strcpy(nn,p);
						}
						break;
					case 'i': // int
						sscanf(p,"%d",&l);
						*(int*)nn=l;
						break;
					case 'b': // bool "true"/"false"
						if (!strcasecmp(p,"true")) *(int*)nn=1; else *(int*)nn=0;
						break;
					case 'w': // switch - da=true
						*(int*)nn=1;
						break;
					case 'e': // enum
						{
							char *com=n->name+1,*c=NULL;
							int l,i=0;
							*(int*)nn=0; // default
							while(*com && *com!=':')
							{
								c=com; l=0;
								while (*com && *com!=':' && *com!=',' && *com!='-')
								{
									com++;
									l++;
								}
								if (*com==',') com++;
								if (l>0)
								{
									char buf[l+1];
									strncpy(buf,c,l);
									buf[l]=0;
									if (!strcasecmp(buf,p))
									{
										*(int*)nn=i;
										break;
									}
									i++;
								}
							}
						}
						break;
				}
				return r;
			}
		}
		n++;
	}
	return r;
}
void pconfig::printOpt(configopts *n,FILE *datei)
{
	char m=Opts(n->name);
	static int ic=0;

	if (m=='C')
	{
		int *cc;
		cc=((int*)n->data);
		ic=*cc;
	}
	else
	{
		if (m!='O' || ic>0)
			fprintf(datei,"%s",T2S(n->name,n->data));
		if (m=='V') ic--;
	}
}
int config::Write(void)
{
	char *home;
	int r=1;
	char *c=p->name;
	int fullpath=0;
	int homesub=0;

	if (*c=='~')
	{
		homesub=1;
		c+=2;
	}
	else
	{
		while (*c && !fullpath)
		{
			if (*c=='/') fullpath=1;
			c++;
		}
		c=p->name;
	}
	if (fullpath || (home=getenv("HOME")))
	{
		char fname[400];
		FILE *datei;

		if (fullpath)
		{
			strcpy(fname,p->name);
		}
		else
		{		
			strcpy(fname,home);
			if (fname[strlen(fname)-1]!='/') strcat(fname,"/");
			if (!homesub) strcat(fname,".");
			strcat(fname,c);
			if (p->dsp)
			{
				strcat(fname,"_");
				strcat(fname,p->dsp);
			}
		}

		if (datei=fopen(fname,"w"))
		{
			configopts *n=p->opts;
			if (p->leaveunknown)
			{
				struct node *n1=p->extralines.head;

				while (n1->next)
				{
					fprintf(datei,"%s\n",n1->text);
					n1=n1->next;
				}
			}
			else
				fprintf(datei,"## This is a generated file, Do not edit!\n");
			while (n->name)
			{
				p->printOpt(n,datei);
				n++;
			}
			fclose(datei);
			r=0;
		}
	}
	return r;
}
int config::RootWrite(void)
{
	char *home;
	int r=1;
	if (getuid()==ROOTID)
	{
		home=GLOBAL_CONFIGS;
		char fname[200];
		FILE *datei;

		strcpy(fname,home);
		if (fname[strlen(fname)-1]!='/') strcat(fname,"/");
		strcat(fname,p->name);

		if (datei=fopen(fname,"w"))
		{
			configopts *n=p->opts;
			fprintf(datei,"## This is a generated file, Do not edit!\n");
			while (n->name)
			{
				p->printOpt(n,datei);
				n++;
			}
			fclose(datei);
			r=0;
		}
	}
	return r;
}
int config::Read(void)
{
	int r=1;
	char *home;
	char *c=p->name;
	int fullpath=0;
	int homesub=0;

	if (*c=='~')
	{
		homesub=1;
		c+=2;
	}
	else
	{
		while (*c && !fullpath)
		{
			if (*c=='/') fullpath=1;
			c++;
		}
		c=p->name;
	}

	configopts *n=p->opts;
	while (n->name)
	{
		if (tolower(n->name[0])=='w') *(int*)n->data=0;
		if (p->Opts(n->name)=='C') *((int*)n->data)=0;
		n++;
	}
	if (!(home=getenv("HOME"))) home="/";

	{
		char fname[800],buf[800],com[100],pname[100]="\0";
		int l,i=7,m=9;
		FILE *datei;

		if (fullpath)
		{
			strcpy(fname,p->name);
			m=7;
			i=6;
		}
		else if (!strcmp(p->name,LOAD_ALL_XCLASSES_CONFIG))
		{
			if (prgname)
			{
				i=0;
				m=6;
				strcpy(pname,"Xclasses_");
				strcat(pname,prgname);
			}
			else
			{
				i=5;
				m=6;
			}
			p->name=pname;
		}

		while (i<m && r)
		{
			switch (i)
			{
				case 0: // home, prgname+host
					strcpy(fname,home);
					if (fname[strlen(fname)-1]!='/') strcat(fname,"/");
					strcat(fname,".");
					strcat(fname,p->name);
					if (p->dsp)
					{
						strcat(fname,"_");
						strcat(fname,p->dsp);
					}
					break;
				case 1: // home, prgname
					strcpy(fname,home);
					if (fname[strlen(fname)-1]!='/') strcat(fname,"/");
					strcat(fname,".");
					strcat(fname,p->name);
					break;
				case 2: // home, Xclasses+host
					strcpy(fname,home);
					if (fname[strlen(fname)-1]!='/') strcat(fname,"/");
					strcat(fname,".Xclasses");
					if (p->dsp)
					{
						strcat(fname,"_");
						strcat(fname,p->dsp);
					}
					break;
				case 3: // home, Xclasses
					strcpy(fname,home);
					if (fname[strlen(fname)-1]!='/') strcat(fname,"/");
					strcat(fname,".Xclasses");
					break;
				case 4: // global, prgname
					strcpy(fname,GLOBAL_CONFIGS);
					strcat(fname,p->name);
					break;
				case 5: // global, Xclasses
					strcpy(fname,GLOBAL_CONFIGS);
					strcat(fname,"Xclasses");
					break;
				case 6: // absolut
					strcpy(fname,p->name);
					break;
				case 7: // filename, home
					strcpy(fname,home);
					if (fname[strlen(fname)-1]!='/') strcat(fname,"/");
					if (!homesub) strcat(fname,".");
					strcat(fname,c);
					break;
				case 8: // filename, global
					strcpy(fname,GLOBAL_CONFIGS);
					strcat(fname,p->name);
					break;
			}
			if (datei=fopen(fname,"r"))
			{
				struct node *n1;

				while (n1=p->extralines.RemHead())
				{
					free(n1->text);
					delete n1;
				}

				char *c;
				while (!feof(datei) && r)
				{
					if (fgets(buf,sizeof(buf),datei)>0)
					{
						if (buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]=0;
						c=buf; l=0;
						while (*c && *c!='=')
						{
							l++;
							c++;
						}
						strncpy(com,buf,l);
						com[l]=0;
						if (*c) c++;
						strcpy(fname,c);
						c=com;
						while (*c && *c!=' '&& *c!='\t') c++;
						*c=0;
						if (!p->S2T(com,fname) && p->leaveunknown)
						{
							n1=new node;
							n1->text=strdup(buf);
							p->extralines.AddTail(n1);
						}
					}
					else
					{
						r=0;
					}
				}
				fclose(datei);
				r=0;
			}
			i++;
		}
		if (m==6)
		{
			p->name=strdup(LOAD_ALL_XCLASSES_CONFIG);
		}
	}
	return r;
}
void config::SetDisplayConfig(Xdisplay *d)
{
	if (d->display()) p->dsp=d->DisplayName();
}

#endif
