#!/usr/bin/perl

use strict;
use warnings;
use perunServicesInit;
use perunServicesUtils;
use Perun::Agent;

# temp:
use Text::Unidecode;

our $SERVICE_NAME = "pbs_publication_fairshare";
our $PROTOCOL_VERSION = "3.0.0";

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

# Constants
our $HOW_OLD_PUBLICATIONS;         *HOW_OLD_PUBLICATIONS       =   \3;   #in years
our $A_USER_LOGIN;                 *A_USER_LOGIN               =   \'urn:perun:user_facility:attribute-def:virt:login';
our $A_USER_ID;                    *A_USER_ID                  =   \'urn:perun:user:attribute-def:core:id';
our $A_RESOURCE_FAIRSHARE_GNAME;   *A_RESOURCE_FAIRSHARE_GNAME =   \'urn:perun:resource:attribute-def:def:fairshareGroupName';
our $A_MEMBER_STATUS;              *A_MEMBER_STATUS            =   \'urn:perun:member:attribute-def:core:status';

my $nowYear = (localtime)[5] + 1900;

#CABINET CALLINGS
my $agent = Perun::Agent->new();
my $cabinetAgent = $agent->getCabinetAgent;

# categories
my %categoriesRanks = ();
$categoriesRanks{$_->getId} = $_->getRank foreach($cabinetAgent->findAllCategories);

# get all authors from cabinet
my $authorsByID = ();
my @authors = $cabinetAgent->findAllAuthors;
foreach my $author (@authors) {
	$authorsByID->{$author->getId} = $author;
}

# load users which are not in fairshare group
my $users = {};
# load resources which are fairshare groups
my $resources = {};

foreach my $resourceId ($data->getResourceIds()) {

	my $fairShareGroupName = $data->getResourceAttributeValue(resource => $resourceId, attrName => $A_RESOURCE_FAIRSHARE_GNAME);

	if ($fairShareGroupName) {

		# this resource is a fairshare group
		$resources->{$resourceId}->{"weight"} = 1.0;
		$resources->{$resourceId}->{"name"} = "G:" . $fairShareGroupName;

		my $publicationsIDs = ();

		foreach my $memberId ($data->getMemberIdsForResource(resource => $resourceId)) {

			# process only VALID members
			my $status = $data->getMemberAttributeValue(member => $memberId, attrName => $A_MEMBER_STATUS);
			next unless ($status eq "VALID");

			my $userId = $data->getUserIdForMember(member => $memberId);
			my $userLogin = $data->getUserFacilityAttributeValue(member => $memberId, attrName => $A_USER_LOGIN);

			$resources->{$resourceId}->{"weight"}++;

			die if($users->{$userId} && $users->{$userId}->{"group"} ne 'root');

			$users->{$userId}->{"login"} = $userLogin;
			$users->{$userId}->{"weight"} = 1.0;
			$users->{$userId}->{"group"} = "G:" . $fairShareGroupName;

			# skip processing publications for users without any publication
			next unless defined $authorsByID->{$userId};

			# gather all users publications for later processing
			my @publications = $cabinetAgent->findPublicationsByFilter(
				userId => $userId,
				yearSince => ($nowYear - $HOW_OLD_PUBLICATIONS),
				yearTill => $nowYear);

			for my $pub (@publications) {
				$publicationsIDs->{$pub->getId} = $pub;
			}

		}

		# process each publication of multiple authors only once !!
		foreach my $publication (values %$publicationsIDs) {
			#### Start of fairshare algorithm ####
			my $pubWeight = $categoriesRanks{$publication->getCategoryId} * (1 - (($nowYear - $publication->getYear - 1) / $HOW_OLD_PUBLICATIONS ));
			$resources->{$resourceId}->{"weight"} += $pubWeight;
			#### End of fairshare algorithm ####
		}

	} else {

		# this resource is not a fairshare group
		foreach my $memberId ($data->getMemberIdsForResource(resource => $resourceId)) {

			#if user is already there, we can skip him (take the one from the fairshare group instead)
			my $userId = $data->getUserIdForMember(member => $memberId);
			next if($users->{$userId});

			# process only VALID members
			my $status = $data->getMemberAttributeValue(member => $memberId, attrName => $A_MEMBER_STATUS);
			next unless ($status eq "VALID");

			$users->{$userId}->{"login"} = $data->getUserFacilityAttributeValue(member => $memberId, attrName => $A_USER_LOGIN);
			$users->{$userId}->{"weight"} = 1.0;
			$users->{$userId}->{"group"} = 'root';

		}

	}

}

# Count all 'root' group users fairshares
for my $author (@authors) {
	next unless defined $users->{$author->getId}; #filter out users which are not assigned on the facility for which this script is executed right now
	next unless ($users->{$author->getId}->{'group'} eq 'root');

	## get all publications of author
	my @publications = $cabinetAgent->findPublicationsByFilter(
		userId => $author->getId,
		yearSince => ($nowYear - $HOW_OLD_PUBLICATIONS),
		yearTill => $nowYear);

	for my $publication (@publications) {

		#### Start of fairshare algorithm ####
		my $pubWeight = $categoriesRanks{$publication->getCategoryId} * (1 - (($nowYear - $publication->getYear - 1) / $HOW_OLD_PUBLICATIONS ));
		$users->{$author->getId}->{"weight"} += $pubWeight;
		push @{$users->{$author->getId}->{"pubs"}}, $pubWeight;
		#### End of fairshare algorithm ####

	}

}

# start uid must be bigger than 1 so for example 10
my $uid = 10;
my $fileName = "$DIRECTORY/$::SERVICE_NAME";
open FILE,">$fileName" or die "Cannot open $fileName: $!";

# first groups
for my $resourceRef (sort { $b->{"weight"} <=> $a->{"weight"} || $a->{"name"} cmp $b->{"name"} } values %$resources) {
	printf FILE "%s\t%d\t%s\t%.0f\n", $resourceRef->{"name"}, $uid, 'root', $resourceRef->{"weight"};
	$uid++;
}

# then users
for my $userRef (sort { $b->{"weight"} <=> $a->{"weight"} || $a->{"login"} cmp $b->{"login"} } values %$users) {
	printf FILE "%s\t%d\t%s\t%.0f\n", $userRef->{"login"}, $uid, $userRef->{"group"}, $userRef->{"weight"};
	$uid++;
}

close (FILE);
perunServicesInit::finalize;
