% -----------------------------------------------------------------------------
%  (C) Altran Praxis Limited
% -----------------------------------------------------------------------------
% 
%  The SPARK toolset is free software; you can redistribute it and/or modify it
%  under terms of the GNU General Public License as published by the Free
%  Software Foundation; either version 3, or (at your option) any later
%  version. The SPARK toolset 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 distributed with the SPARK toolset; see file
%  COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
%  the license.
% 
% =============================================================================

%###############################################################################
% PURPOSE
%-------------------------------------------------------------------------------
% Loads path functions for this session.
%###############################################################################

%###############################################################################
% MODULE
%###############################################################################
:- module(load__pfs, [load_pfs/0]).

%###############################################################################
% DEPENDENCIES
%###############################################################################

:- use_module('data__formats.pro',
              [add_state/2,
               add_type/2]).

:- use_module('data__data_files.pro',
              [get_datafiles_pfs/1]).

:- use_module('data__pfs.pro',
              [add_pfs_action/2,
               add_pfs_pf/4,
               add_pfs_statement/2,
               add_pfs_successor_statement/3,
               add_pfs_traversal_condition/3]).

:- use_module('ioutilities.pro',
              [read_line_from_stream/2,
               read_up_to_number_of_chars_from_stream/3,
               throw_error/2]).

:- use_module('newutilities.pro',
              [unique_atom/2]).

:- use_module('parseutilities.pro',
              [parse_atom_silent/4,
               parse_natural_int/3]).

%###############################################################################
% TYPES
%###############################################################################

:- add_type('ParseStatus',
            [end_of_file,
             notfinished,
             finished]).

%###############################################################################
% DATA
%###############################################################################

:- add_state(get_last_statement_id,
             get_last_statement_id('StatementId_Atom')).
:- dynamic(get_last_statement_id/1).

:- add_state(get_last_successor_statement_id,
             get_last_successor_statement_id('SuccessorStatementId_Atom')).
:- dynamic(get_last_successor_statement_id/1).

:- add_state(get_order,
             get_order('Int')).
:- dynamic(get_order/1).

%###############################################################################
% PREDICATES
%###############################################################################


:- set_prolog_flag(double_quotes, chars).

%===============================================================================
% load_pfs.
%-------------------------------------------------------------------------------
% Loads information from the known pfs file.
%===============================================================================

load_pfs:-
    get_datafiles_pfs(PfsFile_Atom),
    open(PfsFile_Atom, read, Stream),
    process_path_functions(Stream),
    close(Stream),
    !.

%===============================================================================
% process_path_functions(+Stream).
%===============================================================================

process_path_functions(Stream):-
    initialise_order,
    repeat,
    read_line_from_stream(Stream, ReadText),
    process_path_functions_line(Stream, ReadText),
    process_path_functions_x(ReadText),
    !.

%-------------------------------------------------------------------------------

% Finish if at end of file.
process_path_functions_x(end_of_file):-
    !.

% From above, not at end of file.
process_path_functions_x(_ReadLine):-
    fail.

%===============================================================================
% process_path_functions_line(Stream, ReadText).
%===============================================================================

% Do nothing at end of file.
process_path_functions_line(_Stream, end_of_file):-
    !.

% Check for statement line.
process_path_functions_line(_Stream, CharList):-
    scan_for_statement(CharList),
    !.

% Check for successor statement line.
process_path_functions_line(_Stream, CharList):-
    scan_for_successor_statement(CharList),
    !.

% Check for path function.
process_path_functions_line(Stream, CharList):-
    scan_for_path_header(CharList, Number_Int),

    % Record this path function, associating with last found successor
    % statement.
    retrieve_and_increment_order(Order_Int),
    unique_atom('pf', PFId_Atom),
    must_get_last_successor_statement_id(SuccessorStatementId_Atom),
    add_pfs_pf(PFId_Atom,
               Order_Int,
               Number_Int,
               SuccessorStatementId_Atom),

    % The components of this path function are now retrieved.
    process_single_path_function(Stream, PFId_Atom),
    !.

% From above, nothing found at this line.
process_path_functions_line(_Stream, _CharList):-
    !.

%===============================================================================
% scan_for_statement(+CharList).
%===============================================================================

% Make this call visible to the spxref tool.
:- public load__pfs:parse_statement/3.

scan_for_statement(CharList):-
    phrase(parse_statement(PFTraceStatement), CharList),
    unique_atom('stmt', StatementId_Atom),
    add_pfs_statement(StatementId_Atom, PFTraceStatement),
    replace_last_statement_id(StatementId_Atom),
    !.

%-------------------------------------------------------------------------------

% Statement: XXX YYY
parse_statement(statement(PFPositionFrom, Successors_Int)) -->
    "Statement:",
    parse_atom_silent([space, newline], zeroormore),
    parse_statement_position_from(PFPositionFrom),
    parse_atom_silent([space, newline], oneormore),
    parse_statement_successors(Successors_Int),
    parse_atom_silent([space, newline], zeroormore),
    !.

%-------------------------------------------------------------------------------

