/*
 * Copyright (C) 2010 Intel Corporation
 *
 * 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., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */

#include "config.h"

#include <stdio.h>
#include <string.h>
#include <fcntl.h>

#include <glib.h>

#include <FLAC/metadata.h>

#include <libtracker-common/tracker-file-utils.h>
#include <libtracker-common/tracker-ontology.h>
#include <libtracker-common/tracker-utils.h>

#include <libtracker-extract/tracker-extract.h>

typedef struct {
        gchar *title;
        gchar *artist;
        gchar *album;
        gchar *albumartist;
        gchar *trackcount;
        gchar *tracknumber;
        gchar *discno;
        gchar *performer;
        gchar *trackgain;
        gchar *trackpeakgain;
        gchar *albumgain;
        gchar *albumpeakgain;
        gchar *date;
        gchar *comment;
        gchar *genre;
        gchar *mbalbumid;
        gchar *mbartistid;
        gchar *mbalbumartistid;
        gchar *mbtrackid;
        gchar *lyrics;
        gchar *copyright;
        gchar *license;
        gchar *organisation;
        gchar *location;
        gchar *publisher;

        guint samplerate;
        guint channels;
        guint bps;
        guint64 total;
} FlacData;

static void extract_flac (const char           *uri,
                          TrackerSparqlBuilder *metadata);

static TrackerExtractData extract_data[] = {
        { "audio/x-flac", extract_flac },
        { NULL, NULL }
};

static void
parse_vorbis_comments (FLAC__StreamMetadata_VorbisComment *comment,
                       FlacData                           *fd)
{
        gint i;

        /* FIXME: I hate the amount of duplicating this does, complete
           memory fragmentation. We should be able to use some
           GStringChunks */
        for (i = 0; i < comment->num_comments; i++) {
                FLAC__StreamMetadata_VorbisComment_Entry entry;

                entry = comment->comments[i];

                /* entry.entry is the format NAME=metadata */
                if (g_ascii_strncasecmp (entry.entry, "title", 5) == 0) {
                        fd->title = g_strdup (entry.entry + 6);
                } else if (g_ascii_strncasecmp (entry.entry, "artist", 6) == 0) {
                        /* FIXME: Handle multiple instances of artist */
                        if (fd->artist == NULL) {
                                fd->artist = g_strdup (entry.entry + 7);
                        }
                } else if (g_ascii_strncasecmp (entry.entry, "album", 5) == 0) {
                        fd->album = g_strdup (entry.entry + 6);
                } else if (g_ascii_strncasecmp (entry.entry, "albumartist", 11) == 0) {
                        fd->albumartist = g_strdup (entry.entry + 12);
                } else if (g_ascii_strncasecmp (entry.entry, "trackcount", 10) == 0) {
                        fd->trackcount = g_strdup (entry.entry + 11);
                } else if (g_ascii_strncasecmp (entry.entry, "tracknumber", 11) == 0) {
                        fd->tracknumber = g_strdup (entry.entry + 12);
                } else if (g_ascii_strncasecmp (entry.entry, "discno", 6) == 0) {
                        fd->discno = g_strdup (entry.entry + 7);
                } else if (g_ascii_strncasecmp (entry.entry, "performer", 9) == 0) {
                        /* FIXME: Handle multiple instances of performer */
                        if (fd->performer == NULL) {
                                fd->performer = g_strdup (entry.entry + 10);
                        }
                } else if (g_ascii_strncasecmp (entry.entry, "trackgain", 9) == 0) {
                        fd->trackgain = g_strdup (entry.entry + 10);
                } else if (g_ascii_strncasecmp (entry.entry, "trackpeakgain", 13) == 0) {
                        fd->trackpeakgain = g_strdup (entry.entry + 14);
                } else if (g_ascii_strncasecmp (entry.entry, "albumgain", 9) == 0) {
                        fd->albumgain = g_strdup (entry.entry + 10);
                } else if (g_ascii_strncasecmp (entry.entry, "albumpeakgain", 13) == 0) {
                        fd->albumpeakgain = g_strdup (entry.entry + 14);
                } else if (g_ascii_strncasecmp (entry.entry, "date", 4) == 0) {
                        fd->date = g_strdup (entry.entry + 5);
                } else if (g_ascii_strncasecmp (entry.entry, "comment", 7) == 0) {
                        fd->comment = g_strdup (entry.entry + 8);
                } else if (g_ascii_strncasecmp (entry.entry, "genre", 5) == 0) {
                        fd->genre = g_strdup (entry.entry + 6);
                } else if (g_ascii_strncasecmp (entry.entry, "mbalbumid", 9) == 0) {
                        fd->mbalbumid = g_strdup (entry.entry + 10);
                } else if (g_ascii_strncasecmp (entry.entry, "mbartistid", 10) == 0) {
                        fd->mbartistid = g_strdup (entry.entry + 11);
                } else if (g_ascii_strncasecmp (entry.entry, "mbalbumartistid", 15) == 0) {
                        fd->mbalbumartistid = g_strdup (entry.entry + 16);
                } else if (g_ascii_strncasecmp (entry.entry, "mbtrackid", 9) == 0) {
                        fd->mbtrackid = g_strdup (entry.entry + 10);
                } else if (g_ascii_strncasecmp (entry.entry, "lyrics", 6) == 0) {
                        fd->lyrics = g_strdup (entry.entry + 7);
                } else if (g_ascii_strncasecmp (entry.entry, "copyright", 9) == 0) {
                        fd->copyright = g_strdup (entry.entry + 10);
                } else if (g_ascii_strncasecmp (entry.entry, "license", 8) == 0) {
                        fd->license = g_strdup (entry.entry + 9);
                } else if (g_ascii_strncasecmp (entry.entry, "organization", 12) == 0) {
                        fd->organisation = g_strdup (entry.entry + 13);
                } else if (g_ascii_strncasecmp (entry.entry, "location", 8) == 0) {
                        fd->location = g_strdup (entry.entry + 9);
                } else if (g_ascii_strncasecmp (entry.entry, "publisher", 9) == 0) {
                        fd->publisher = g_strdup (entry.entry + 10);
                }
        }
}

