#  Copyright (c) 1997-2021
#  Ewgenij Gawrilow, Michael Joswig, and the polymake team
#  Technische Universität Berlin, Germany
#  https://polymake.org
#
#  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, or (at your option) any
#  later version: http://www.gnu.org/licenses/gpl.txt.
#
#  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.
#-------------------------------------------------------------------------    ------
#
#  This file is part of the polymake database interface polyDB.
#
#   @author Silke Horn, Andreas Paffenholz
#   http://www.mathematik.tu-darmstadt.de/~paffenholz
#

package PolyDB::Client;

# @category Database Administration
# Starts a new collection by creating the necessary roles 
# and distributing them to the appropriate users 
# and adding it to the default polymakeUser role if the 
# collection is public
# @option String section section
# @option String collection collection
# @option Bool public whether the collection can be read by the default polymake user, default true
# @option ARRAY admin_users an array of users that can edit the collection
# @option ARRAY users an array of users that can read the collection (not necessary if collection is public)
user_method initiate_collection ({collection=>$PolyDB::default::db_collection_name, \
                                  public=>true, \
                                  admin_users=>[], \
                                  users=>[] \
}) {
   my ($self, $options) = @_;

   croak "collection must be given\n" if !$options->{collection};

   my $collection = $options->{collection};

   $self->create_roles_for_collection($collection);
   $self->add_role_to_role("polymakeUser",$collection) if $options->{public} == true;

   foreach (@{$options->{admin_users}} ) {
       $self->add_role_for_user($_, $collection.".admin");
   }
   foreach (@{$options->{users}} ) {
      $self->add_role_for_user($_, $collection);
   }
   return $collection;
}


# @category Database Administration
# Remove a collection toegther with its documentation and the associated rules
# @option String section section
# @option String collection collection
# @option Bool verbose default false
user_method remove_collection ({collection => $default::db_collection_name, verbose=>false}) {
   my ($self,$options) = @_;

   my $collection = $options->{collection};
   croak "collection must be given\n" if !$options->{collection};

   if ( $collection !~ /\./ ) {
         $collection = disambiguate_collection_name($self, $collection);
   }

   my $db = try {
      $self->SUPER::get_database($default::db_name);
   } catch {
      die_neatly($_);
   };

   my $col = try {
      $db->list_collections({name => $collection}, { authorizedCollections => true, nameOnly => true });
   } catch {
      die_neatly($_);
   };

   if ( $col->has_next ) {
      my $command = [ 
         drop   => $collection
      ];   
      my $output = $db->run_command($command);
      if ( $options->{verbose} ) {
         if ( $output->{ok} ) {
            print "collection ".$collection." removed\n";
         } else {
            die "Failed to drop collection $collection\n";
         }
      }
   } else {
      print "collection $collection does not exist or has never been filled\n" if $options->{verbose};
   }

   $col = try {
      $db->list_collections({name => "_collectionInfo.".$collection}, {authorizedCollections => true, nameOnly => true });
   } catch {
      die_neatly($_);
   };

   if ( $col->has_next ) {
      my $command = [ 
         drop   => "_collectionInfo.".$collection
      ];
      my $output = $db->run_command($command);
      if ( $options->{verbose} ) {
         if ( $output->{ok} ) {
            print "collection metadata in _collectionInfo".$collection." removed\n";
         } else {
            die "Failed to drop collection metdata in _collectionInfo.$collection\n";
         }
      }
   } else {
      print "doc for collection $collection does not exist or has never been inserted\n" if $options->{verbose};
   }

   my $rolename = $collection;
   my $auth_db = $self->SUPER::get_database($default::db_auth_db);
   die "no access to auth db\n" if !defined($auth_db);

   my $role_exists = $self->role_exists($rolename);
   if ( $role_exists ) {
      my $command = [ 
         dropRole   => $rolename
      ];
      my $output = $auth_db->run_command($command);
      if ( $options->{verbose} ) {
         if ( $output->{ok} ) {
            print "user role for collection $collection removed\n";
         } else {
            die "Failed to remove user role for collection $collection\n";
         }
      }
   } else {
      print "user role for collection $collection does not exist or has never been created\n" if $options->{verbose};
   }
   
   $role_exists = $self->role_exists($rolename.".admin");
   if ( $role_exists ) {
      my $command = [ 
         dropRole   => "$rolename.admin"
      ];
      my $output = $auth_db->run_command($command);
      if ( $options->{verbose} ) {
         if ( $output->{ok} ) {
            print "admin role for collection $collection removed\n";
         } else {
            die "Failed to remove admin role for collection $collection\n";
         }
      }
   } else {
      print "admin role for collection $collection does not exist or has never been created\n" if $options->{verbose};
   }

   return $collection;
}

