# Copyright (c) 2022 Science and Technology Facilities Council

# All rights reserved.

# Modifications made as part of the fparser project are distributed
# under the following license:

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:

# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.

# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.

# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

'''Test Fortran 2008 rule R214

    action-stmt is allocate-stmt
                    or assignment-stmt
                    or backspace-stmt
                    or call-stmt
                    or close-stmt
                    or continue-stmt
                    or cycle-stmt
                    or deallocate-stmt
                    or end-function-stmt
                    or end-mp-subprogram-stmt
                    or end-program-stmt
                    or end-subroutine-stmt
                    or endfile-stmt
                    or error-stop-stmt
                    or exit-stmt
                    or flush-stmt
                    or forall-stmt
                    or goto-stmt
                    or if-stmt
                    or inquire-stmt
                    or lock-stmt
                    or nullify-stmt
                    or open-stmt
                    or pointer-assignment-stmt
                    or print-stmt
                    or read-stmt
                    or return-stmt
                    or rewind-stmt
                    or stop-stmt
                    or sync-all-stmt
                    or sync-images-stmt
                    or sync-memory-stmt
                    or unlock-stmt
                    or wait-stmt
                    or where-stmt
                    or write-stmt
                    or arithmetic-if-stmt
                    or computed-goto-stmt

    Associated constraints are:

    "C201 (R208) An execution-part shall not contain an end-function-stmt,
          end-mp-subprogram-stmt, end-program-stmt, or end-subroutine-stmt."

    TODO: Implement and test lock-stmt, sync-all-stmt, sync-images-stmt,
          sync-memory-stmt, unlock-stmt (#321)
'''

import pytest
from fparser.api import get_reader, walk
from fparser.two.Fortran2003 import (
    Allocate_Stmt, Arithmetic_If_Stmt, Backspace_Stmt, Call_Stmt, Close_Stmt,
    Computed_Goto_Stmt, Continue_Stmt, Cycle_Stmt, Deallocate_Stmt,
    End_Function_Stmt, End_Subroutine_Stmt, Endfile_Stmt, Exit_Stmt, Flush_Stmt,
    Forall_Stmt, Goto_Stmt, If_Stmt, Inquire_Stmt, Nullify_Stmt, Open_Stmt,
    Pointer_Assignment_Stmt, Print_Stmt, Read_Stmt, Return_Stmt, Rewind_Stmt,
    Stop_Stmt, Wait_Stmt, Where_Stmt, Write_Stmt
)
from fparser.two.Fortran2008 import Action_Stmt, Error_Stop_Stmt
from fparser.two.utils import NoMatchError


@pytest.mark.usefixtures('f2008_create', 'fake_symbol_table')
@pytest.mark.parametrize('string, cls', [
    ('ALLOCATE(A(10))', Allocate_Stmt),
    ('BACKSPACE 42', Backspace_Stmt),
    ('CALL SOME_ROUTINE()', Call_Stmt),
    ('CLOSE(23)', Close_Stmt),
    ('CONTINUE', Continue_Stmt),
    ('CYCLE', Cycle_Stmt),
    ('DEALLOCATE(A)', Deallocate_Stmt),
    ('END FUNCTION FUNC', End_Function_Stmt),
    ('END SUBROUTINE MYSUB', End_Subroutine_Stmt),
    ('ENDFILE 42', Endfile_Stmt),
    ('EXIT', Exit_Stmt),
    ('FLUSH 23', Flush_Stmt),
    ('FORALL (I=1:N) A(I,I) = X(I)', Forall_Stmt),
    ('GO TO 915', Goto_Stmt),
    ('IF (A > B) A = B', If_Stmt),
    ('INQUIRE (IOLENGTH=N) M', Inquire_Stmt),
    ('NULLIFY(P)', Nullify_Stmt),
    ("OPEN (10, FILE = 'employee.names', ACTION = 'READ', PAD = 'YES')", Open_Stmt),
    ('P => NULL()', Pointer_Assignment_Stmt),
    ("PRINT *, 'HELLO WORLD'", Print_Stmt),
    ('READ (6, *) VAR', Read_Stmt),
    ('RETURN', Return_Stmt),
    ('REWIND 23', Rewind_Stmt),
    ('STOP', Stop_Stmt),
    ('WAIT(23)', Wait_Stmt),
    ('WHERE (TEMP > 100.0) TEMP = TEMP - REDUCE_TEMP', Where_Stmt),
    ('WRITE(10, *) A', Write_Stmt),
    ('IF (A - B) 1, 2, 3', Arithmetic_If_Stmt),
    ('GO TO (1, 2, 3) 2-1', Computed_Goto_Stmt)
])
def test_other(string, cls):
    '''Test that previous subclasses are still matched correctly.'''
    result = Action_Stmt(string)
    assert isinstance(result, cls)


def test_other_functional(f2008_parser):
    '''Test previous subclasses are still matched correctly in a subroutine.'''
    tree = f2008_parser(get_reader('''\
subroutine my_abort
stop
end subroutine my_abort
    '''))
    assert walk(tree, Stop_Stmt)
    assert 'STOP' in str(tree)


@pytest.mark.usefixtures('f2008_create')
@pytest.mark.parametrize('string, cls', [
    ('ERROR STOP', Error_Stop_Stmt)
])
def test_new_f2008_stmt(string, cls):
    '''Test that newly added F2008 subclasses are matched correctly.'''
    result = Action_Stmt(string)
    assert isinstance(result, cls)


@pytest.mark.usefixtures('f2008_create')
def test_error():
    '''Test that invalid syntax raises an error.'''
    with pytest.raises(NoMatchError) as excinfo:
        _ = Action_Stmt('ERR STOP')
    assert "Action_Stmt: 'ERR STOP'" in str(excinfo.value)