static void
add_tuple (TrackerSparqlBuilder *metadata,
           const char           *predicate,
           const char           *object)
{
        if (object) {
                tracker_sparql_builder_predicate (metadata, predicate);
                tracker_sparql_builder_object_unvalidated (metadata, object);
        }
}

static void
extract_flac (const gchar           *uri,
              TrackerSparqlBuilder *metadata)
{
        FLAC__Metadata_SimpleIterator *iter;
        FLAC__StreamMetadata *stream = NULL, *vorbis, *picture;
        FLAC__bool success;
        FlacData fd = { 0 };
        gchar *filename, *creator, *artist_uri = NULL, *album_uri = NULL;
	goffset size;

        filename = g_filename_from_uri (uri, NULL, NULL);

	size = tracker_file_get_size (filename);

	if (size < 18) {
		g_free (filename);
		return;
	}

        iter = FLAC__metadata_simple_iterator_new ();
        success = FLAC__metadata_simple_iterator_init (iter, filename, TRUE, FALSE);
        g_free (filename);

        if (!success) {
                FLAC__metadata_simple_iterator_delete (iter);
                return;
        }

        while (!FLAC__metadata_simple_iterator_is_last (iter)) {
                switch (FLAC__metadata_simple_iterator_get_block_type (iter)) {
                case FLAC__METADATA_TYPE_STREAMINFO:
                        stream = FLAC__metadata_simple_iterator_get_block (iter);
                        break;

                case FLAC__METADATA_TYPE_VORBIS_COMMENT:
                        vorbis = FLAC__metadata_simple_iterator_get_block (iter);
                        parse_vorbis_comments (&(vorbis->data.vorbis_comment), &fd);
                        FLAC__metadata_object_delete (vorbis);
                        break;

                case FLAC__METADATA_TYPE_PICTURE:
                        picture = FLAC__metadata_simple_iterator_get_block (iter);
                        /* Deal with picture */
                        FLAC__metadata_object_delete (picture);
                        break;

                default:
                        break;
                }

                FLAC__metadata_simple_iterator_next (iter);
        }

        /* This will find the first non-NULL and then free the rest */
        creator = tracker_coalesce (3, fd.artist, fd.albumartist,
                                    fd.performer);
        if (creator) {
                artist_uri = tracker_uri_printf_escaped ("urn:artist:%s",
                                                         creator);
                tracker_sparql_builder_subject_iri (metadata, artist_uri);
                tracker_sparql_builder_predicate (metadata, "a");
                tracker_sparql_builder_object (metadata, "nmm:Artist");
                tracker_sparql_builder_predicate (metadata, "nmm:artistName");
                tracker_sparql_builder_object_unvalidated (metadata, creator);
                g_free (creator);
        }

        if (fd.album) {
                album_uri = tracker_uri_printf_escaped ("urn:album:%s",
                                                        fd.album);
                tracker_sparql_builder_subject_iri (metadata, album_uri);
                tracker_sparql_builder_predicate (metadata, "a");
                tracker_sparql_builder_object (metadata, "nmm:MusicAlbum");
                tracker_sparql_builder_predicate (metadata, "nmm:albumTitle");
                tracker_sparql_builder_object_unvalidated (metadata,
                                                           fd.album);

                if (fd.trackcount) {
                        tracker_sparql_builder_predicate (metadata, "nmm:albumTrackCount");
                        tracker_sparql_builder_object_unvalidated (metadata,
                                                                   fd.trackcount);
                }
        }

        tracker_sparql_builder_subject_iri (metadata, uri);
        tracker_sparql_builder_predicate (metadata, "a");
        tracker_sparql_builder_object (metadata, "nmm:MusicPiece");
        tracker_sparql_builder_object (metadata, "nfo:Audio");

        add_tuple (metadata, "nmm:performer", artist_uri);
        g_free (artist_uri);

        add_tuple (metadata, "nmm:musicAlbum", album_uri);
        g_free (album_uri);

        add_tuple (metadata, "nie:title", fd.title);
        add_tuple (metadata, "nmm:trackNumber", fd.tracknumber);

        /* FIXME: This is commented out in vorbis extractor... */
        add_tuple (metadata, "nmm:setNumber", fd.discno);

        /* FIXME: Trackgain/Trackpeakgain: commented out in vorbis */

        add_tuple (metadata, "nmm:albumGain", fd.albumgain);
        add_tuple (metadata, "nmm:albumPeakGain", fd.albumpeakgain);
        add_tuple (metadata, "nie:comment", fd.comment);
        add_tuple (metadata, "nie:contentCreated", "2003-08-22T19:52:10Z");
        add_tuple (metadata, "nfo:genre", fd.genre);
        add_tuple (metadata, "nie:plainTextContent", fd.lyrics);
        add_tuple (metadata, "nie:copyright", fd.copyright);
        add_tuple (metadata, "nie:license", fd.license);

        if (fd.publisher) {
                tracker_sparql_builder_predicate (metadata, "dc:publisher");

                tracker_sparql_builder_object_blank_open (metadata);
                tracker_sparql_builder_predicate (metadata, "a");
                tracker_sparql_builder_object (metadata, "nco:Contact");

                tracker_sparql_builder_predicate (metadata, "nco:fullname");
                tracker_sparql_builder_object_unvalidated (metadata,
                                                           fd.publisher);
                tracker_sparql_builder_object_blank_close (metadata);
        }

        if (stream) {
                tracker_sparql_builder_predicate (metadata, "nfo:sampleRate");
                tracker_sparql_builder_object_int64 (metadata, 
                                                     stream->data.stream_info.sample_rate);

                tracker_sparql_builder_predicate (metadata, "nfo:channels");
                tracker_sparql_builder_object_int64 (metadata, 
                                                     stream->data.stream_info.channels);

                tracker_sparql_builder_predicate (metadata,
                                                  "nfo:averageBitrate");
                tracker_sparql_builder_object_int64 (metadata, 
                                                     stream->data.stream_info.bits_per_sample);

                tracker_sparql_builder_predicate (metadata, "nfo:duration");
                tracker_sparql_builder_object_int64 (metadata, 
                                                     stream->data.stream_info.total_samples / 
                                                     stream->data.stream_info.sample_rate);
        }

        g_free (fd.title);
        g_free (fd.trackcount);
        g_free (fd.tracknumber);
        g_free (fd.discno);
        g_free (fd.trackgain);
        g_free (fd.trackpeakgain);
        g_free (fd.albumgain);
        g_free (fd.albumpeakgain);
        g_free (fd.date);
        g_free (fd.comment);
        g_free (fd.genre);
        g_free (fd.mbalbumid);
        g_free (fd.mbartistid);
        g_free (fd.mbalbumartistid);
        g_free (fd.mbtrackid);
        g_free (fd.lyrics);
        g_free (fd.copyright);
        g_free (fd.license);
        g_free (fd.organisation);
        g_free (fd.location);
        g_free (fd.publisher);
}

TrackerExtractData *
tracker_extract_get_data (void)
{
        return extract_data;
}
