// This file is part of BOINC.
// https://boinc.berkeley.edu
// Copyright (C) 2026 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.

// assimilator for single jobs.
// - if success, move the output file(s) to job directory
// - delete job description file
// - delete WU template file

#include <stdlib.h>
#include <string.h>
#include <string>
#include <unistd.h>
#include <vector>
#include <sys/resource.h>

#include "boinc_db.h"
#include "error_numbers.h"
#include "filesys.h"
#include "str_replace.h"

#include "sched_msgs.h"
#include "sched_config.h"
#include "sched_util.h"
#include "validate_util.h"

using std::vector;
using std::string;

int assimilate_handler_init(int, char**) {
    return 0;
}

void assimilate_handler_usage() {
    // describe the project specific arguments here
    //fprintf(stderr,
    //    "    Custom options:\n"
    //    "    [--project_option X]  a project specific option\n"
    //);
}

int assimilate_handler(
    WORKUNIT& wu, vector<RESULT>& /*results*/, RESULT& canonical_result
) {
    int retval;
    char buf[1024], filename[MAXPATHLEN], job_dir[MAXPATHLEN], job_dir_file[MAXPATHLEN];

    // delete the template files
    //
    unlink(config.project_path("templates/sj_wu_template_%d", wu.id));
    unlink(config.project_path("templates/sj_result_template_%d", wu.id));

    // read and delete the job directory file
    //
    sprintf(filename, "sj_%lu", wu.id);
    dir_hier_path(
        filename, config.upload_dir, config.uldl_dir_fanout, job_dir_file
    );
    FILE* f = fopen(job_dir_file, "r");
    if (!f) {
        log_messages.printf(MSG_CRITICAL, "Can't open job file %s\n", job_dir_file);
        return 0;
    }
    if (!fgets(buf, 1024, f)) {
        log_messages.printf(MSG_CRITICAL, "Can't read job file %s\n", job_dir_file);
        fclose(f);
        return 0;
    }
    fclose(f);
    unlink(job_dir_file);

    // parse the job directory file
    //
    char* p = strstr(buf, "<job_dir>");
    if (!p) {
        log_messages.printf(MSG_CRITICAL, "garbage in job file: %s\n", buf);
        return 0;
    }
    safe_strcpy(job_dir, buf+strlen("<job_dir>"));
    p = strstr(job_dir, "</job_dir>");
    if (!p) {
        log_messages.printf(MSG_CRITICAL, "garbage in job file: %s\n", buf);
        return 0;
    }
    *p = 0;

    // Create a job summary file
    //
    sprintf(filename, "%s/job_summary_%lu", job_dir, wu.id);
    f = fopen(filename, "w");
    if (!f) {
        log_messages.printf(MSG_CRITICAL, "Can't open job summary file %s\n", filename);
        return ERR_FOPEN;
    }

    // If job was successful, copy the output files
    //
    if (wu.canonical_resultid) {
        fprintf(f,
            "Job was completed by host %lu.\n"
            "CPU time: %f seconds\n",
            canonical_result.hostid,
            canonical_result.cpu_time
        );
        vector<OUTPUT_FILE_INFO> output_files;
        char copy_path[MAXPATHLEN];
        get_output_file_infos(canonical_result, output_files);
        for (const OUTPUT_FILE_INFO& fi: output_files) {
            string logical_name;
            retval = get_logical_name(canonical_result, fi.path, logical_name);
            if (retval) {
                fprintf(f,
                    "Couldn't get logical name for %s: %s\n",
                    fi.path.c_str(), boincerror(retval)
                );
                continue;
            }
            sprintf(copy_path, "%s/%s", job_dir, logical_name.c_str());
            retval = boinc_copy(fi.path.c_str() , copy_path);
            if (retval) {
                fprintf(f,
                    "Output file %s not present.\n", logical_name.c_str()
                );
                continue;
            }
        }
    } else {
        fprintf(f,
            "The job was not successfully completed.\n"
            "Error: 0x%x\n", wu.error_mask
        );
    }
    fclose(f);
    return 0;
}