% start
parse_statement_position_from(start) -->
    "start",
    !.

% line 48
parse_statement_position_from(line(Int)) -->
    "line",
    parse_atom_silent([space, newline], oneormore),
    parse_natural_int(Int),
    !.

%-------------------------------------------------------------------------------

% 2 successor(s)
parse_statement_successors(Int) -->
    parse_natural_int(Int),
    parse_atom_silent([space, newline], oneormore),
    "successor(s)",
    !.

%===============================================================================
% scan_for_successor_statement(+CharList).
%===============================================================================

% Make this call visible to the spxref tool.
:- public load__pfs:parse_successor_statement/3.

scan_for_successor_statement(CharList):-
    phrase(parse_successor_statement(PFTraceSuccessorStatement), CharList),
    must_get_last_statement_id(ParentStatementId_Atom),
    unique_atom('suc_stmt', SuccessorStatementId_Atom),
    add_pfs_successor_statement(SuccessorStatementId_Atom,
                                PFTraceSuccessorStatement,
                                ParentStatementId_Atom),
    replace_last_successor_statement_id(SuccessorStatementId_Atom),
    !.

%-------------------------------------------------------------------------------

% Successor statement: XXX.
parse_successor_statement(successor_statement(PFPositionTo)) -->
    parse_atom_silent([space, newline], zeroormore),
    "Successor statement:",
    parse_atom_silent([space, newline], zeroormore),
    parse_statement_position_to(PFPositionTo),
    parse_atom_silent([space, newline], zeroormore),
    ".",
    !.

%-------------------------------------------------------------------------------

parse_statement_position_to(finish) -->
    "finish",
    !.

parse_statement_position_to(line(Int)) -->
    "line",
    parse_atom_silent([space, newline], oneormore),
    parse_natural_int(Int),
    !.

%===============================================================================
% scan_for_path_header(+CharList, -Number_Int).
%===============================================================================

% Make this call visible to the spxref tool.
:- public load__pfs:parse_path_header/3.

scan_for_path_header(CharList, Number_Int):-
    phrase(parse_path_header(Number_Int), CharList),
    !.

%-------------------------------------------------------------------------------

%    Path  1
parse_path_header(Number_Int) -->
    parse_atom_silent([space, newline], zeroormore),
    "Path",
    parse_atom_silent([space, newline], oneormore),
    parse_natural_int(Number_Int),
    !.

%===============================================================================
% process_single_path_function(+Stream, +PFId_Atom).
%===============================================================================

process_single_path_function(Stream, PFId_Atom):-

    % Retrieve traversal condition tag.
    read_line_from_stream(Stream, ReadText),
    confirm_traversal_condition(ReadText),

    % Retrieve traversal conditions (and action line).
    retrieve_traversal_conditions(Stream, PFId_Atom),

    % Retrieve action.
    retrieve_action(Stream, PFId_Atom),
    !.

%===============================================================================
% confirm_traversal_condition(+ReadText).
%===============================================================================

% Make this call visible to the spxref tool.
:- public load__pfs:parse_traversal_condition/2.

confirm_traversal_condition(end_of_file):-
    throw_error('Unexpected end of file in parsing pfs file.', []).

confirm_traversal_condition(CharList):-
    phrase(parse_traversal_condition, CharList),
    !.

confirm_traversal_condition(CharList):-
    throw_error('Badly formed traversal condition line: ~w', [CharList]).

%-------------------------------------------------------------------------------

%      Traversal condition:
parse_traversal_condition -->
    parse_atom_silent([space, newline], zeroormore),
    "Traversal condition:",
    parse_atom_silent([space, newline], zeroormore),
    !.

%===============================================================================
% retrieve_traversal_conditions(+Stream, +PFId_Atom).
%===============================================================================

retrieve_traversal_conditions(Stream, PFId_Atom):-
    repeat,
    read_up_to_number_of_chars_from_stream(Stream, 6, ReadText),
    process_single_path_function_leader(Stream, ReadText, PFId_Atom, ParseStatus),
    retrieve_traversal_conditions_x(ParseStatus),
    !.

%-------------------------------------------------------------------------------

% Is finished.
retrieve_traversal_conditions_x(finished):-
    !.

% Not finished. Fail and look at more lines.
retrieve_traversal_conditions_x(notfinished):-
    fail.

% Error if at end of file.
retrieve_traversal_conditions_x(end_of_file):-
    throw_error('Unexpected end of file in parsing pfs file.', []).

%===============================================================================
% process_single_path_function_leader(+Stream, +ReadText, +ParentPFId_Atom,
%                                    -ParseStatus).
%===============================================================================

% At end_of_file only report end_of_file.
process_single_path_function_leader(_Stream, end_of_file, _ParentPFId_Atom, end_of_file):-
    !.

% Search for path formula.
process_single_path_function_leader(Stream, CharList, ParentPFId_Atom, notfinished):-
    scan_for_path_formula(Stream, CharList, ParentPFId_Atom),
    !.

% Search for action line.
process_single_path_function_leader(Stream, CharList, _ParentPFId_Atom, finished):-
    scan_for_action_line(Stream, CharList),
    !.

