
/*
 * Osmo - a handy personal organizer
 *
 * Copyright (C) 2007 Tomasz Maka <pasp@users.sourceforge.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "utils.h"
#include "utils_gui.h"
#include "utils_time.h"
#include "utils_date.h"
#include "i18n.h"
#include "options_prefs.h"

/*------------------------------------------------------------------------------*/

gchar *
utl_text_with_tags_to_html (gchar *input_text)
{
const gchar *html_tags[] = {
    "mark_color",   "<span style=\"background-color:#FFFF00;\">",
    "bold",         "<span style=\"font-weight: bold;\">",
    "italic",       "<span style=\"font-style: italic;\">",
    "underline",    "<span style=\"text-decoration:underline;\">",
    "strike",       "<span style=\"text-decoration:line-through;\">"
};
guint n_pairs = G_N_ELEMENTS (html_tags) / 2;
gchar *output = g_strdup ("");
gunichar TAG_CHAR = TAG;      /* Unicode chars in the Private Use Area. */
gchar tag_char_utf8[7] = {0};
gchar **tokens, *o_links_1_text, *o_links_2_text;
gint count, i;

    g_unichar_to_utf8 (TAG_CHAR, tag_char_utf8);

    tokens = g_strsplit (input_text, tag_char_utf8, 0);

    for (count = 0; tokens[count]; count++)
    {
        if (count % 2 == 0) {     /* normal text */
            output = utl_strconcat (output, tokens[count], NULL);
        } else {
            if (tokens[count][0] != '/') {   /* opening tag */
                for (i=0; i < n_pairs; i++) {
                    if (!strcmp(html_tags[i*2+0], tokens[count])) {
                        output = utl_strconcat (output, html_tags[i*2+1], NULL);
                        break;
                    }
                }
            } else {
                /* closing tag */
                output = utl_strconcat (output, "</span>", NULL);
            }
        }
    }

    g_strfreev (tokens);

    o_links_1_text = utl_text_replace (output, REGEX_URL, "<a href=\"\\0\">\\0</a>");
    g_free (output);

    o_links_2_text = utl_text_replace (o_links_1_text, REGEX_EMAIL, "<a href=\"mailto:\\0\">\\0</a>");
    g_free (o_links_1_text);

    return o_links_2_text;
}

/*------------------------------------------------------------------------------*/

gchar *     
utl_inline_image_to_html (guint8 *inline_data)
{
GdkPixbuf *image;
gchar *output = g_strdup ("");
gchar *img;
gchar tmpbuf[BUFFER_SIZE];

    image = gdk_pixbuf_new_from_inline (-1, inline_data, FALSE, NULL);

    g_snprintf (tmpbuf, BUFFER_SIZE, "width=\"%d\" height=\"%d\">", 
                gdk_pixbuf_get_width (image), gdk_pixbuf_get_height (image));

    output = utl_strconcat (output, "<img src=\"data:image/png;base64,", NULL);
    img = utl_inline_to_png_base64 (inline_data);
    output = utl_strconcat (output, img, "\" ", tmpbuf, NULL);
    g_free (img);

    g_object_unref (image);

    return output;
}

/*------------------------------------------------------------------------------*/

gchar *     
utl_inline_to_png_base64 (guint8 *inline_data)
{
GdkPixbuf *image;
gchar *buffer, *output;
gsize bufferSize;
GError *error = 0;

    image = gdk_pixbuf_new_from_inline (-1, inline_data, FALSE, NULL);

    if (!gdk_pixbuf_save_to_buffer(image, &buffer, &bufferSize, "png", &error, NULL)) {
        g_error_free(error);
        return NULL;
    }

    g_object_unref (image);

    output = g_base64_encode ((const guchar *)buffer, bufferSize);
    g_free (buffer);

    return output;
}

/*------------------------------------------------------------------------------*/

gchar *
utl_text_replace (const gchar *text, const gchar *regex, const gchar *replacement)
{
    GRegex *reg = g_regex_new (regex, G_REGEX_OPTIMIZE, 0, NULL);
    gchar *buffer = g_regex_replace (reg, text, -1, 0, replacement, 0, NULL);
    g_regex_unref (reg);

    return buffer;
}

