// This file is part of Freecell Solver. It is subject to the license terms in
// the COPYING.txt file found in the top-level directory of this distribution
// and at http://fc-solve.shlomifish.org/docs/distro/COPYING.html . No part of
// Freecell Solver, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the COPYING file.
//
// Copyright (c) 2000 Shlomi Fish
// serial_range_solver.c - the Freecell Solver range solver.
//
// Also see:
//  - fc_pro_range_solver.c - range solver that can count the moves in
//  the solution and display the FC-Pro ones.
//  - threaded_range_solver.c - solve different boards in several POSIX
//  threads.
#include "default_iter_handler.h"
#include "cl_callback_range.h"
#include "trace_mem.h"
#include "try_param.h"
#include "range_solvers.h"
#include "print_time.h"

static inline int range_solvers_main(int argc, char *argv[], int arg,
    fc_solve_ms_deal_idx_type start_board, fc_solve_ms_deal_idx_type end_board,
    const fc_solve_ms_deal_idx_type stop_at)
{
    fcs_iters_int total_num_iters = 0;
#ifndef FCS_WITHOUT_MAX_NUM_STATES
    bool was_total_iterations_limit_per_board_set = false;
#endif
    fcs_int_limit_t total_iterations_limit_per_board = -1;
    fcs_binary_output binary_output = INIT_BINARY_OUTPUT;
    const char *solutions_directory = NULL;
    char *solution_fn = NULL;

    for (; arg < argc; arg++)
    {
        const char *param;
        if ((param = TRY_P("--binary-output-to")))
        {
            binary_output.filename = param;
        }
        else if ((param = TRY_P("--total-iterations-limit")))
        {
#ifndef FCS_WITHOUT_MAX_NUM_STATES
            was_total_iterations_limit_per_board_set = true;
            total_iterations_limit_per_board = (fcs_int_limit_t)atol(param);
#endif
        }
        else if ((param = TRY_P("--solutions-directory")))
        {
            solutions_directory = param;
            solution_fn =
                SREALLOC(solution_fn, strlen(solutions_directory) + 1024);
        }
        else
        {
            break;
        }
    }

    fc_solve_print_started_at();
    fflush(stdout);

    fc_solve_display_information_context display_context =
        INITIAL_DISPLAY_CONTEXT;
    void *const instance = alloc_instance_and_parse(argc, argv, &arg,
        known_parameters, cmd_line_callback, &display_context, true);

    bin_init(&binary_output, &start_board, &end_board,
        &total_iterations_limit_per_board);

#ifndef FCS_WITHOUT_MAX_NUM_STATES
    if (was_total_iterations_limit_per_board_set)
    {
        freecell_solver_user_limit_iterations_long(
            instance, total_iterations_limit_per_board);
    }
#endif

    fcs_state_string state_string;
    get_board__setup_string(state_string);
    for (fc_solve_ms_deal_idx_type board_num = start_board;
        board_num <= end_board; ++board_num)
    {
        get_board_l__without_setup(board_num, state_string);

        const int ret =
            freecell_solver_user_solve_board(instance, state_string);

        switch (ret)
        {
        case FCS_STATE_SUSPEND_PROCESS:
            fc_solve_print_intractable(board_num);
            print_int(&binary_output, -1);
            break;

        case FCS_STATE_FLARES_PLAN_ERROR:
            print_flares_plan_error(instance);
            goto out_of_loop;

        case FCS_STATE_IS_NOT_SOLVEABLE:
            fc_solve_print_unsolved(board_num);
            print_int(&binary_output, -2);
            break;

        case FCS_STATE_WAS_SOLVED:
#ifdef FCS_RANGE_SOLVERS_PRINT_SOLVED
            printf("Solved Board No. " RIN_ULL_FMT "\n", board_num);
#endif
            print_int(&binary_output,
                (int)freecell_solver_user_get_num_times_long(instance));
            break;

        default:
            exit_error("%s", "Unknown ret code!");
        }

#ifndef WIN32
// #define RIN_ULL6_FMT "%.6llu"
#define RIN_ULL9_FMT "%09llu"
#else
// #define RIN_ULL6_FMT "%.6I64u"
#define RIN_ULL9_FMT "%09I64u"
#endif
        if (solutions_directory)
        {
            sprintf(solution_fn, "%s" RIN_ULL9_FMT ".sol", solutions_directory,
                board_num);
            FILE *const output_fh = fopen(solution_fn, "wt");
            if (!output_fh)
            {
                exit_error("Could not open output file '%s' for writing!",
                    solution_fn);
            }
            fc_solve_output_result_to_file(
                output_fh, instance, ret, &display_context);
            fclose(output_fh);
        }
        total_num_iters += freecell_solver_user_get_num_times_long(instance);

        if (board_num % stop_at == 0)
        {
            fc_solve_print_reached(board_num, total_num_iters);
        }
        freecell_solver_user_recycle(instance);
        trace_mem(board_num);
    }
out_of_loop:

    freecell_solver_user_free(instance);
    bin_close(&binary_output);
    free(solution_fn);
    solution_fn = NULL;

    return 0;
}