% None of the above is an error.
process_single_path_function_leader(_Stream, CharList, _ParentPFId_Atom, _ParseStatus):-
    throw_error('Badly formed line in processing verification condition, starting: ~w', [CharList]).

%===============================================================================
% scan_for_path_formula(+Stream, +CharList, +ParentVCId_Atom).
%===============================================================================

% Make this call visible to the spxref tool.
:- public load__pfs:parse_path_formula/3.

scan_for_path_formula(Stream, CharList, ParentPFId_Atom):-
    phrase(parse_path_formula(Number_Int), CharList),

    % Retrieve the term from the stream.
    read_term(Stream, TravCond_Term, []),

    % Consume rest of line following the term.
    read_line_from_stream(Stream, _ReadText),

    add_pfs_traversal_condition(Number_Int, TravCond_Term, ParentPFId_Atom),
    !.

%-------------------------------------------------------------------------------

% 1:
parse_path_formula(Number_Int) -->
    parse_atom_silent([space, newline], zeroormore),
    parse_natural_int(Number_Int),
    ":",
    parse_atom_silent([space, newline], zeroormore),
    !.

%===============================================================================
% scan_for_action_line(+Stream, +CharList).
%===============================================================================

% Make this call visible to the spxref tool.
:- public load__pfs:parse_before_action_line/2.

scan_for_action_line(Stream, CharList):-
    phrase(parse_before_action_line, CharList),

    % Retrieve the rest of the line, and check that the action line is as
    % expected.
    read_line_from_stream(Stream, ReadText),
    confirm_valid_action_line(ReadText),
    !.

%-------------------------------------------------------------------------------

% "      " (i.e. six spaces)
parse_before_action_line -->
    "      ",
    !.

%-------------------------------------------------------------------------------

% Make this call visible to the spxref tool.
:- public load__pfs:parse_action_line/2.

confirm_valid_action_line(end_of_file):-
    throw_error('Unexpected end of file in parsing pfs file.', []).

confirm_valid_action_line(CharList):-
    phrase(parse_action_line, CharList),
    !.

confirm_valid_action_line(CharList):-
    throw_error('Badly formed action line between traversal conditions and action: ~w', [CharList]).

%-------------------------------------------------------------------------------

% Action:
parse_action_line -->
    parse_atom_silent([space, newline], zeroormore),
    "Action:",
    parse_atom_silent([space, newline], zeroormore),
    !.

%===============================================================================
% retrieve_action(Stream, PFId_Atom).
%===============================================================================

retrieve_action(Stream, ParentPFId_Atom):-

    % Retrieve the term from the stream.
    read_term(Stream, Action_Term, []),

    % Consume rest of line following the term.
    read_line_from_stream(Stream, _ReadText),

    add_pfs_action(Action_Term, ParentPFId_Atom),
    !.

%===============================================================================
% replace_last_statement_id(+StatementId_Atom).
%===============================================================================

% Replace existing trace.
replace_last_statement_id(StatementId_Atom):-
    retract(get_last_statement_id(_StatementId_Atom)),
    assert(get_last_statement_id(StatementId_Atom)),
    !.

% Set initial trace.
replace_last_statement_id(StatementId_Atom):-
    assert(get_last_statement_id(StatementId_Atom)),
    !.

%===============================================================================
% must_get_last_statement_id(+StatementId_Atom).
%===============================================================================

must_get_last_statement_id(StatementId_Atom):-
            get_last_statement_id(StatementId_Atom),
            !.

must_get_last_statement_id(_StatementId_Atom):-
    throw_error('An expected statement line has not been found.', []).

%===============================================================================
% replace_last_successor_statement_id(SuccessorStatementId_Atom).
%===============================================================================

% Replace existing trace.
replace_last_successor_statement_id(SuccessorStatementId_Atom):-
    retract(get_last_successor_statement_id(_SuccessorStatementId_Atom)),
    assert(get_last_successor_statement_id(SuccessorStatementId_Atom)),
    !.

% Set initial trace.
replace_last_successor_statement_id(SuccessorStatementId_Atom):-
    assert(get_last_successor_statement_id(SuccessorStatementId_Atom)),
    !.

%===============================================================================
% must_get_last_successor_statement_id(SuccessorStatementId_Atom).
%===============================================================================

must_get_last_successor_statement_id(SuccessorStatementId_Atom):-
            get_last_successor_statement_id(SuccessorStatementId_Atom),
            !.

must_get_last_successor_statement_id(_SuccessorStatementId_Atom):-
    throw_error('An expected successor statement line has not been found.', []).

%===============================================================================
% initialise_order.
%===============================================================================

initialise_order:-
    retractall(get_order(_Int)),
    assert(get_order(1)),
    !.

%===============================================================================
% retrieve_and_increment_order(+Int).
%===============================================================================

retrieve_and_increment_order(Int):-
    retract(get_order(Int)),
    NextInt is Int+1,
    assert(get_order(NextInt)),
    !.


:- set_prolog_flag(double_quotes, codes).

%###############################################################################
% END-OF-FILE
