#!/usr/bin/perl
use strict;
use warnings;
use perunServicesInit;
use perunServicesUtils;
use Perun::Agent;
use JSON::XS;
use Tie::IxHash;

our $SERVICE_NAME = "pbsmon_json";
our $PROTOCOL_VERSION = "3.0.0";
my $SCRIPT_VERSION = "3.0.2";

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


#Constants
our $A_FACILITY_NAME;             *A_FACILITY_NAME =              \'urn:perun:facility:attribute-def:def:displayName';
our $A_FACILITY_COMMENT;          *A_FACILITY_COMMENT =           \'urn:perun:facility:attribute-def:def:comment';
our $A_FACILITY_CPU;              *A_FACILITY_CPU =               \'urn:perun:facility:attribute-def:def:cpu';
our $A_FACILITY_DESC;             *A_FACILITY_DESC =              \'urn:perun:facility:attribute-def:def:desc';
our $A_FACILITY_DISK;             *A_FACILITY_DISK =              \'urn:perun:facility:attribute-def:def:disk';
our $A_FACILITY_MEMORY;           *A_FACILITY_MEMORY =            \'urn:perun:facility:attribute-def:def:memory';
our $A_FACILITY_NETWORK;          *A_FACILITY_NETWORK =           \'urn:perun:facility:attribute-def:def:network';
our $A_FACILITY_OWNER;            *A_FACILITY_OWNER =             \'urn:perun:facility:attribute-def:def:owner';
our $A_FACILITY_PBSMON_SERVER;    *A_FACILITY_PBSMON_SERVER =     \'urn:perun:facility:attribute-def:def:pbsmonServer';
our $A_FACILITY_PHOTO;            *A_FACILITY_PHOTO =             \'urn:perun:facility:attribute-def:def:photo';
our $A_FACILITY_SPEC;             *A_FACILITY_SPEC =              \'urn:perun:facility:attribute-def:def:spec';
our $A_FACILITY_THUMBNAIL;        *A_FACILITY_THUMBNAIL =         \'urn:perun:facility:attribute-def:def:thumbnail';
our $A_FACILITY_LISTING_PRIORITY; *A_FACILITY_LISTING_PRIORITY =  \'urn:perun:facility:attribute-def:def:listingPriority';
our $A_HOST_HOSTNAME;             *A_HOST_HOSTNAME =              \'urn:perun:host:attribute-def:core:hostname';
our $A_HOST_CORE_NUMBER;          *A_HOST_CORE_NUMBER =           \'urn:perun:host:attribute-def:def:coresNumber';
our $A_HOST_FRONTEND;             *A_HOST_FRONTEND =              \'urn:perun:host:attribute-def:def:frontend';
our $A_HOST_RESERVED;             *A_HOST_RESERVED =              \'urn:perun:host:attribute-def:def:reserved';
our $A_FACILITY_IS_CLUSTER;       *A_FACILITY_IS_CLUSTER =        \'urn:perun:facility:attribute-def:def:isCluster';
our $A_R_VO_NAME;                 *A_R_VO_NAME =                  \'urn:perun:resource:attribute-def:virt:voShortName';

#Global data structure
our $struc = {};
$struc->{'physical_machines'} = [];
$struc->{'reserved_machines'} = [];
$struc->{'frontends'} = [];


my $physicalMachinesStruc = {};

my $agent = perunServicesInit->getAgent;
my $facilitiesAgent = $agent->getFacilitiesAgent;
my $servicesAgent = $agent->getServicesAgent;
my $attributesAgent = $agent->getAttributesAgent;

my $service = $servicesAgent->getServiceByName(name => $SERVICE_NAME);


my $pbsmonServer = $agent->getAttributesAgent->getAttribute(attributeName => $A_FACILITY_PBSMON_SERVER, facility => perunServicesInit::getFacility->getId)->getValue;
unless($pbsmonServer) { die "pbsmonServer not specified for facility ".(perunServicesInit::getFacility->getId()); }

if($pbsmonServer eq 'metavo.metacentrum.cz') { $pbsmonServer = 'segin.vm.cesnet.cz'; }  #FIXME

if($pbsmonServer ne perunServicesInit::getFacility->getName) {
	my $pbsmonServerFacility;
	$pbsmonServerFacility = $facilitiesAgent->getFacilityByName(name => $pbsmonServer);

	$servicesAgent->planServicePropagation(service => $service->getId, facility => $pbsmonServerFacility->getId);
	exit 0;
}


my @facilities = $facilitiesAgent->getAssignedFacilities(service => $service->getId);

my @facilitesAndAttrbutes;
foreach my $facility (@facilities) {
	if($facility->getName eq $pbsmonServer) { next; } # don't want generate data for pbsmonServer itself
	my $data = $servicesAgent->getHashedHierarchicalData(service => $service->getId, facility => $facility->getId);

	my @facilityVosShortNames;
	foreach my $resourceId ($data->getResourceIds()) {
		my $voShortName = $data->getResourceAttributeValue( resource => $resourceId, attrName => $A_R_VO_NAME);
		push @facilityVosShortNames, $voShortName;
	}

	@facilityVosShortNames = sort @facilityVosShortNames;
	my %facilityStruc;
	tie %facilityStruc, 'Tie::IxHash';
	%facilityStruc = (    id => $data->getFacilityAttributeValue(attrName => $A_FACILITY_NAME),
		name => $data->getFacilityAttributeValue(attrName => $A_FACILITY_NAME),
		cluster => (defined($data->getFacilityAttributeValue(attrName => $A_FACILITY_IS_CLUSTER)) && $data->getFacilityAttributeValue(attrName => $A_FACILITY_IS_CLUSTER) == 1) ? 'true' : 'false',
		desc => $data->getFacilityAttributeValue(attrName => $A_FACILITY_DESC),
		spec => $data->getFacilityAttributeValue(attrName => $A_FACILITY_SPEC),
		cpudesc => $data->getFacilityAttributeValue(attrName => $A_FACILITY_CPU),
		photo => $data->getFacilityAttributeValue(attrName => $A_FACILITY_PHOTO),
		thumbnail => $data->getFacilityAttributeValue(attrName => $A_FACILITY_THUMBNAIL),
		memory => $data->getFacilityAttributeValue(attrName => $A_FACILITY_MEMORY),
		disk => $data->getFacilityAttributeValue(attrName => $A_FACILITY_DISK),
		network => $data->getFacilityAttributeValue(attrName => $A_FACILITY_NETWORK),
		comment => $data->getFacilityAttributeValue(attrName => $A_FACILITY_COMMENT),
		owner => $data->getFacilityAttributeValue(attrName => $A_FACILITY_OWNER),
		vos => \@facilityVosShortNames,
	);

	my $facilityListingPriority = $data->getFacilityAttributeValue(attrName => $A_FACILITY_LISTING_PRIORITY);
	push @facilitesAndAttrbutes, {
		"facilityStruct" => \%facilityStruc,
		"facilityListingPriority" => $facilityListingPriority,
		"facility" => $facility,
	};
}


for my $item (sort { $b->{"facilityListingPriority"} <=> $a->{"facilityListingPriority"} } @facilitesAndAttrbutes) {
	my $facility = $item->{"facility"};
	my $facilityStruc = $item->{"facilityStruct"};
	my @owners = $facilitiesAgent->getOwners(facility => $facility->getId);

	my $mergedOwner = '';  # if facility have multiple owner merge their names into one, separeter by commas
	for my $owner (@owners) {
		if($owner->getType eq 'administrative') {
			$mergedOwner .= ', ' if($mergedOwner);
			$mergedOwner .= $owner->getName;
		}
	}
	unless(defined $physicalMachinesStruc->{$mergedOwner}) {
		my %ownerStruc = (  id => $mergedOwner,
			name => { cs => $mergedOwner,
				en => $mergedOwner,
			},
			resources => [],
			url =>  { cs => undef,
				en => undef,
			},
			spec => { cs => $mergedOwner,
				en => $mergedOwner,
			}
		);
		$physicalMachinesStruc->{$mergedOwner} = \%ownerStruc;
}

push @{$physicalMachinesStruc->{$mergedOwner}->{'resources'}}, $facilityStruc;


	my @hosts = $facilitiesAgent->getHosts(facility => $facility->getId);
	my @hostsStruc = ();
	for my $host (@hosts) {
		my %hostAttributes = attributesToHash $attributesAgent->getRequiredAttributes(service => $service->getId, host => $host->getId);
		push @hostsStruc, { name => $hostAttributes{$A_HOST_HOSTNAME},
			cpu => $hostAttributes{$A_HOST_CORE_NUMBER},
		};
		push @{$struc->{'frontends'}}, $hostAttributes{$A_HOST_HOSTNAME} if($hostAttributes{$A_HOST_FRONTEND});
		push @{$struc->{'reserved_machines'}}, $hostAttributes{$A_HOST_HOSTNAME} if($hostAttributes{$A_HOST_RESERVED});
	}
	if($facilityStruc->{cluster} eq "false") {
		$facilityStruc->{'cpu'} = $hostsStruc[0]->{'cpu'};
	} else {
		$facilityStruc->{'machines'} = \@hostsStruc;
}
}

for my $key (sort keys %$physicalMachinesStruc) {
	push @{$struc->{'physical_machines'}}, $physicalMachinesStruc->{$key};
}

#push @{$struc->{'physical_machines'}}, values %$physicalMachinesStruc;


my $fileName = "$DIRECTORY/$SERVICE_NAME";
open FILE,">$fileName" or die "Cannot open $fileName: $! \n";
print FILE JSON::XS->new->utf8->pretty->canonical->encode($struc);
close FILE;

perunServicesInit::finalize;
