int main(int argc, char * argv[])
{
    black_hole_solver_instance_t * solver;
    char board[MAX_LEN_BOARD_STRING];
    int error_line_num;
    int ret, solver_ret_code;
    char * filename = NULL;
    FILE * fh;
    int arg_idx;
    long max_iters_limit = -1;

    if (black_hole_solver_create(&solver))
    {
        fprintf(stderr, "%s\n", "Could not initialise solver (out-of-memory)");
        exit(-1);
    }

    arg_idx = 1;
    if (argc > arg_idx)
    {
        if (!strcmp(argv[arg_idx], "--max-iters"))
        {
            arg_idx++;
            if (argc == arg_idx)
            {
                fprintf(stderr, "Error! --max-iters requires an arguments.\n");
                exit(-1);
            }
            max_iters_limit = atol(argv[arg_idx++]);
        }
    }

    black_hole_solver_set_max_iters_limit(solver, max_iters_limit);
    
    if (argc > arg_idx)
    {
        if (strcmp(argv[arg_idx], "-"))
        {
            filename = argv[arg_idx];
        }
        arg_idx++;
    }

    if (filename)
    {
        fh = fopen(filename, "rt");
    }
    else
    {
        fh = stdin;
    }

    fread(board, sizeof(board[0]), MAX_LEN_BOARD_STRING, fh);

    if (filename)
    {
        fclose(fh);
    }

    board[MAX_LEN_BOARD_STRING-1] = '\0';

    if (black_hole_solver_read_board(
        solver,
        board,
        &error_line_num
        ))
    {
        fprintf(stderr, "Error reading the board at line No. %d!\n", error_line_num);
        exit(-1);
    }

    ret = 0;

    solver_ret_code = black_hole_solver_run(solver);

    if (!solver_ret_code)
    {
        int col_idx, card_rank, card_suit;
        int next_move_ret_code;

        printf("Solved!\n");

        while ((next_move_ret_code = black_hole_solver_get_next_move(
            solver,
            &col_idx,
            &card_rank,
            &card_suit
            )) == BLACK_HOLE_SOLVER__SUCCESS)
        {
            printf ("Move a card from stack %d to the foundations\n\n" 
                "Info: Card moved is %c%c\n\n\n====================\n\n",
                col_idx,
                (("0A23456789TJQK")[card_rank]), ("HCDS")[card_suit]
            );
        }

        if (next_move_ret_code != BLACK_HOLE_SOLVER__END)
        {
            fprintf(
                stderr, 
                "%s - %d\n",
                "Get next move routine returned the wrong error code.",
                next_move_ret_code
            );
            ret = -1;
        }
    }
    else if (solver_ret_code == BLACK_HOLE_SOLVER__OUT_OF_ITERS)
    {
        printf("Intractable!\n");
        ret = -2;
    }
    else
    {
        printf("Unsolved!\n");
        ret = -1;
    }

    printf("\n\n--------------------\n" 
        "Total number of states checked is %ld.\n"
        "This scan generated %ld states.\n",
        black_hole_solver_get_iterations_num(solver),
        black_hole_solver_get_num_states_in_collection(solver)
    );

    black_hole_solver_free(solver);

    return ret;
}
int main(int argc, char * argv[])
{
    black_hole_solver_instance_t * solver;
    char board[MAX_LEN_BOARD_STRING];
    int error_line_num;
    int ret, solver_ret_code;
    char * filename = NULL;
    FILE * fh;
    int arg_idx;
    long max_iters_limit = -1;
    long iters_display_step = 0;
    enum GAME_TYPE game_type = GAME__UNKNOWN;
    fcs_bool_t display_boards = FALSE;
    fcs_bool_t is_rank_reachability_prune_enabled = FALSE;

    arg_idx = 1;
    while (argc > arg_idx)
    {
        if (!strcmp(argv[arg_idx], "--version"))
        {
            printf("black-hole-solver version %s\nLibrary version %s\n",
                VERSION,
                VERSION
            );
            exit(0);
        }
        else if (!strcmp(argv[arg_idx], "--help"))
        {
            printf ("%s", help_text);
            exit(0);
        }
        else if (!strcmp(argv[arg_idx], "--max-iters"))
        {
            arg_idx++;
            if (argc == arg_idx)
            {
                fprintf(stderr, "Error! --max-iters requires an arguments.\n");
                exit(-1);
            }
            max_iters_limit = atol(argv[arg_idx++]);
        }
        else if (!strcmp(argv[arg_idx], "--game"))
        {
            arg_idx++;
            if (argc == arg_idx)
            {
                fprintf(stderr, "Error! --game requires an arguments.\n");
                exit(-1);
            }
            char * g = argv[arg_idx++];

            if (!strcmp(g, "black_hole"))
            {
                game_type = GAME__BH;
            }
            else if (!strcmp(g, "all_in_a_row"))
            {
                game_type = GAME__ALL;
            }
            else
            {
                fprintf(stderr, "%s\n", "Error! --game should be either \"black_hole\" or \"all_in_a_row\".");
                exit(-1);
            }
        }
        else if (!strcmp(argv[arg_idx], "--display-boards"))
        {
            arg_idx++;
            display_boards = TRUE;
        }
        else if (!strcmp(argv[arg_idx], "--rank-reach-prune"))
        {
            arg_idx++;
            is_rank_reachability_prune_enabled = TRUE;
        }
        else if (!strcmp(argv[arg_idx], "--iters-display-step"))
        {
            arg_idx++;
            if (argc == arg_idx)
            {
                fprintf(stderr, "Error! --iters-display-step requires an arguments.\n");
                exit(-1);
            }
            iters_display_step = atol(argv[arg_idx++]);

            if (iters_display_step < 0)
            {
                fprintf(stderr, "Error! --iters-display-step should be positive or zero.\n");
                exit(-1);
            }
        }
        else
        {
            break;
        }
    }

    if (black_hole_solver_create(&solver))
    {
        fprintf(stderr, "%s\n", "Could not initialise solver (out-of-memory)");
        exit(-1);
    }

    if (game_type == GAME__UNKNOWN)
    {
        fprintf(stderr, "%s\n", "Error! Must specify game type using --game.");
        exit(-1);
    }

    black_hole_solver_set_max_iters_limit(solver, max_iters_limit);
    black_hole_solver_set_iters_display_step(solver, iters_display_step);
    black_hole_solver_enable_rank_reachability_prune(
        solver,
        is_rank_reachability_prune_enabled
    );

    if (argc > arg_idx)
    {
        if (strcmp(argv[arg_idx], "-"))
        {
            filename = argv[arg_idx];
        }
        arg_idx++;
    }

    if (filename)
    {
        fh = fopen(filename, "rt");
    }
    else
    {
        fh = stdin;
    }

    fread(board, sizeof(board[0]), MAX_LEN_BOARD_STRING, fh);

    if (filename)
    {
        fclose(fh);
    }

    board[MAX_LEN_BOARD_STRING-1] = '\0';

    if (black_hole_solver_read_board(
        solver,
        board,
        &error_line_num,
        (
            (game_type == GAME__BH)
            ? BHS__BLACK_HOLE__NUM_COLUMNS
            : BHS__ALL_IN_A_ROW__NUM_COLUMNS
        ),
        (
            (game_type == GAME__BH)
            ? BHS__BLACK_HOLE__MAX_NUM_CARDS_IN_COL
            : BHS__ALL_IN_A_ROW__MAX_NUM_CARDS_IN_COL
        ),
        (
            (game_type == GAME__BH)
            ? BHS__BLACK_HOLE__BITS_PER_COL
            : BHS__ALL_IN_A_ROW__BITS_PER_COL
        )
        )
    )
    {
        fprintf(stderr, "Error reading the board at line No. %d!\n", error_line_num);
        exit(-1);
    }

    ret = 0;

    solver_ret_code = black_hole_solver_run(solver);

    if (!solver_ret_code)
    {
        int col_idx, card_rank, card_suit;
        int next_move_ret_code;

        printf("%s\n", "Solved!");

        out_board(solver, display_boards);

        while ((next_move_ret_code = black_hole_solver_get_next_move(
            solver,
            &col_idx,
            &card_rank,
            &card_suit
            )) == BLACK_HOLE_SOLVER__SUCCESS)
        {
            printf ("Move a card from stack %d to the foundations\n\n"
                "Info: Card moved is %c%c\n\n\n====================\n\n",
                col_idx,
                (("0A23456789TJQK")[card_rank]), ("HCDS")[card_suit]
            );

            out_board(solver, display_boards);
        }

        if (next_move_ret_code != BLACK_HOLE_SOLVER__END)
        {
            fprintf(
                stderr,
                "%s - %d\n",
                "Get next move routine returned the wrong error code.",
                next_move_ret_code
            );
            ret = -1;
        }
    }
    else if (solver_ret_code == BLACK_HOLE_SOLVER__OUT_OF_ITERS)
    {
        printf("%s\n", "Intractable!");
        ret = -2;
    }
    else
    {
        printf("%s\n", "Unsolved!");
        ret = -1;
    }

    printf("\n\n--------------------\n"
        "Total number of states checked is %ld.\n"
        "This scan generated %ld states.\n",
        black_hole_solver_get_iterations_num(solver),
        black_hole_solver_get_num_states_in_collection(solver)
    );

    black_hole_solver_free(solver);

    return ret;
}