/*
** Copyright (C) 2011-2020  <djcb@djcbsoftware.nl>
**
** 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 3, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**
*/

#include <string.h>
#include "mu-flags.hh"

using namespace Mu;

struct FlagInfo {
	MuFlags		 flag;
	char		 kar;
	const char	*name;
	MuFlagType	 flag_type;
};

static const FlagInfo FLAG_INFO[] = {

	/* NOTE: order of this is significant, due to optimizations
	 * below */

	{ MU_FLAG_DRAFT,      'D', "draft",     MU_FLAG_TYPE_MAILFILE },
	{ MU_FLAG_FLAGGED,    'F', "flagged",   MU_FLAG_TYPE_MAILFILE },
	{ MU_FLAG_PASSED,     'P', "passed",    MU_FLAG_TYPE_MAILFILE },
	{ MU_FLAG_REPLIED,    'R', "replied",   MU_FLAG_TYPE_MAILFILE },
	{ MU_FLAG_SEEN,       'S', "seen",      MU_FLAG_TYPE_MAILFILE },
	{ MU_FLAG_TRASHED,    'T', "trashed",   MU_FLAG_TYPE_MAILFILE },

	{ MU_FLAG_NEW,        'N', "new",       MU_FLAG_TYPE_MAILDIR  },

	{ MU_FLAG_SIGNED,     'z', "signed",    MU_FLAG_TYPE_CONTENT  },
	{ MU_FLAG_ENCRYPTED,  'x', "encrypted", MU_FLAG_TYPE_CONTENT  },
	{ MU_FLAG_HAS_ATTACH, 'a', "attach",    MU_FLAG_TYPE_CONTENT  },
	{ MU_FLAG_LIST,       'l', "list",      MU_FLAG_TYPE_CONTENT  },


	{ MU_FLAG_UNREAD,     'u', "unread",    MU_FLAG_TYPE_PSEUDO  }
};


MuFlagType
Mu::mu_flag_type (MuFlags flag)
{
	unsigned u;

	for (u = 0; u != G_N_ELEMENTS (FLAG_INFO); ++u)
		if (FLAG_INFO[u].flag == flag)
			return FLAG_INFO[u].flag_type;

	return MU_FLAG_TYPE_INVALID;
}


char
Mu::mu_flag_char (MuFlags flag)
{
	unsigned u;

	for (u = 0; u != G_N_ELEMENTS (FLAG_INFO); ++u)
		if (FLAG_INFO[u].flag == flag)
			return FLAG_INFO[u].kar;
	return 0;
}


MuFlags
Mu::mu_flag_char_from_name (const char *str)
{
	unsigned u;

	g_return_val_if_fail (str, MU_FLAG_INVALID);

	for (u = 0; u != G_N_ELEMENTS (FLAG_INFO); ++u)
		if (g_strcmp0(FLAG_INFO[u].name, str) == 0)
			return (MuFlags)FLAG_INFO[u].kar;

	return (MuFlags)0;
}


static MuFlags
mu_flag_from_char (char kar)
{
	unsigned u;

	for (u = 0; u != G_N_ELEMENTS (FLAG_INFO); ++u)
		if (FLAG_INFO[u].kar == kar)
			return FLAG_INFO[u].flag;

	return MU_FLAG_INVALID;
}


const char*
Mu::mu_flag_name (MuFlags flag)
{
	unsigned u;

	for (u = 0; u != G_N_ELEMENTS (FLAG_INFO); ++u)
		if (FLAG_INFO[u].flag == flag)
			return FLAG_INFO[u].name;

	return NULL;
}


const char*
Mu::mu_flags_to_str_s (MuFlags flags, MuFlagType types)
{
	unsigned u,v;
	static char str[sizeof(FLAG_INFO) + 1];

	for (u = 0, v = 0; u != G_N_ELEMENTS(FLAG_INFO); ++u)
		if (flags & FLAG_INFO[u].flag &&
		    types & FLAG_INFO[u].flag_type)
			str[v++] = FLAG_INFO[u].kar;
	str[v] = '\0';

	return str;
}


MuFlags
Mu::mu_flags_from_str (const char *str, MuFlagType types,
		   gboolean ignore_invalid)
{
	const char	*cur;
	MuFlags		 flag;

	g_return_val_if_fail (str, MU_FLAG_INVALID);

	for (cur = str, flag = MU_FLAG_NONE; *cur; ++cur) {

		MuFlags f;

		f = mu_flag_from_char (*cur);

		if (f == MU_FLAG_INVALID) {
			if (ignore_invalid)
				continue;
			return MU_FLAG_INVALID;
		}

		if (mu_flag_type (f) & types)
			flag |= f;
	}

	return flag;
}



char*
Mu::mu_flags_custom_from_str (const char *str)
{
	char *custom;
	const char* cur;
	unsigned u;

	g_return_val_if_fail (str, NULL);

	for (cur = str, u = 0, custom = NULL; *cur; ++cur) {

		MuFlags flag;
		flag = mu_flag_from_char (*cur);

		/* if it's a valid file flag, ignore it */
		if (flag != MU_FLAG_INVALID &&
		    mu_flag_type (flag) == MU_FLAG_TYPE_MAILFILE)
			continue;

		/* otherwise, add it to our custom string */
		if (!custom)
			custom = g_new0 (char, strlen(str) + 1);
		custom[u++] = *cur;
	}

	return custom;
}



void
Mu::mu_flags_foreach (MuFlagsForeachFunc func, gpointer user_data)
{
	unsigned u;

	g_return_if_fail (func);

	for (u = 0; u != G_N_ELEMENTS(FLAG_INFO); ++u)
		func (FLAG_INFO[u].flag, user_data);
}


MuFlags
Mu::mu_flags_from_str_delta (const char *str, MuFlags oldflags,
			 MuFlagType types)
{
	const char	*cur;
	MuFlags		 newflags;

	g_return_val_if_fail (str, MU_FLAG_INVALID);

	for (cur = str, newflags = oldflags; *cur; ++cur) {

		MuFlags f;
		if (*cur == '+' || *cur == '-') {
			f = mu_flag_from_char (cur[1]);
			if (f == 0)
				goto error;
			if (*cur == '+')
				newflags  |= f;
			else
				newflags  &= ~f;
			++cur;
			continue;
		}

		goto error;
	}

	return newflags;
error:
	g_warning ("invalid flag string");
	return MU_FLAG_INVALID;

}
