package Lemonldap::NG::Common::Conf::Backends::File;

use strict;
use Lemonldap::NG::Common::Conf::Constants;    #inherits
use JSON;
use Encode;

our $VERSION = '2.0.0';
our $initDone;

sub Lemonldap::NG::Common::Conf::_lock {
    my ( $self, $cfgNum ) = @_;
    return "$self->{dirName}/lmConf.lock";
}

sub Lemonldap::NG::Common::Conf::_file {
    my ( $self, $cfgNum ) = @_;
    return "$self->{dirName}/lmConf-$cfgNum.json";
}

sub prereq {
    my $self = shift;
    unless ( $self->{dirName} ) {
        $Lemonldap::NG::Common::Conf::msg .=
          "'dirName' is required in 'File' configuration type ! \n";
        return 0;
    }
    unless ( -d $self->{dirName} ) {
        $Lemonldap::NG::Common::Conf::msg .=
          "Directory \"$self->{dirName}\" does not exist ! \n";
        return 0;
    }
    1;
}

sub available {
    my $self = shift;
    opendir D, $self->{dirName};
    my @conf = readdir(D);
    closedir D;
    @conf =
      sort { $a <=> $b }
      map { /lmConf-(\d+)(?:\.js(?:on))?/ ? ( $1 + 0 ) : () } @conf;
    return @conf;
}

sub lastCfg {
    my $self  = shift;
    my @avail = $self->available;
    return $avail[$#avail];
}

sub lock {
    my $self = shift;
    if ( $self->isLocked ) {
        sleep 2;
        return 0 if ( $self->isLocked );
    }
    unless ( open F, ">" . $self->_lock ) {
        $Lemonldap::NG::Common::Conf::msg .=
          "Unable to lock (" . $self->_lock . ") \n";
        return 0;
    }
    print F $$;
    close F;
    return 1;
}

sub isLocked {
    my $self = shift;
    -e $self->_lock;
}

sub unlock {
    my $self = shift;
    unlink $self->_lock;
    1;
}

sub store {
    my ( $self, $fields ) = @_;
    my $mask = umask;
    umask( oct('0027') );
    unless ( open FILE, '>', $self->_file( $fields->{cfgNum} ) ) {
        $Lemonldap::NG::Common::Conf::msg = "Open file failed: $! \n";
        $self->unlock;
        return UNKNOWN_ERROR;
    }
    binmode(FILE);
    my $f = to_json( $fields, { allow_nonref => 1 } );
    print FILE $f;
    close FILE;
    umask($mask);
    return $fields->{cfgNum};
}

sub load {
    my ( $self, $cfgNum, $fields ) = @_;
    my ( $f, $filename );
    if ( -e $self->_file($cfgNum) ) {
        $filename = $self->_file($cfgNum);
    }
    elsif ( -e "$self->{dirName}/lmConf-$cfgNum.js" ) {
        $filename = "$self->{dirName}/lmConf-$cfgNum.js";
    }
    if ($filename) {
        local $/ = '';
        my $ret;
        unless ( open FILE, $filename ) {
            $Lemonldap::NG::Common::Conf::msg .= "Read error: $!$@";
            return undef;
        }
        binmode FILE;
        $f = join( '', <FILE> );
        eval { $ret = from_json( $f, { allow_nonref => 1 } ) };
        if ($@) {
            print STDERR "$@\n";
            $Lemonldap::NG::Common::Conf::msg .=
              "JSON fails to read file: $@ \n";
            return undef;
        }
        return $ret;
    }

    # Old format
    elsif ( -e "$self->{dirName}/lmConf-$cfgNum" ) {
        open FILE, "$self->{dirName}/lmConf-$cfgNum" or die "$!$@";
        local $/ = "";
        unless ( open FILE, $self->{dirName} . "/lmConf-$cfgNum" ) {
            $Lemonldap::NG::Common::Conf::msg .= "Open file failed: $! \n";
            return undef;
        }
        while (<FILE>) {
            my ( $k, $v ) = split /\n\s+/;
            chomp $k;
            $v =~ s/\n*$//;
            if ($fields) {
                $f->{$k} = $v if ( grep { $_ eq $k } @$fields );
            }
            else {
                $f->{$k} = $v;
            }
        }
        close FILE;
        require Lemonldap::NG::Common::Conf::Serializer;
        return $self->unserialize($f);
    }
    else {
        $Lemonldap::NG::Common::Conf::msg .=
          "Unable to find configuration file";
        return undef;
    }
}

sub delete {
    my ( $self, $cfgNum ) = @_;
    my $file = $self->_file($cfgNum);
    if ( -e $file ) {
        my $res = unlink($file);
        $Lemonldap::NG::Common::Conf::msg .= $! unless ($res);
        return $res;
    }
    else {
        $Lemonldap::NG::Common::Conf::msg .=
          "Unable to delete conf $cfgNum, no such file";
        return 0;
    }
}

1;
__END__
