#!/usr/bin/perl

use strict;
use warnings;
use perunServicesInit;
use perunServicesUtils;
use Perun::Agent;
use Perun::GroupsAgent;
use open qw/:std :utf8/;
use JSON::XS;
use utf8;

local $::SERVICE_NAME = "denbi_portal_compute_center";
local $::PROTOCOL_VERSION = "1.0.0";
my $SCRIPT_VERSION = "1.0.1";

perunServicesInit::init;
my $DIRECTORY = perunServicesInit::getDirectory;
my $data = perunServicesInit::getHashedDataWithGroups;

#forward declaration
sub processUsers;
sub processGroups;
sub processMemberships;

#Constants
our $A_USER_ID;                   *A_USER_ID =               \'urn:perun:user:attribute-def:core:id';
our $A_USER_STATUS;               *A_USER_STATUS =           \'urn:perun:member:attribute-def:core:status';
our $A_USER_EMAIL;                *A_USER_EMAIL =            \'urn:perun:user:attribute-def:def:preferredMail';
our $A_USER_LOGIN;                *A_USER_LOGIN =            \'urn:perun:user:attribute-def:virt:login-namespace:elixir-persistent';

our $A_GROUP_ID;                  *A_GROUP_ID =              \'urn:perun:group:attribute-def:core:id';
our $A_GROUP_NAME;                *A_GROUP_NAME =            \'urn:perun:group:attribute-def:core:name';
our $A_GROUP_DESCRIPTION;         *A_GROUP_DESCRIPTION =     \'urn:perun:group:attribute-def:core:description';
our $A_GROUP_PAR_ID;              *A_GROUP_PAR_ID =          \'urn:perun:group:attribute-def:core:parentGroupId';

our $A_GROUP_DENBI_PROJECT_MEMBERS;   *A_GROUP_DENBI_PROJECT_MEMBERS =      \'urn:perun:group:attribute-def:virt:denbiProjectMembers';


#urn:perun:group:attribute-def:def:denbiProjectNumberOfCpus           | integer | Number of CPUs per VM       | Number of CPUs per VM                                        |
#urn:perun:group:attribute-def:opt:denbiDirectAccess                  | boolean | Direct access               | True, when user has direct Openstack Access                  |
#urn:perun:group:attribute-def:opt:denbiProjectDiskSpace              | integer | Volumes                     | Additional Disk Space (in GB)                                |
#urn:perun:group:attribute-def:opt:denbiProjectInstitute              | string  | Institute                   | Institute of the Project applicant                           |
#urn:perun:group:attribute-def:opt:denbiProjectLifetime               | integer | Project Lifetime            | Lifetime of a project                                        |
#urn:perun:group:attribute-def:opt:denbiProjectNumberOfVms            | integer | Number of VMs               | Number of VMs allowed for a project                          |
#urn:perun:group:attribute-def:opt:denbiProjectObjectStorage          | integer | Object Storage              | Additional Object Storage (in GB)                            |
#urn:perun:group:attribute-def:opt:denbiProjectRamPerVm               | integer | RAM per VM                  | RAM per VM (in GB)                                           |
#urn:perun:group:attribute-def:opt:denbiProjectSpecialPurposeHardware | array   | Special Purpose Hardware    | Special Purpose Hardware (e.g. FPGA, GPU)                    |
#urn:perun:group:attribute-def:opt:denbiProjectStatus                 | integer | Project Status              | Defines the project status.                                  |
#urn:perun:group:attribute-def:virt:denbiProjectMembers               | string  | Project Members             | List of Project Members as JSON.                             |

our $STATUS_VALID;                *STATUS_VALID =            \'VALID';
our $STATUS_EXPIRED;              *STATUS_EXPIRED =          \'EXPIRED';
our $STATUS_SUSPENDED;            *STATUS_SUSPENDED =        \'SUSPENDED';

#urn:perun:member:attribute-def:core:status                           | string  | Status                      | Status of member (VALID,INVALID,EXPIRED,SUSPENDED,DISABLED). |
#urn:perun:user:attribute-def:virt:login-namespace:elixir-persistent  | string  | ELIXIR login                | Login to ELIXIR. It is set automatically with first call.    |
#urn:perun:user_facility:attribute-def:opt:denbiVmsRunning            | hash    | Machines Running            | Collection of user-generated running VMs                     |




my $userStruc = {};
my $groupStruc = {};
my $membershipStruc = {};

my $userStatus = {};
my $userEmail = {};
my $userDisplayName = {};
my $userLogin = {};

my $groupName = {};
my $groupParentId = {};

my $fileUsers = $DIRECTORY . "/users.scim";
my $fileGroups = $DIRECTORY . "/groups.scim";

my $agent = perunServicesInit->getAgent;

foreach my $resourceId ($data->getResourceIds()) {
	foreach my $groupId ($data->getGroupIdsForResource( resource => $resourceId )) {
		processGroups($groupId, $resourceId);
	}
}

