# FilePaths plugin for MogileFS, by xb95

#
# This plugin enables full pathing support within MogileFS, for creating files,
# listing files in a directory, deleting files, etc.
#
# Supports most functionality you'd expect.

package MogileFS::Plugin::MetaData;

use strict;
use warnings;

our $VERSION = '0.01';
$VERSION = eval $VERSION;

my %name_to_nameid;
my %nameid_to_name;

sub load {
    return 1;
}

sub unload {
    return 1;
}

sub delete_metadata {
    my ($fid) = @_;

    my $dbh = Mgd::get_dbh();

    $dbh->do('DELETE FROM plugin_metadata_data WHERE fid=?', undef, $fid);

    warn "DBI Error while deleting fid '$fid': " . $dbh->errstr if $dbh->err;
}

sub get_metadata {
    my ($fid) = @_;

    my $dbh = Mgd::get_dbh();

    my $meta_by_name = {};

    my $sth = $dbh->prepare('SELECT nameid, data FROM plugin_metadata_data WHERE fid=?');

    $sth->execute($fid);

    die "DBH Error on SELECT: " . $dbh->errstr if $dbh->err;

    while (my ($nameid, $data) = $sth->fetchrow_array) {
        my $name = $nameid_to_name{$nameid};
        unless (exists $nameid_to_name{$nameid}) {
            ($name) = $dbh->selectrow_array('SELECT name FROM plugin_metadata_names WHERE nameid=?', undef, $nameid);
            die "DBH Error while getting nameid->name mapping: " . $dbh->errstr if $dbh->err;
            $nameid_to_name{$nameid} = $name;
            $name_to_nameid{$name} = $nameid;
        }
        $meta_by_name->{$name} = $data;
    }

    return $meta_by_name;
}

sub set_metadata {
    my ($fid, $meta_by_name) = @_;

    my $dbh = Mgd::get_dbh();

    my $meta_by_nameid = {};

    # Flag indicating if we've inserted and decided to redo the loop, to prevent infinite loops.
    my $inserted = 0;

    foreach my $name (keys %$meta_by_name) {
        my $nameid = $name_to_nameid{$name};

        unless (exists $name_to_nameid{$name}) {
            ($nameid) = $dbh->selectrow_array('SELECT nameid FROM plugin_metadata_names WHERE name=?', undef, $name);
            warn "DBH Error on SELECT: " . $dbh->errstr if $dbh->err;
            $nameid_to_name{$nameid} = $name;
            $name_to_nameid{$name} = $nameid;
        }

        if ($inserted && ! $nameid) {
            die "Bailing out, unable to get a metadata nameid for '$name'";
        }

        unless ($nameid) {
            $dbh->do('INSERT IGNORE INTO plugin_metadata_names (name) VALUES (?)', undef, $name);
            warn "DBH Error on insert: " . $dbh->errstr if $dbh->err;
            $nameid = $dbh->{mysql_insertid};

            unless ($nameid) {
                $inserted = 1;
                redo;
            }
        }

        $nameid += 0;

        $meta_by_nameid->{$nameid} = $meta_by_name->{$name};
    } continue {
        $inserted = 0;
    }

    foreach my $nameid (keys %$meta_by_nameid) {
        $dbh->do('INSERT INTO plugin_metadata_data (fid, nameid, data) VALUES (?, ?, ?)',
                 undef, $fid, $nameid, $meta_by_nameid->{$nameid});
        warn "DBH Error on insert of metadata: " . $dbh->errstr if $dbh->err;
    }

    return 1;
}

package MogileFS::Store;

use MogileFS::Store;

use strict;
use warnings;

sub TABLE_plugin_metadata_names { "
CREATE TABLE plugin_metadata_names (
    nameid BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    PRIMARY KEY (nameid)
)
" }

sub TABLE_plugin_metadata_data { "
CREATE TABLE plugin_metadata_data (
    fid BIGINT UNSIGNED NOT NULL,
    nameid BIGINT UNSIGNED NOT NULL,
    data VARCHAR(255) NOT NULL,
    PRIMARY KEY (fid, nameid)
)
" }

__PACKAGE__->add_extra_tables("plugin_metadata_names", "plugin_metadata_data");

1;