gboolean
utl_text_match (const gchar *text, const gchar *regex) {
    GRegex *reg = g_regex_new (regex, G_REGEX_OPTIMIZE, 0, NULL);
    gboolean matched = g_regex_match (reg, text, 0, NULL);
    g_regex_unref (reg);

    return matched;
}

/*------------------------------------------------------------------------------*/

#ifdef HAVE_LIBWEBKIT

gboolean
utl_webkit_console_msg (WebKitWebView *web_view,
						gchar         *message,
						gint           line,
						gchar         *source_id,
						gpointer       user_data) {
	return TRUE; /* stop other handlers */
}

void
utl_webkit_on_menu (WebKitWebView *view, GtkMenu *menu) {
    /* don't show popup menus */
    gtk_widget_destroy(GTK_WIDGET(menu));
}


gboolean
utl_webkit_link_clicked (WebKitWebView *view, WebKitWebFrame *frame,
                         WebKitNetworkRequest *request, 
                         WebKitWebNavigationAction *navigation_action,
                         WebKitWebPolicyDecision *policy_decision) {

const gchar *uri;
WebKitWebNavigationReason reason;

    g_return_val_if_fail (WEBKIT_IS_WEB_VIEW (view), FALSE);
    g_return_val_if_fail (WEBKIT_IS_NETWORK_REQUEST (request), FALSE);

    reason = webkit_web_navigation_action_get_reason (navigation_action);

    if (reason != WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED)
        return FALSE;

    uri = webkit_network_request_get_uri (request);
    webkit_web_policy_decision_ignore (policy_decision);

    utl_run_helper ((gchar *)uri, utl_get_link_type ((gchar *)uri));

    return FALSE;
}

#endif  /* HAVE_LIBWEBKIT */

/*------------------------------------------------------------------------------*/

gchar *
utl_text_to_html_page (const gchar *text, const gchar *font_family, 
                       const gchar *background_color, const gchar *text_color,
                       const gchar *header_html, const gchar *top_html, const gchar *bottom_html)
{
gunichar TAG_CHAR = TAG;      /* Unicode chars in the Private Use Area. */
gchar *output = g_strdup ("");
gchar tag_char_utf8[7] = {0};
gchar **tokens, *itext, *o_links_1_text, *o_links_2_text;
gint count, i;
const gchar *html_tags[] = {
    "mark_color",   "<span style=\"background-color:#FFFF00;\">",
    "bold",         "<span style=\"font-weight: bold;\">",
    "italic",       "<span style=\"font-style: italic;\">",
    "underline",    "<span style=\"text-decoration:underline;\">",
    "strike",       "<span style=\"text-decoration:line-through;\">"
};
guint n_pairs = G_N_ELEMENTS (html_tags) / 2;

    g_unichar_to_utf8 (TAG_CHAR, tag_char_utf8);

    itext = utl_text_to_html (text, FALSE, TRUE);

    output = utl_strconcat (output, "<html><head>", NULL);
    output = utl_strconcat (output, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />", NULL);
    output = utl_strconcat (output, "<style type=\"text/css\">", NULL);
    output = utl_strconcat (output, "body {", NULL);

    if (background_color != NULL) {
        output = utl_strconcat (output, "background-color: ", background_color, "; ", NULL);
    }
    if (text_color != NULL) {
        output = utl_strconcat (output, "text-color: ", text_color, "; ", NULL);
    }
    if (font_family != NULL) {
        output = utl_strconcat (output, "font-family: ", font_family, "; ", NULL);
    }

    output = utl_strconcat (output, "line-height: 145\%; ", NULL);
    output = utl_strconcat (output, "} </style>", NULL);

    if (header_html != NULL) {
        output = utl_strconcat (output, header_html, NULL);
    }
    output = utl_strconcat (output, "</head><body>", NULL);

    if (top_html != NULL) {
        output = utl_strconcat (output, top_html, NULL);
    }

    output = utl_strconcat (output, "<span style=\"white-space: pre-wrap;\">", NULL);

    tokens = g_strsplit (itext, tag_char_utf8, 0);
    g_free (itext);

    for (count = 0; tokens[count]; count++)
    {
        if (count % 2 == 0) {     /* normal text */
            output = utl_strconcat (output, tokens[count], NULL);
        } else {
            if (tokens[count][0] != '/') {   /* opening tag */
                for (i=0; i < n_pairs; i++) {
                    if (!strcmp(html_tags[i*2+0], tokens[count])) {
                        output = utl_strconcat (output, html_tags[i*2+1], NULL);
                        break;
                    }
                }
            } else {
                /* closing tag */
                output = utl_strconcat (output, "</span>", NULL);
            }
        }
    }

    g_strfreev (tokens);

    output = utl_strconcat (output, "</span>", NULL);
                          
    if (bottom_html != NULL) {
        output = utl_strconcat (output, bottom_html, NULL);
    }
                          
    output = utl_strconcat (output, "</body></html>", NULL);

    o_links_1_text = utl_text_replace (output, REGEX_URL, "<a href=\"\\0\">\\0</a>");
    g_free (output);

    o_links_2_text = utl_text_replace (o_links_1_text, REGEX_EMAIL, "<a href=\"mailto:\\0\">\\0</a>");
    g_free (o_links_1_text);

    return o_links_2_text;
}

/*------------------------------------------------------------------------------*/

gchar *
utl_text_to_html (const gchar *text, 
				  gboolean ignoreBR,     /* ignore line breaks */
				  gboolean ignoreSP      /* ignore spaces */)
{

const gchar *pairs[] = {
    "&",    "&amp;",
    "\"",   "&quot;",
    "<",    "&lt;",
    ">",    "&gt;"
};

GRegex *reg;
gint i = 0;
gchar *buffer = NULL, *temp = NULL;
guint n_pairs = G_N_ELEMENTS (pairs) / 2;

    temp = g_strdup(text);

    for (i=0; i < n_pairs; i++) {
        reg = g_regex_new (pairs[i*2+0], 0, 0, NULL);
        buffer = g_regex_replace_literal (reg, temp, -1, 0, pairs[i*2+1], 0, NULL);
        g_free (temp);
        temp = buffer;
        g_regex_unref (reg);
    }

	if (ignoreBR == FALSE) {
        reg = g_regex_new ("\\n", 0, 0, NULL);
        buffer = g_regex_replace_literal (reg, temp, -1, 0, "<br />", 0, NULL);
        g_free (temp);
        temp = buffer;
        g_regex_unref (reg);
	}

	if (ignoreSP == FALSE) {
        reg = g_regex_new (" ", 0, 0, NULL);
        buffer = g_regex_replace_literal (reg, temp, -1, 0, "%20", 0, NULL);
        g_free (temp);
        temp = buffer;
        g_regex_unref (reg);
	}

    return temp;
}

/*------------------------------------------------------------------------------*/

gchar *
utl_get_day_name (guint day, gboolean short_name)
{
    static gchar buffer[BUFFER_SIZE];
    GDate *tmpdate = NULL;

    g_return_val_if_fail (day > 0 && day <= 31, buffer);

    tmpdate = g_date_new_dmy (day, 1, 2007);
    g_return_val_if_fail (tmpdate != NULL, buffer);

    g_date_strftime (buffer, BUFFER_SIZE, short_name ? "%a" : "%A", tmpdate);
    g_date_free (tmpdate);

    return buffer;
}

/*------------------------------------------------------------------------------*/

gchar *
utl_get_julian_day_name (guint32 julian)
{
    static gchar buffer[BUFFER_SIZE];
    GDate *tmpdate = NULL;

    buffer[0] = '\0';
    g_return_val_if_fail (g_date_valid_julian (julian) == TRUE, buffer);

    tmpdate = g_date_new_julian (julian);
    g_return_val_if_fail (tmpdate != NULL, buffer);

    g_date_strftime (buffer, BUFFER_SIZE, "%A", tmpdate);
    g_date_free (tmpdate);

    return buffer;
}

/*------------------------------------------------------------------------------*/

gchar *
utl_get_date_name (const GDate *date)
{
    static gchar buffer[BUFFER_SIZE];

    g_date_strftime (buffer, BUFFER_SIZE, "%e %B %Y", date);     /* e.g. 1 August 1999 */
    return buffer;
}

/*------------------------------------------------------------------------------*/

gchar *
utl_get_date_name_format (const GDate *date, gchar *fmt)
{
    static gchar buffer[BUFFER_SIZE];

    g_date_strftime (buffer, BUFFER_SIZE, fmt, date);
    return buffer;
}

/*------------------------------------------------------------------------------*/

guint
utl_get_weekend_days_in_month (const GDate *date)
{
    GDate *tmpdate = NULL;
    guint i, day, days, weekend_days;

    tmpdate = g_date_new_dmy (1, g_date_get_month (date), g_date_get_year (date));
    g_return_val_if_fail (tmpdate != NULL, 0);

    days = utl_date_get_days_in_month (tmpdate);
    weekend_days = 0;

    for (i = 1; i <= days; i++) {
        g_date_set_day (tmpdate, i);
        day = g_date_get_weekday (tmpdate);
        if (day == G_DATE_SATURDAY || day == G_DATE_SUNDAY) {
            weekend_days++;
        }
    }

    g_date_free (tmpdate);
    return weekend_days;
}

/*------------------------------------------------------------------------------*/

guint
utl_get_weekend_days_in_month_my (guint month, guint year)
{
    GDate *tmpdate = NULL;
    guint i, day, days, weekend_days;

    g_return_val_if_fail (g_date_valid_dmy (1, month, year) == TRUE, 0);

    tmpdate = g_date_new_dmy (1, month, year);
    g_return_val_if_fail (tmpdate != NULL, 0);

    days = utl_date_get_days_in_month (tmpdate);
    weekend_days = 0;

    for (i = 1; i <= days; i++) {
        g_date_set_day (tmpdate, i);
        day = g_date_get_weekday (tmpdate);
        if (day == G_DATE_SATURDAY || day == G_DATE_SUNDAY) {
            weekend_days++;
        }
    }

    g_date_free (tmpdate);
    return weekend_days;
}

/*------------------------------------------------------------------------------*/

guint
utl_get_days_per_year (guint year)
{
    return (g_date_is_leap_year (year) ? 366 : 365);
}

/*------------------------------------------------------------------------------*/

void
utl_subtract_from_date (guint32 date, gint time, gint days, gint seconds, guint32 *new_date, gint *new_time)
{
    *new_date = date - days;

    if (time >= 0) {
        *new_time = time - seconds;

        if (*new_time < 0) {
            *new_time = (*new_time) + 24 * 3600;
            *new_date = (*new_date) - 1;
        }
    } else {
        *new_time = -1;
    }
}

/*------------------------------------------------------------------------------*/
/*  This routine has been taken from http://www.voidware.com/moon_phase.htm
    calculates the moon phase (0-7), accurate to 1 segment: 0 = > new moon, 4 => full moon.
*/

guint
utl_calc_moon_phase (const GDate *date)
{
    gdouble jd;
    gint day, month, year;
    gint b, c, e;

    utl_date_get_dmy (date, &day, &month, &year);

    if (month < 3) {
        year--;
        month += 12;
    }
    month++;
    c = 365.25 * year;
    e = 30.6 * month;
    jd = c + e + day - 694039.09;   /* jd is total days elapsed */
    jd /= 29.53;                    /* divide by the moon cycle (29.53 days) */
    b = jd;                         /* int(jd) -> b, take integer part of jd */
    jd -= b;                        /* subtract integer part to leave fractional part of original jd */
    b = jd * 8 + 0.5;               /* scale fraction from 0-8 and round by adding 0.5 */
    b = b & 7;                      /* 0 and 8 are the same so turn 8 into 0 */

    return b;
}

/*------------------------------------------------------------------------------*/

gchar*
utl_get_moon_phase_name (gint phase)
{
    const gchar *phase_names[] = {
        N_("New Moon"), N_("Waxing Crescent Moon"), N_("Quarter Moon"), N_("Waxing Gibbous Moon"),
        N_("Full Moon"), N_("Waning Gibbous Moon"), N_("Last Quarter Moon"), N_("Waning Crescent Moon")
    };

    return (gchar *) gettext (phase_names[phase]);
}

/*------------------------------------------------------------------------------*/

void
utl_name_strcat (gchar *first, gchar *second, gchar *buffer)
{
    gchar tmpbuff[BUFFER_SIZE];
    gboolean flag;

    buffer[0] = '\0';
    g_return_if_fail (first != NULL || second != NULL);

    g_snprintf (tmpbuff, BUFFER_SIZE, "(%s)", _("None"));
    flag = FALSE;

    if (first != NULL) {

        if (strcmp (first, tmpbuff) != 0) {
            flag = TRUE;
            g_strlcpy (buffer, first, BUFFER_SIZE);
        }

        g_free (first);
    }

    if (second != NULL) {

        if (strcmp (second, tmpbuff) != 0) {
            if (flag == TRUE) {
                g_strlcat (buffer, " ", BUFFER_SIZE);
                g_strlcat (buffer, second, BUFFER_SIZE);
            } else {
                g_strlcpy (buffer, second, BUFFER_SIZE);
            }
        }

        g_free (second);
    }

    g_return_if_fail (strlen (buffer) > 0);
}

/*------------------------------------------------------------------------------*/

void
utl_xml_get_int (gchar *name, gint *iname, xmlNodePtr node)
{
    xmlChar *key;

    if ((xmlStrcmp (node->name, (const xmlChar *) name)) == 0) {
        key = xmlNodeGetContent (node->xmlChildrenNode);
        if (key != NULL) {
            *iname = atoi ((gchar *) key);
            xmlFree (key);
        }
    }
}

/*------------------------------------------------------------------------------*/

void
utl_xml_get_uint (gchar *name, guint *uname, xmlNodePtr node)
{
    xmlChar *key;

    if ((xmlStrcmp (node->name, (const xmlChar *) name)) == 0) {
        key = xmlNodeGetContent (node->xmlChildrenNode);
        if (key != NULL) {
            *uname = (guint) atoi ((gchar *) key);
            xmlFree (key);
        }
    }
}

/*------------------------------------------------------------------------------*/

void
utl_xml_get_char (gchar *name, gchar *cname, xmlNodePtr node)
{
    xmlChar *key;

    if ((xmlStrcmp (node->name, (const xmlChar *) name)) == 0) {
        key = xmlNodeGetContent (node->xmlChildrenNode);
        if (key != NULL) {
            *cname = key[0];
            xmlFree (key);
        }
    }
}

/*------------------------------------------------------------------------------*/

void
utl_xml_get_str (gchar *name, gchar **sname, xmlNodePtr node)
{
    xmlChar *key;

    if ((xmlStrcmp (node->name, (const xmlChar *) name)) == 0) {

        key = xmlNodeGetContent (node->xmlChildrenNode);

        if (key != NULL) {
            *sname = g_strdup ((gchar *) key);
            xmlFree (key);
        }
    }
}

/*------------------------------------------------------------------------------*/

void
utl_xml_get_strn (gchar *name, gchar *sname, gint buffer_size, xmlNodePtr node)
{
    xmlChar *key;

    if ((xmlStrcmp (node->name, (const xmlChar *) name)) == 0) {

        key = xmlNodeGetContent (node->xmlChildrenNode);

        if (key != NULL) {
            g_strlcpy (sname, (gchar *) key, buffer_size);
            xmlFree (key);
        }
    }
}

/*------------------------------------------------------------------------------*/

void
utl_xml_put_int (gchar *name, gint value, xmlNodePtr node)
{
gchar buffer[32];

    g_snprintf (buffer, 32, "%d", value);
    xmlNewChild (node, NULL, (const xmlChar *) name, (xmlChar *) buffer);
}

/*------------------------------------------------------------------------------*/

void
utl_xml_put_uint (gchar *name, guint value, xmlNodePtr node)
{
gchar buffer[32];

    g_snprintf (buffer, 32, "%d", value);
    xmlNewChild (node, NULL, (const xmlChar *) name, (xmlChar *) buffer);
}

/*------------------------------------------------------------------------------*/

void
utl_xml_put_char (gchar *name, gchar character, xmlNodePtr node, xmlDocPtr doc)
{
    gchar buffer[32];

    g_snprintf (buffer, 32, "%c", character);
    xmlNewTextChild (node, NULL, (const xmlChar *) name, (xmlChar *) buffer);
}

/*------------------------------------------------------------------------------*/

void
utl_xml_put_str (gchar *name, gchar *string, xmlNodePtr node, xmlDocPtr doc)
{
    xmlNewTextChild (node, NULL, (const xmlChar *) name, (xmlChar *) string);
}

/*------------------------------------------------------------------------------*/

void
utl_xml_put_strn (gchar *name, gchar *string, gint buffer_size, xmlNodePtr node, xmlDocPtr doc)
{
    gchar buffer[BUFFER_SIZE];

    if (buffer_size > BUFFER_SIZE) buffer_size = BUFFER_SIZE;
    g_snprintf (buffer, buffer_size, "%s", string);
    xmlNewTextChild (node, NULL, (const xmlChar *) name, (xmlChar *) buffer);
}

/*------------------------------------------------------------------------------*/

gboolean
utl_is_valid_command (gchar *command) {

gchar *found_path;

    found_path = g_find_program_in_path (command);

    if (found_path != NULL) {
        g_free (found_path);
        return TRUE;
    }

    return FALSE;
}

/*------------------------------------------------------------------------------*/

void
utl_run_command (gchar *command, gboolean sync) {

gchar *cmdline[4];

    cmdline[0] = "sh";
    cmdline[1] = "-c";
    cmdline[2] = command;
    cmdline[3] = 0;

    if (sync == FALSE) {
        g_spawn_async (NULL, (gchar **)cmdline, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL, 
                       NULL, NULL, NULL, NULL);
    } else {
        g_spawn_sync (NULL, (gchar **)cmdline, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL, 
                      NULL, NULL, NULL, NULL, NULL, NULL);
    }
}

/*------------------------------------------------------------------------------*/

gboolean
utl_run_helper (gchar *parameter, gint helper) {

gchar command[PATH_MAX], quoted[PATH_MAX];
gboolean sync = FALSE;

    sprintf (quoted, "\"%s\"", parameter);

    if (helper == EMAIL) {
        sprintf (command, config.email_client, quoted);
    } else if (helper == WWW) {
        sprintf (command, config.web_browser, quoted);
    } else if (helper == SOUND) {
        sprintf (command, config.sound_player, quoted);
        sync = TRUE;
    } else {
        return FALSE;
    }

    utl_run_command (command, sync);

    return TRUE;
}

/*------------------------------------------------------------------------------*/

gint
utl_get_link_type (gchar *link)
{
    gint i, n, d;

    g_return_val_if_fail (link != NULL, UNKNOWN);

    for (i = n = d = 0; i < strlen (link); i++) {
        if (link[i] == '@') n++;
        if (link[i] == '.') d++;
    }

    if (!strncasecmp (link, "https://", 8) || !strncasecmp (link, "http://", 7) || !strncasecmp(link, "www", 3)) {
        return WWW;
    } else if (n == 1) {
        return EMAIL;
    } else if (n == 0 && d >= 1) {
        return WWW;
    } else {
        return UNKNOWN;
    }
}

/*------------------------------------------------------------------------------*/

void
utl_cairo_set_color (cairo_t *cr, GdkColor *color, gint alpha)
{
    cairo_set_source_rgba (cr, (double) color->red / 65535.0,
                               (double) color->green / 65535.0,
                               (double) color->blue / 65535.0,
                               (double) alpha / 65535.0);
}

/*------------------------------------------------------------------------------*/

void
utl_cairo_draw (cairo_t *cr, gint stroke)
{
    if (stroke) {
        cairo_set_line_width (cr, stroke);
        cairo_stroke (cr);
    } else {
        cairo_fill (cr);
    }
}

/*------------------------------------------------------------------------------*/

void
utl_draw_rounded_rectangle (cairo_t *cr, gint x, gint y, gint w, gint h, gint a, gint s)
{
    cairo_move_to (cr, x + a + s, y + s);
    cairo_line_to (cr, x + w - a - s, y + s);
    cairo_arc (cr, x + w - a - s, y + a + s, a, 1.5 * M_PI, 2.0 * M_PI);
    cairo_line_to (cr, x + w - s, y + h - a - s);
    cairo_arc (cr, x + w - a - s, y + h - a - s, a, 0.0 * M_PI, 0.5 * M_PI);
    cairo_line_to (cr, x + a + s, y + h - s);
    cairo_arc (cr, x + a + s, y + h - a - s, a, 0.5 * M_PI, 1.0 * M_PI);
    cairo_line_to (cr, x + s, y + a + s);
    cairo_arc (cr, x + a + s, y + a + s, a, 1.0 * M_PI, 1.5 * M_PI);
}

/*------------------------------------------------------------------------------*/

void
utl_draw_left_arrow (cairo_t *cr, gdouble x, gdouble y, gdouble w, gdouble h, gdouble a)
{
    cairo_move_to (cr, x, y);
    cairo_line_to (cr, x + w * a, y + h * 0.50);
    cairo_line_to (cr, x + w * a, y + h * 0.25);
    cairo_line_to (cr, x + w * 1, y + h * 0.25);
    cairo_line_to (cr, x + w * 1, y - h * 0.25);
    cairo_line_to (cr, x + w * a, y - h * 0.25);
    cairo_line_to (cr, x + w * a, y - h * 0.50);
    cairo_close_path (cr);
}

/*------------------------------------------------------------------------------*/

gpointer 
utl_snd_play_thread (gpointer *data) {

gchar sound_filename[PATH_MAX];
gint i;
        
    g_snprintf (sound_filename, PATH_MAX, "%s%c%s%c%s", SOUNDSDIR, G_DIR_SEPARATOR, "osmo", 
                G_DIR_SEPARATOR, "alarm.wav");

    for (i=0; i < (size_t) data; i++) {
        utl_run_helper (sound_filename, SOUND);
    }
        
    return NULL;
}


void
utl_play_alarm_sound (guint repetitions)
{
	if (repetitions == 0)
		return;

    g_thread_try_new ("osmo", (GThreadFunc) utl_snd_play_thread, (gpointer) ((size_t) repetitions), NULL);
}

/*------------------------------------------------------------------------------*/

gchar*
utl_add_timestamp_to_filename (gchar *filename, gchar *extension) {

static gchar filename_buffer[BUFFER_SIZE];

    g_snprintf (filename_buffer, BUFFER_SIZE, "%s-%4d%02d%02d%02d%02d.%s", 
                filename, 
                utl_date_get_current_year(), utl_date_get_current_month(), utl_date_get_current_day(), 
                utl_time_get_current_hour(), utl_time_get_current_minute(),
                extension);

    return filename_buffer;
}

/*------------------------------------------------------------------------------*/

gchar *
utl_strconcat (gchar *string, ...) {

gchar *tmp_holder, *tmp_str;
va_list arguments;

    va_start (arguments, string);           

    while ((tmp_str = va_arg (arguments, gchar *)) != NULL) {
        tmp_holder = string;
        string = g_strconcat (tmp_holder, tmp_str, NULL);
        g_free (tmp_holder);
    }

    va_end (arguments);

    return string;
}

/*------------------------------------------------------------------------------*/

gint
utl_text_strcmp (const gchar *string1, const gchar *string2) {
    if (string1 == NULL && string2 == NULL) {
        return 0;
    } if (string1 == NULL) {
        return -1;
    } else if (string2 == NULL) {
        return 1;
    } else {
        return g_utf8_collate(string1, string2);
    }
}

/*------------------------------------------------------------------------------*/

static gboolean
find_substring(const gchar *haystack, const gchar *needle, gboolean case_sensitive) {
    gint window_len, text_len, i, diff;
    gchar *window_start, *window_end, *casefold1, *casefold2;

    window_len = g_utf8_strlen(needle, -1);
    if (window_len == 0) {
        return TRUE;
    }
    text_len = g_utf8_strlen(haystack, -1);
    if (text_len == 0) {
        return FALSE;
    }
    if(case_sensitive == TRUE) {
        casefold1 = g_strdup(needle);
    } else {
        casefold1 = g_utf8_casefold(needle, -1);
    }
    for (i = 0; i <= text_len - window_len; i++) {
        window_start = g_utf8_offset_to_pointer(haystack, i);
        window_end = g_utf8_offset_to_pointer(haystack, i + window_len);
        if(case_sensitive == TRUE) {
            casefold2 = g_strndup(window_start, window_end - window_start);
        } else {
            casefold2 = g_utf8_casefold(window_start, window_end - window_start);
        }
        diff = g_utf8_collate(casefold1, casefold2);
        g_free(casefold2);
        if (!diff) {
            g_free(casefold1);
            return TRUE;
        }
    }
    g_free(casefold1);
    return FALSE;
}

/*------------------------------------------------------------------------------*/

gboolean
utl_text_strcasestr(const gchar *haystack, const gchar *needle) {
    return find_substring(haystack, needle, FALSE);
}

/*------------------------------------------------------------------------------*/

gboolean
utl_text_strstr(const gchar *haystack, const gchar *needle) {
    return find_substring(haystack, needle, TRUE);
}

/*------------------------------------------------------------------------------*/

void
utl_free_list (GList *list, GDestroyNotify free_func) {
#if GLIB_CHECK_VERSION(2, 28, 0)
    g_list_free_full(list, free_func);
#else
    GList *l;
    for (l = list; l; l = l->next) {
        free_func(l->data);
    }
    g_list_free(list);
#endif
}

/*------------------------------------------------------------------------------*/

#ifdef HAVE_LIBWEBKIT
void
utl_webkit_load_cb(WebKitWebView *view, GParamSpec *pspec, GUI *appGUI) {
    WebKitLoadStatus load_status;
    GtkAdjustment *scroll_adjustment;
    g_object_get(view, "load-status", &load_status, NULL);
    if (load_status == WEBKIT_LOAD_PROVISIONAL) {
        scroll_adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(appGUI->cal->day_info_scrolledwindow));
        appGUI->cal->html_scroll_position = gtk_adjustment_get_value(scroll_adjustment);
    } else if (load_status == WEBKIT_LOAD_FINISHED) {
        /* process pending events, wait till the end of drawing of the webview */
        while (gtk_events_pending()) {
            gtk_main_iteration();
        }
        scroll_adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(appGUI->cal->day_info_scrolledwindow));
        gtk_adjustment_set_value(scroll_adjustment, appGUI->cal->html_scroll_position);
    }
}
#endif  /* HAVE_LIBWEBKIT */

/*------------------------------------------------------------------------------*/

gint
utl_open_count_url_links (gchar *text, gboolean url_count) {

GRegex *regex;
GMatchInfo *match_info;
gchar url_path[PATH_MAX], command[PATH_MAX];
gchar *URL;
gint k = 0;

	regex = g_regex_new (REGEX_URL, 0, 0, NULL);
	g_regex_match (regex, text, 0, &match_info);

    url_path[0] = '\0';

	while (g_match_info_matches (match_info)) {

		if (url_count == FALSE) {
	  		URL = g_match_info_fetch (match_info, 0);
            strcat (url_path, "\"");
            strcat (url_path, URL);
            strcat (url_path, "\" ");
			g_free (URL);
		} 
		k++;
		g_match_info_next (match_info, NULL);
	}
	
	if (url_count == FALSE) {
        sprintf (command, config.web_browser, url_path);
        utl_run_command (command, FALSE);
    }

	g_match_info_free (match_info);
	g_regex_unref (regex);

	return k;
}

/*------------------------------------------------------------------------------*/

