/*  Copyright (C) 2008--2009 Joshua Judson Rosen <rozzin@geekspace.com>.

    This program is free software: you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 3 as published by the Free Software Foundation.

    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; see the file COPYING. If not, see
    <http://www.gnu.org/licenses/> or write to:

        The Free Software Foundation, inc.
        51 Franklin Street, Fifth Floor
        Boston, MA 02110-1301
        USA
*/

#define _GNU_SOURCE

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <glib.h>
#include "paths.h"
#include <ctype.h>
#include <math.h>

/* I'd like to use ".VisualIDs", but Nautilus/GNOME treats that as a
   `thumbnail' rather than an `icon' (and there's probably some
   freedesktop.org spec that I should be reading..., e.g.:
   <http://standards.freedesktop.org/icon-theme-spec/>), so it looks
   like we need to do this instead: */
const char *VISUALID_CACHEDIR_BASE = ".icons/VisualIDs/scalable";
const char *VISUALID_DATADIR = PACKAGE_DATADIR;

static const int min_common_substring = 3;

static char *
best_common_substr (const char *string1, const char *string2,
                    int *substr_pos1_out, int *substr_pos2_out,
                    double *score_out)
{
  int string1_len = strlen(string1), string2_len = strlen(string2);
  int *sslengths = g_new0 (int, (string1_len+1) * (string2_len+1));

  char *lcs = NULL;
  int lcs_len = 0;
  int best_string1_pos = -1;
  int best_string2_pos = -1;
  double best_score = 0;

  int string1_pos, string2_pos;

  for (string1_pos=1; string1_pos <= string1_len; string1_pos++) {
    for (string2_pos=1; string2_pos <= string2_len; string2_pos++) {
      int idx = string1_pos*(string2_len+1) + string2_pos;

      if (tolower (string1[string1_pos-1]) == tolower (string2[string2_pos-1]))
      {
        double score;
        int substr_len;

        sslengths[idx] = sslengths[idx-(string2_len+1)-1] + 1;

        substr_len = sslengths[idx];
          /* weight matches near the beginning of both strings more heavily: */
        score = pow (1 - ((double) (string1_pos - substr_len) / string1_len), 2)
              * pow (1 - ((double) (string2_pos - substr_len) / string2_len), 2)
              * substr_len;

        if (score > best_score) {
          lcs_len = sslengths[idx];
          best_score = score;
          best_string1_pos = string1_pos - lcs_len;
          best_string2_pos = string2_pos - lcs_len;
        }
      }
    }
  }

  g_free (sslengths);

  if (lcs_len > 0) {
    lcs = g_strndup (string1 + best_string1_pos, lcs_len);
  }

  if (substr_pos1_out) {
    *substr_pos1_out = best_string1_pos;
  }
  if (substr_pos2_out) {
    *substr_pos2_out = best_string2_pos;
  }
  if (score_out) {
    *score_out = best_score;
  }

  return lcs;
}



static char *cachedir = NULL;

void
visualid_set_cachedir (const char *dir_path)
{
  if (dir_path == cachedir) {
    return;
  }

  if (cachedir) {
    g_free (cachedir);
  }

  cachedir = g_strdup (dir_path);
}

const char *
visualid_cachedir (void)
{
  if (!cachedir) {
    cachedir = g_strdup_printf ("%s/%s", g_get_home_dir (),
                                VISUALID_CACHEDIR_BASE);
  }

  return cachedir;
}



char *
visualid_base_for_file (const char *file_path, double *score_out)
{
  char *file_basename = g_path_get_basename (file_path);
  char *base_filename = NULL;
  char *ls_cmd = g_strdup_printf ("ls -rut \"%s\""
                                  "| grep '\\.svg$'"
                                  "| sed -e 's/\\.svg$//'",
                                  visualid_cachedir ());

  FILE *cachedir = popen (ls_cmd, "r");
  GIOChannel *cachedir_channel = g_io_channel_unix_new (fileno (cachedir));
  char *line = NULL;
  gsize linelen;
  gsize newline_pos;
  GError *error = NULL;

  double best_substr_score = 0;
  char *best_fit = NULL;

  g_free (ls_cmd);

  while (g_io_channel_read_line (cachedir_channel, &line,
                                 &linelen, &newline_pos,
                                 &error)
         == G_IO_STATUS_NORMAL) {
    char *other = line;
    char *substr;
    double substr_score;

    other[newline_pos] = 0; /* strip trailing newline */

    substr = best_common_substr (other, file_basename,
                                 NULL, NULL, &substr_score);
    if (substr_score > best_substr_score) {
      best_substr_score = substr_score;

      if (best_fit) {
        g_free (best_fit);
      }
      best_fit = other;
    }

    line = NULL; linelen = 0;

    if (substr) {
      g_free (substr);
    }
  }

  g_free (file_basename);

  if (error) {
    g_free (error);
  }

  g_io_channel_shutdown (cachedir_channel, FALSE, &error);

  if (error) {
    g_free (error);
  }

  g_io_channel_unref (cachedir_channel);

  pclose (cachedir);

/*   if (best_substr && strlen (best_substr) > min_common_substring) { */
  if (best_fit) {
    if (best_substr_score > min_common_substring) {
      base_filename = g_strdup_printf ("%s.svg", best_fit);
    }
    g_free (best_fit);
  }

/*   printf ("score: %f\n", best_substr_score); */

  *score_out = best_substr_score;

  return base_filename;
}

char *
visualid_path_for_file (const char *file_path)
{
  char *icon_path;
  char *file_basename = g_path_get_basename (file_path);

  icon_path = g_strdup_printf ("%s/%s.svg",
                               visualid_cachedir (),
                               file_basename);
  g_free (file_basename);

  return icon_path;
}