# PREPARE USERSDATA TO JSON
my @users = values %$userStruc;
#foreach my $uid (sort keys %$userStruc) {
#        my $user = {};
#        $user->{"id"} = $uid;
#        $user->{"displayName"} = $userStruc->{$uid}->{$userDisplayName};
#        $user->{"status"} = $userStruc->{$uid}->{$userStatus};
#        $user->{"mail"} = $userStruc->{$uid}->{$userEmail};
#        $user->{"login"} = $userStruc->{$uid}->{$userLogin};
#
#        push @users, $user;
#}

# PRINT USERS TO JSON
open FILE_USERS,">$fileUsers" or die "Cannot open $fileUsers: $! \n";
binmode(FILE_USERS);
print FILE_USERS JSON::XS->new->utf8->pretty->canonical->encode(\@users);
close (FILE_USERS) or die "Cannot close $fileUsers: $! \n";

# PREPARE GROUPSDATA TO JSON
my @groups = values %$groupStruc;
#foreach my $gid (sort keys %$groupStruc) {
#        my $group = {};
#        $group->{"id"} = $gid;
#        $group->{"name"} = $groupStruc->{$gid}->{$A_GROUP_NAME};
#        $group->{"parentGroupId"} = $groupStruc->{$gid}->{$A_GROUP_PAR_ID};
#
#        my @members;
#        foreach my $uid (sort keys %{$membershipStruc->{$gid}}){
#                my $struct = {};
#                $struct->{"userId"} = $uid;
#                push @members, $struct;
#        }
#
#        $group->{"members"} = \@members;
#        push @groups, $group;
#}

# PRINT GROUPS TO JSON
open FILE_GROUPS,">$fileGroups" or die "Cannot open $fileGroups: $! \n";
binmode(FILE_GROUPS);
print FILE_GROUPS JSON::XS->new->utf8->pretty->canonical->encode(\@groups);
close (FILE_GROUPS) or die "Cannot close $fileGroups: $! \n";

perunServicesInit::finalize;

##############################################################################
#   Only subs definitions down there
##############################################################################
sub convertToFriendlyName($) {
	my $name = shift;
	$name =~ s/^([^:]+:){5}//;
	return $name;
}

## creates structure for users.scim file
sub processUsers {
	my ($gid, $memberId, $resourceId) = @_;

	my $userTypeAttributes = $data->getAllUserAttributes(member => $memberId);
	my $memberTypeAttributes = $data->getAllMemberAttributes(member => $memberId);
	my $memberResourceTypeAttributes = $data->getAllMemberResourceAttributes(member => $memberId, resource => $resourceId);
	my $userFacilityTypeAttributes = $data->getAllUserFacilityAttributes(member => $memberId, facility => $data->getFacilityId());
	my $memberGroupTypeAttributes = $data->getAllMemberGroupAttributes(member => $memberId, group => $gid);

	my %memberAttributes = ($userTypeAttributes ? %{$userTypeAttributes} : (),
							$memberTypeAttributes ? %{$memberTypeAttributes} : (),
							$memberResourceTypeAttributes ? %{$memberResourceTypeAttributes} : (),
							$userFacilityTypeAttributes ? %{$userFacilityTypeAttributes} : (),
							$memberGroupTypeAttributes ? %{$memberGroupTypeAttributes} : ());
	my $uid = $data->getUserAttributeValue( member => $memberId, attrName => $A_USER_ID );
	my $status = $data->getMemberAttributeValue( member => $memberId, attrName => $A_USER_STATUS );

	if($status ne $STATUS_VALID) { return; }

	unless($userStruc->{$uid}) {
		%memberAttributes = map { convertToFriendlyName($_) => $memberAttributes{$_} } keys %memberAttributes;
		$userStruc->{$uid} = \%memberAttributes;
	}

	processMemberships $gid, $uid;
}

## creates structure for groups.scim file
sub processGroups {
	my ($groupId, $resourceId) = @_;
	my %groupAttributes= %{$data->getAllGroupAttributes(group => $groupId)};

	my $gid = $groupAttributes{$A_GROUP_ID};

	#convert attributes data
	$groupAttributes{$A_GROUP_DENBI_PROJECT_MEMBERS} = decode_json $groupAttributes{$A_GROUP_DENBI_PROJECT_MEMBERS};

	unless(exists $groupStruc->{$gid}) {
		%groupAttributes = map { convertToFriendlyName($_) => $groupAttributes{$_} } keys %groupAttributes;
		$groupStruc->{$gid} = \%groupAttributes;
	}

	foreach my $memberId ( $data->getMemberIdsForResourceAndGroup( group=> $groupId, resource => $resourceId ) ) {
		processUsers $gid, $memberId, $resourceId;
	}
}

## creates structure for memberships
sub processMemberships {
	my ($gid, $uid) = @_;

	unless(exists $membershipStruc->{$gid}->{$uid}) {
		$membershipStruc->{$gid}->{$uid} = {};
	}
}