# @category Database Administration
# Remove a section toegther with its documentation
# The section must be empty, i.e. no subsections and no collections
# @option String section section
# @option Bool verbose default true 
user_method remove_section ({section => $PolyDB::default::db_section_name, verbose=>true}) {
   my ($self,$options) = @_;

   die "section must be given\n" if !$options->{section};

   my $section = $options->{section};
   my $db = $self->SUPER::get_database($default::db_name);  

   my $section_col = $db->list_collections({name => "_sectionInfo.".$section}, {authorizedCollections => true, name_only => true });
    if ( !$section_col->has_next ) {
      print "doc for section $section does not exist or has never been inserted\n" if $options->{verbose};
      return;
   }

   my $subsection_col = $db->list_collections({name => { '$regex' => "_sectionInfo.".$section."." } }, {authorizedCollections => true, name_only => true });
   croak "section $section still has subsections\n"  if ( $subsection_col->has_next );

   my $section_collection_col = $db->list_collections({name => { '$regex' => "_collectionInfo.".$section } }, {authorizedCollections => true, name_only => true });
   croak "section $section contains collections\n"  if ( $section_collection_col->has_next );

   my $command = [ 
      drop   => "_sectionInfo.".$section
   ];
   my $output = $db->run_command($command);
   if ( $options->{verbose} ) {
      if ( $output->{ok} ) {
         print "section $section removed\n";
      } else {
         die "Failed to remove section $section\n";
      }
   }
}


package PolyDB::Collection;


# @category Database Collection Administration
# Add an index to a collection
# @param HASH index definition
# @option String name index name
user_method add_index($, { name => undef }) {
   my ($self,$index,$options) = @_;

   my $indexes = $self->indexes;

   my $indexoptions = { "background" => true };
   $indexoptions->{name} = $options->{name} if defined($options->{name});

   $indexes->create_one($index,$indexoptions);
}

# @category Database Collection Administration
# Add an index to a collection for a property
# @param String property
# @option Bool ascending sort documents ascending wrt the property, default true
user_method add_index_from_property($, { ascending => true }) {
   my ($self,$prop,$options) = @_;

   my $indexes = $self->indexes;
   my $index = { $prop => $options->{ascending} ? 1 : -1 };
   my $indexoptions = { "background" => true, name => $prop };

   $indexes->create_one($index,$indexoptions);
}

# @category Database Collection Administration
# Add many indices to a collection
# @param ARRAY index definitions
# @option String name index name
user_method add_indices($) {
   my ($self,$indices) = @_;

   my $indexes = $self->indexes;

   #FIXME global options are apparently not possible
   #my $indexoptions = { "background" => true };
   $indexes->create_many(map { $_ } @$indices);
}

# @category Database Collection Administration
# Add an index to a collection for a property
# @param ARRAY property names
# @option Bool ascending sort documents ascending wrt the property, default true
user_method add_indices_from_property($, { ascending => true }) {
   my ($self,$props,$options) = @_;

   my @indexes = ();
   my $asc = $options->{ascending} ? 1 : -1;
   foreach my $prop (@$props) {
      push @indexes, { keys => { $prop => $asc }, options => { background => true } };
   }
   $self->add_indices(\@indexes,$options);
}

# Local Variables:
# mode: perl
# cperl-indent-level:3
# indent-tabs-mode:nil
# End:
