/*****************************************************************************\
 *  user_report_functions.c - Interface to functions dealing with user reports.
 ******************************************************************************
 *  Copyright (C) 2010 Lawrence Livermore National Security.
 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
 *  Written by Danny Auble da@llnl.gov, et. al.
 *  CODE-OCEC-09-009. All rights reserved.
 *
 *  This file is part of Slurm, a resource management program.
 *  For details, see <https://slurm.schedmd.com/>.
 *  Please also read the included file: DISCLAIMER.
 *
 *  Slurm 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 of the License, or (at your option)
 *  any later version.
 *
 *  In addition, as a special exception, the copyright holders give permission
 *  to link the code of portions of this program with the OpenSSL library under
 *  certain conditions as described in each individual source file, and
 *  distribute linked combinations including the two. You must obey the GNU
 *  General Public License in all respects for all of the code used other than
 *  OpenSSL. If you modify file(s) with this exception, you may extend this
 *  exception to your version of the file(s), but you are not obligated to do
 *  so. If you do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source files in
 *  the program, then also delete it here.
 *
 *  Slurm 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.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with Slurm; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
\*****************************************************************************/

#include "slurm/slurm.h"
#include "slurm/slurm_errno.h"
#include "slurm/slurmdb.h"

#include "src/common/slurmdb_defs.h"
#include "src/interfaces/accounting_storage.h"
#include "src/common/xstring.h"

extern list_t *slurmdb_report_user_top_usage(void *db_conn,
					     slurmdb_user_cond_t *user_cond,
					     bool group_accounts)
{
	list_t *cluster_list = NULL;
	list_itr_t *itr = NULL, *itr2 = NULL, *itr3 = NULL;
	list_itr_t *cluster_itr = NULL;
	slurmdb_cluster_cond_t cluster_cond;
	list_t *user_list = NULL;
	list_t *usage_cluster_list = NULL;
	char *object = NULL;
	int exit_code = 0;
	slurmdb_user_rec_t *user = NULL;
	slurmdb_cluster_rec_t *cluster = NULL;
	slurmdb_assoc_rec_t *assoc = NULL;
	slurmdb_report_user_rec_t *slurmdb_report_user = NULL;
	slurmdb_report_cluster_rec_t *slurmdb_report_cluster = NULL;
	uid_t my_uid = getuid();
	bool delete_user_cond = 0, delete_assoc_cond = 0,
		delete_cluster_list = 0;
	time_t start_time, end_time;

	if (!user_cond) {
		delete_user_cond = 1;
		user_cond = xmalloc(sizeof(slurmdb_user_cond_t));
	}

	if (!user_cond->assoc_cond) {
		delete_assoc_cond = 1;
		user_cond->assoc_cond =
			xmalloc(sizeof(slurmdb_assoc_cond_t));
	}

	if (!user_cond->assoc_cond->cluster_list) {
		delete_cluster_list = 1;
		user_cond->assoc_cond->cluster_list = list_create(xfree_ptr);
	}

	user_cond->with_deleted = 1;
	user_cond->with_assocs = 1;
	user_cond->assoc_cond->flags = ASSOC_COND_FLAG_WITH_NG_USAGE |
		ASSOC_COND_FLAG_WOPI;

	/* This needs to be done on some systems to make sure
	   assoc_cond isn't messed up.  This has happened on some 64
	   bit machines and this is here to be on the safe side.
	*/
	start_time = user_cond->assoc_cond->usage_start;
	end_time = user_cond->assoc_cond->usage_end;
	slurmdb_report_set_start_end_time(&start_time, &end_time);
	user_cond->assoc_cond->usage_start = start_time;
	user_cond->assoc_cond->usage_end = end_time;

	user_list = acct_storage_g_get_users(db_conn, my_uid, user_cond);
	if (!user_list) {
		exit_code=1;
		fprintf(stderr, " Problem with user query.\n");
		goto end_it;
	}

	/* We have to get the clusters here or we will be unable to
	   get the correct total time for the cluster if associations
	   are not enforced.
	*/
	slurmdb_init_cluster_cond(&cluster_cond, 0);
	cluster_cond.with_usage = 1;
	cluster_cond.with_deleted = 1;
	cluster_cond.usage_end = user_cond->assoc_cond->usage_end;
	cluster_cond.usage_start = user_cond->assoc_cond->usage_start;
	cluster_cond.cluster_list = user_cond->assoc_cond->cluster_list;

	usage_cluster_list = acct_storage_g_get_clusters(
		db_conn, my_uid, &cluster_cond);
	if (!usage_cluster_list) {
		exit_code=1;
		fprintf(stderr, " Problem with cluster query.\n");
		goto end_it;
	}

	cluster_list = list_create(slurmdb_destroy_report_cluster_rec);

	itr = list_iterator_create(usage_cluster_list);
	while((cluster = list_next(itr))) {
		/* check to see if this cluster is around during the
		   time we are looking at */
		if (!cluster->accounting_list
		   || !list_count(cluster->accounting_list))
			continue;

		slurmdb_report_cluster = slurmdb_cluster_rec_2_report(cluster);

		list_append(cluster_list, slurmdb_report_cluster);

		slurmdb_report_cluster->user_list =
			list_create(slurmdb_destroy_report_user_rec);
	}
	list_iterator_destroy(itr);
	FREE_NULL_LIST(usage_cluster_list);

	itr = list_iterator_create(user_list);
	cluster_itr = list_iterator_create(cluster_list);
	while((user = list_next(itr))) {
		struct passwd *passwd_ptr = NULL;
		if (!user->assoc_list || !list_count(user->assoc_list))
			continue;

		passwd_ptr = getpwnam(user->name);
		if (passwd_ptr)
			user->uid = passwd_ptr->pw_uid;
		else
			user->uid = NO_VAL;

		itr2 = list_iterator_create(user->assoc_list);
		while((assoc = list_next(itr2))) {

			if (!assoc->accounting_list
			   || !list_count(assoc->accounting_list))
				continue;

			while((slurmdb_report_cluster =
			       list_next(cluster_itr))) {
				if (!xstrcmp(slurmdb_report_cluster->name,
					     assoc->cluster)) {
					list_itr_t *user_itr = NULL;
					if (!group_accounts) {
						slurmdb_report_user = NULL;
						goto new_user;
					}
					user_itr = list_iterator_create(
						slurmdb_report_cluster->
						user_list);
					while((slurmdb_report_user
					       = list_next(user_itr))) {
						if (slurmdb_report_user->uid
						   != NO_VAL) {
							if (slurmdb_report_user->
							   uid
							   == user->uid)
								break;
						} else if (slurmdb_report_user->
							  name
							  && !xstrcasecmp(
								  slurmdb_report_user->
								  name,
								  user->name))
							break;
					}
					list_iterator_destroy(user_itr);
				new_user:
					if (!slurmdb_report_user) {
						slurmdb_report_user = xmalloc(
							sizeof
							(slurmdb_report_user_rec_t));
						slurmdb_report_user->name =
							xstrdup(assoc->user);
						slurmdb_report_user->uid =
							user->uid;
						slurmdb_report_user->acct_list =
							list_create(xfree_ptr);
						slurmdb_report_user->partition =
							xstrdup(assoc->partition);
						list_append(slurmdb_report_cluster->
							    user_list,
							    slurmdb_report_user);

						/*
						 * In order to know that
						 * partition associations exist
						 * for reporting we need to add
						 * an empty partition list to
						 * user_cond.
						 */
						if (slurmdb_report_user->
						    partition &&
						    !user_cond->assoc_cond->
						    partition_list) {
							user_cond->assoc_cond->
								partition_list =
								list_create(NULL);
						}
					}
					break;
				}
			}
			if (!slurmdb_report_cluster) {
				error("This cluster '%s' hasn't "
				      "registered yet, but we have jobs "
				      "that ran?", assoc->cluster);
				slurmdb_report_cluster =
					xmalloc(sizeof(slurmdb_report_cluster_rec_t));
				list_append(cluster_list, slurmdb_report_cluster);

				slurmdb_report_cluster->name = xstrdup(assoc->cluster);
				slurmdb_report_cluster->user_list =
					list_create(slurmdb_destroy_report_user_rec);
				slurmdb_report_user =
					xmalloc(sizeof(slurmdb_report_user_rec_t));
				slurmdb_report_user->name = xstrdup(assoc->user);
				slurmdb_report_user->uid = user->uid;
				slurmdb_report_user->acct_list =
					list_create(xfree_ptr);
				list_append(slurmdb_report_cluster->user_list,
					    slurmdb_report_user);
			}
			list_iterator_reset(cluster_itr);

			itr3 = list_iterator_create(
				slurmdb_report_user->acct_list);
			while((object = list_next(itr3))) {
				if (!xstrcmp(object, assoc->acct))
					break;
			}
			list_iterator_destroy(itr3);

			if (!object)
				list_append(slurmdb_report_user->acct_list,
					    xstrdup(assoc->acct));
			slurmdb_transfer_acct_list_2_tres(
				assoc->accounting_list,
				&slurmdb_report_user->tres_list);
		}
		list_iterator_destroy(itr2);
	}
	list_iterator_destroy(itr);
	list_iterator_destroy(cluster_itr);

end_it:
	if (delete_cluster_list) {
		FREE_NULL_LIST(user_cond->assoc_cond->cluster_list);
		user_cond->assoc_cond->cluster_list = NULL;
	}

	if (delete_assoc_cond) {
		slurmdb_destroy_assoc_cond(user_cond->assoc_cond);
		user_cond->assoc_cond = NULL;
	}

	if (delete_user_cond) {
		slurmdb_destroy_user_cond(user_cond);
		user_cond = NULL;
	}

	FREE_NULL_LIST(user_list);

	if (exit_code) {
		FREE_NULL_LIST(cluster_list);
	}

	return cluster_list;
}
