/*
  Copyright 2013 Statoil ASA.

  This file is part of the Open Porous Media project (OPM).

  OPM is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  OPM 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
  along with OPM.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdexcept>
#include <memory>
#include <stdlib.h>
#include <iostream>
#include <boost/filesystem.hpp>
#define BOOST_TEST_MODULE ParseContextTests
#include <boost/test/unit_test.hpp>

#include <opm/parser/eclipse/Python/Python.hpp>
#include <opm/parser/eclipse/Parser/ErrorGuard.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/D.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/E.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/G.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/O.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/R.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/S.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/T.hpp>
#include <opm/parser/eclipse/Parser/InputErrorAction.hpp>
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/common/utility/OpmInputError.hpp>


#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Runspec.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>

using namespace Opm;





BOOST_AUTO_TEST_CASE(TestUnkownKeyword) {
    const char * deck1 =
        "RUNSPEC\n"
        "DIMENS\n"
        "  10 10 10 /n"
        "\n";

   const char * deck2 =
       "1rdomTX\n"
       "RUNSPEC\n"
        "DIMENS\n"
        "  10 10 10 /n"
        "\n";

    ErrorGuard errors;
    ParseContext parseContext;
    Parser parser(false);


    parser.addKeyword<ParserKeywords::DIMENS>();
    parseContext.update(ParseContext::PARSE_UNKNOWN_KEYWORD , InputError::THROW_EXCEPTION );
    BOOST_CHECK_THROW( parser.parseString( deck1 , parseContext , errors) , OpmInputError);

    parseContext.update(ParseContext::PARSE_UNKNOWN_KEYWORD , InputError::IGNORE );
    BOOST_CHECK_NO_THROW( parser.parseString( deck1 , parseContext , errors) );

    parseContext.update(ParseContext::PARSE_UNKNOWN_KEYWORD , InputError::THROW_EXCEPTION );
    parseContext.update(ParseContext::PARSE_RANDOM_TEXT , InputError::IGNORE );
    BOOST_CHECK_THROW( parser.parseString( deck2 , parseContext , errors) , OpmInputError);

    parseContext.update(ParseContext::PARSE_UNKNOWN_KEYWORD , InputError::IGNORE );
    parseContext.update(ParseContext::PARSE_RANDOM_TEXT , InputError::IGNORE );
    BOOST_CHECK_NO_THROW( parser.parseString( deck2 , parseContext , errors) );

    parseContext.update(ParseContext::PARSE_UNKNOWN_KEYWORD , InputError::IGNORE );
    parseContext.update(ParseContext::PARSE_RANDOM_TEXT , InputError::THROW_EXCEPTION );
    BOOST_CHECK_THROW( parser.parseString( deck2 , parseContext , errors) , OpmInputError);

    parseContext.update(ParseContext::PARSE_UNKNOWN_KEYWORD , InputError::IGNORE );
    parseContext.update(ParseContext::PARSE_RANDOM_TEXT , InputError::IGNORE );
    BOOST_CHECK_NO_THROW( parser.parseString( deck2 , parseContext , errors) );
}


BOOST_AUTO_TEST_CASE(TestUnkownKeywordII) {
    const char * deck1 =
        "RUNSPEC\n"
        "DIMENS\n"
        "  10 10 10 /n"
        "\n";


    ErrorGuard errors;
    ParseContext parseContext;
    Parser parser(false);


    parser.addKeyword<ParserKeywords::DIMENS>();
    parseContext.update(ParseContext::PARSE_UNKNOWN_KEYWORD , InputError::THROW_EXCEPTION );
    BOOST_CHECK_THROW( parser.parseString( deck1 , parseContext, errors ) , OpmInputError);
    parseContext.ignoreKeyword("RUNSPEC");
    BOOST_CHECK_NO_THROW( parser.parseString( deck1 , parseContext, errors ) );
}


BOOST_AUTO_TEST_CASE(Handle_extra_records) {
    const char * deck_string =
         "EQLDIMS\n"
         "  2  100  20  1  1  /\n"
         "\n"
         "EQUIL\n"
         "  2469   382.4   1705.0  0.0    500    0.0     1     1      20 /\n"
         "  2469   382.4   1705.0  0.0    500    0.0     1     1      20 /\n"
         "  2470   382.4   1705.0  0.0    500    0.0     1     1      20 /\n"
         "GRID\n";

    ErrorGuard errors;
    ParseContext parseContext;
    Parser parser(false);

    parser.addKeyword<ParserKeywords::EQLDIMS>();
    parser.addKeyword<ParserKeywords::EQUIL>();
    parser.addKeyword<ParserKeywords::GRID>();
    BOOST_CHECK_THROW( parser.parseString( deck_string , parseContext, errors ) , OpmInputError );

    parseContext.update(ParseContext::PARSE_EXTRA_RECORDS , InputError::IGNORE );
    parser.parseString( deck_string , parseContext, errors );
    BOOST_CHECK( parser.hasKeyword( "GRID" ) );

    parseContext.update(ParseContext::PARSE_EXTRA_RECORDS , InputError::THROW_EXCEPTION );
    BOOST_CHECK_THROW( parser.parseString( deck_string , parseContext, errors ) , OpmInputError);
}


BOOST_AUTO_TEST_CASE(Handle_extra_records_2) {
    const char * deck_string =
         "EQLDIMS\n"
         "  2  100  20  1  1  /\n"
         "\n"
         "EQUIL\n"
         "  2469   382.4   1705.0  0.0    500    0.0     1     1      20 /\n"
         "  2469   382.4   1705.0  0.0    500    0.0     1     1      20 /\n"
         "GRID\n"
         "DIMENS\n"
          " 10 10 3 /\n"
          " 5 3 2 /\n";

    ErrorGuard errors;
    ParseContext parseContext;
    Parser parser(false);

    parser.addKeyword<ParserKeywords::EQLDIMS>();
    parser.addKeyword<ParserKeywords::EQUIL>();
    parser.addKeyword<ParserKeywords::GRID>();
    parser.addKeyword<ParserKeywords::DIMENS>();

    parseContext.update(ParseContext::PARSE_EXTRA_RECORDS , InputError::IGNORE );
    BOOST_CHECK_THROW( parser.parseString( deck_string , parseContext, errors ), OpmInputError );
}


BOOST_AUTO_TEST_CASE(TestUnkownKeyword_DATA) {
    const char * deck_string1 =
        "RUNSPEC\n"
        "\n"
        "UNKNOWN1\n"
        "\n"
        "UNKNOWN2\n"
        "  10 10 10 /n"
        "\n"
        "UNKNOWN3\n"
        "  11 11 11 /n"
        "/\n"
        "\n"
        "DIMENS\n"
        "  12 12 12 /n"
        "\n";


    const char * deck_string2 =
        "RUNSPEC\n"
        "\n"
        "UNKNOWN1\n"
        "\n"
        "UNKNOWN2\n"
        "  10 10 10 /n"
        "\n"
        "UNKNOWN3\n"
        "  11 11 11 /n"
        "/\n"
        "\n"
        "DIMENS\n"
        "  12 12 12 /\n"
        "Ran/dom Noise; \n"
        "with 0 0 0 Data /\n"
        "/\n"
        "\n";


    ErrorGuard errors;
    ParseContext parseContext;
    Parser parser(false);


    parser.addKeyword<ParserKeywords::RUNSPEC>();
    parser.addKeyword<ParserKeywords::DIMENS>();
    parseContext.update(ParseContext::PARSE_UNKNOWN_KEYWORD , InputError::IGNORE );
    parseContext.update(ParseContext::PARSE_RANDOM_TEXT , InputError::THROW_EXCEPTION );
    {
        Deck deck = parser.parseString( deck_string1 , parseContext, errors );
        BOOST_CHECK( deck.hasKeyword( "RUNSPEC") );
        BOOST_CHECK( deck.hasKeyword( "DIMENS") );
    }
    BOOST_CHECK_THROW( parser.parseString( deck_string2 , parseContext, errors ) , OpmInputError);
}


BOOST_AUTO_TEST_CASE(TEST_UNKNOWN_OPERATE) {
    const char * deck =
        "OPERATE\n"
        "SWL    6* MULTX  PERMX 1.E10       / Temp:  SWL=1.E10*PERMX\n"
        "SWL    6* MINLIM SWL   1.0         /\n"
        "SWL    6* LOG10  SWL               / Temp:  SWL=log(1.E10*PERMX)\n"
        "SWL    6* MULTA  SWL   -0.06  0.91 / Final: SWL=0.31-0.06*log(PERMX)\n"
        "--SWCR 6* COPY   SWL               / SWCR=SWL\n"
        "SWCR   6* MULTA  SWL   1.0    0.0  / SWCR=SWL+0.0 (+0.3)\n"
        "SWCR   6* MAXLIM SWCR  0.7         / max(SWCR)=0.7\n"
        "SGU    6* MULTA  SWL   -1.0   1.0  / SGU=1-SWL\n"
        "/\n";

    ErrorGuard errors;
    ParseContext parseContext;
    Parser parser(false);

    parseContext.update(ParseContext::PARSE_UNKNOWN_KEYWORD , InputError::THROW_EXCEPTION );
    BOOST_CHECK_THROW( parser.parseString( deck , parseContext, errors ) , OpmInputError);

    parseContext.update(ParseContext::PARSE_RANDOM_SLASH , InputError::IGNORE );
    parseContext.update(ParseContext::PARSE_UNKNOWN_KEYWORD , InputError::IGNORE );
    parser.parseString( deck , parseContext, errors );
    BOOST_CHECK_NO_THROW( parser.parseString( deck , parseContext, errors ) );

    parser.addKeyword<ParserKeywords::OPERATE>();
    parser.parseString( deck , parseContext, errors );
    parseContext.update(ParseContext::PARSE_RANDOM_SLASH , InputError::THROW_EXCEPTION );
    parseContext.update(ParseContext::PARSE_UNKNOWN_KEYWORD , InputError::THROW_EXCEPTION );
    BOOST_CHECK_NO_THROW( parser.parseString( deck , parseContext, errors ) );
}




BOOST_AUTO_TEST_CASE( CheckMissingSizeKeyword) {
    const char * deck =
        "SOLUTION\n"
        "EQUIL\n"
        "  10 10 10 10 / \n"
        "\n";

    ErrorGuard errors;
    ParseContext parseContext;
    Parser parser(false);

    parser.addKeyword<ParserKeywords::EQUIL>();
    parser.addKeyword<ParserKeywords::EQLDIMS>();
    parser.addKeyword<ParserKeywords::SOLUTION>();

    parseContext.update( ParseContext::PARSE_MISSING_DIMS_KEYWORD , InputError::THROW_EXCEPTION );
    BOOST_CHECK_THROW( parser.parseString( deck , parseContext, errors ) , OpmInputError);

    parseContext.update( ParseContext::PARSE_MISSING_DIMS_KEYWORD , InputError::IGNORE );
    BOOST_CHECK_NO_THROW( parser.parseString( deck , parseContext, errors ) );
}


BOOST_AUTO_TEST_CASE( CheckUnsupportedInSCHEDULE ) {
    const char * deckStringUnSupported =
        "START\n"
        " 10 'JAN' 2000 /\n"
        "RUNSPEC\n"
        "DIMENS\n"
        "  10 10 10 / \n"
        "GRID\n"
        "DX\n"
        "1000*0.25 /\n"
        "DY\n"
        "1000*0.25 /\n"
        "DZ\n"
        "1000*0.25 /\n"
        "TOPS\n"
        "100*0.25 /\n"
        "SCHEDULE\n"
        "MULTZ\n"
        "   1000*0.10 /\n"
        "\n";

    const char * deckStringSupported =
        "START\n"
        " 10 'JAN' 2000 /\n"
        "RUNSPEC\n"
        "DIMENS\n"
        "  10 10 10 / \n"
        "GRID\n"
        "DX\n"
        "1000*0.25 /\n"
        "DY\n"
        "1000*0.25 /\n"
        "DZ\n"
        "1000*0.25 /\n"
        "TOPS\n"
        "100*0.25 /\n"
        "SCHEDULE\n"
        "MULTFLT\n"
        "   'F1' 0.10 /\n"
        "/\n"
        "\n";

    ErrorGuard errors;
    ParseContext parseContext;
    Parser parser(true);

    auto deckSupported = parser.parseString( deckStringSupported , parseContext, errors );
    auto deckUnSupported = parser.parseString( deckStringUnSupported , parseContext, errors );
    EclipseGrid grid( deckSupported );
    TableManager table ( deckSupported );
    FieldPropsManager fp(deckSupported, Phases{true, true, true}, grid, table);
    Runspec runspec(deckSupported);
    auto python = std::make_shared<Python>();

    parseContext.update( ParseContext::UNSUPPORTED_SCHEDULE_GEO_MODIFIER , InputError::IGNORE );
    BOOST_CHECK_NO_THROW( Schedule( deckSupported  , grid , fp, runspec, parseContext, errors, python  ));
    BOOST_CHECK_NO_THROW( Schedule( deckUnSupported, grid , fp, runspec, parseContext, errors, python  ));

    parseContext.update( ParseContext::UNSUPPORTED_SCHEDULE_GEO_MODIFIER , InputError::THROW_EXCEPTION );
    BOOST_CHECK_THROW( Schedule( deckUnSupported , grid , fp, runspec , parseContext , errors, python), OpmInputError );
    BOOST_CHECK_NO_THROW( Schedule( deckSupported , grid , fp, runspec , parseContext, errors, python));
}



BOOST_AUTO_TEST_CASE(TestRandomSlash) {
    const char * deck1 =
        "SCHEDULE\n"
        "TSTEP\n"
        "  10 10 10 /\n"
        "/\n";

    const char * deck2 =
        "SCHEDULE\n"
        "TSTEP\n"
        "  10 10 10 /\n"
        "   /\n";


    ErrorGuard errors;
    ParseContext parseContext;
    Parser parser(false);


    parser.addKeyword<ParserKeywords::TSTEP>();
    parser.addKeyword<ParserKeywords::SCHEDULE>();

    parseContext.update(ParseContext::PARSE_RANDOM_SLASH , InputError::THROW_EXCEPTION);
    parseContext.update(ParseContext::PARSE_RANDOM_TEXT , InputError::IGNORE);
    BOOST_CHECK_THROW( parser.parseString( deck1 , parseContext, errors ) , OpmInputError);
    BOOST_CHECK_THROW( parser.parseString( deck2 , parseContext, errors ) , OpmInputError);


    parseContext.update(ParseContext::PARSE_RANDOM_SLASH , InputError::IGNORE);
    parseContext.update(ParseContext::PARSE_RANDOM_TEXT , InputError::THROW_EXCEPTION);
    BOOST_CHECK_NO_THROW( parser.parseString( deck1 , parseContext, errors ) );
    BOOST_CHECK_NO_THROW( parser.parseString( deck2 , parseContext, errors ) );
}



BOOST_AUTO_TEST_CASE(TestCOMPORD) {
    const char * deckString =
        "START\n"
        " 10 'JAN' 2000 /\n"
        "RUNSPEC\n"
        "DIMENS\n"
        "  10 10 10 / \n"
        "GRID\n"
        "DX\n"
        "1000*0.25 /\n"
        "DY\n"
        "1000*0.25 /\n"
        "DZ\n"
        "1000*0.25 /\n"
        "TOPS\n"
        "100*0.25 /\n"
        "SCHEDULE\n"
        "COMPORD\n"
        "  '*'  'DEPTH' /\n"
        "/\n";

    ParseContext parseContext;
    ErrorGuard errors;
    Parser parser(true);
    auto deck = parser.parseString( deckString , parseContext, errors );

    EclipseGrid grid( deck );
    TableManager table ( deck );
    FieldPropsManager fp( deck, Phases{true, true, true}, grid, table);
    Runspec runspec(deck);
    auto python = std::make_shared<Python>();

    parseContext.update( ParseContext::UNSUPPORTED_COMPORD_TYPE , InputError::IGNORE);
    BOOST_CHECK_NO_THROW( Schedule( deck , grid , fp, runspec, parseContext, errors, python ));

    parseContext.update( ParseContext::UNSUPPORTED_COMPORD_TYPE , InputError::THROW_EXCEPTION);
    BOOST_CHECK_THROW( Schedule( deck,  grid , fp, runspec , parseContext, errors, python), OpmInputError );
}


BOOST_AUTO_TEST_CASE(TestInvalidKey) {
    ParseContext parseContext;
    BOOST_CHECK_THROW( parseContext.addKey("KEY*", InputError::THROW_EXCEPTION) , std::invalid_argument );
    BOOST_CHECK_THROW( parseContext.addKey("KEY:", InputError::THROW_EXCEPTION) , std::invalid_argument );
}


BOOST_AUTO_TEST_CASE(TestNew) {
    ParseContext parseContext;

    BOOST_CHECK_EQUAL( false , parseContext.hasKey("NO"));
    parseContext.addKey("NEW_KEY", InputError::THROW_EXCEPTION);
    BOOST_CHECK_EQUAL( true , parseContext.hasKey("NEW_KEY"));
    BOOST_CHECK_THROW( parseContext.get("NO") , std::invalid_argument);
    BOOST_CHECK_EQUAL( parseContext.get("NEW_KEY") , InputError::THROW_EXCEPTION );
    parseContext.addKey("KEY2", InputError::THROW_EXCEPTION);
    BOOST_CHECK_EQUAL( parseContext.get("NEW_KEY") , InputError::THROW_EXCEPTION );

    BOOST_CHECK_THROW( parseContext.updateKey("NO" , InputError::IGNORE) , std::invalid_argument);

    parseContext.updateKey("NEW_KEY" , InputError::WARN);
    BOOST_CHECK_EQUAL( parseContext.get("NEW_KEY") , InputError::WARN );

    BOOST_CHECK_NO_THROW( parseContext.update("KEY2:NEW_KEY" , InputError::IGNORE));
    BOOST_CHECK_NO_THROW( parseContext.update("UnknownKey" , InputError::IGNORE));
    BOOST_CHECK_EQUAL( parseContext.get("NEW_KEY") , InputError::IGNORE );
    BOOST_CHECK_EQUAL( parseContext.get("KEY2") , InputError::IGNORE );

    parseContext.addKey("SECRET_KEY", InputError::THROW_EXCEPTION);
    parseContext.addKey("NEW_KEY2", InputError::THROW_EXCEPTION);
    parseContext.addKey("NEW_KEY3", InputError::THROW_EXCEPTION);
    parseContext.update("NEW_KEY*" , InputError::WARN);
    BOOST_CHECK_EQUAL( parseContext.get("NEW_KEY") , InputError::WARN );
    BOOST_CHECK_EQUAL( parseContext.get("NEW_KEY2") , InputError::WARN );
    BOOST_CHECK_EQUAL( parseContext.get("NEW_KEY3") , InputError::WARN );

    parseContext.update( InputError::IGNORE );
    BOOST_CHECK_EQUAL( parseContext.get("NEW_KEY3")   , InputError::IGNORE );
    BOOST_CHECK_EQUAL( parseContext.get("SECRET_KEY") , InputError::IGNORE );


}


BOOST_AUTO_TEST_CASE( test_constructor_with_values) {
    ParseContext parseContext( {{ParseContext::PARSE_RANDOM_SLASH , InputError::IGNORE},
                {"UNSUPPORTED_*" , InputError::WARN},
                    {"UNKNWON-IGNORED" , InputError::WARN}});

    BOOST_CHECK_EQUAL( parseContext.get(ParseContext::PARSE_RANDOM_SLASH) , InputError::IGNORE );
    BOOST_CHECK_EQUAL( parseContext.get(ParseContext::PARSE_RANDOM_TEXT) , InputError::THROW_EXCEPTION );
    BOOST_CHECK_EQUAL( parseContext.get(ParseContext::UNSUPPORTED_INITIAL_THPRES) , InputError::WARN );
    BOOST_CHECK_EQUAL( parseContext.get(ParseContext::UNSUPPORTED_COMPORD_TYPE) , InputError::WARN );
}



BOOST_AUTO_TEST_CASE( test_too_much_data ) {
    const char * deckString =
        "RUNSPEC\n"
        "DIMENS\n"
        "  10 10 10 10 /n"
        "\n";

    ParseContext parseContext;
    Parser parser;
    ErrorGuard errors;


    parseContext.update(ParseContext::PARSE_EXTRA_DATA , InputError::THROW_EXCEPTION );
    BOOST_CHECK_THROW( parser.parseString( deckString , parseContext, errors ) , OpmInputError);

    parseContext.update(ParseContext::PARSE_EXTRA_DATA , InputError::IGNORE );
    auto deck = parser.parseString( deckString , parseContext, errors );
}


BOOST_AUTO_TEST_CASE(test_1arg_constructor) {
    setenv("OPM_ERRORS_IGNORE", "PARSE_RANDOM_SLASH", 1);
    {
        ParseContext ctx(InputError::WARN);
        BOOST_CHECK_EQUAL(ctx.get(ParseContext::UNSUPPORTED_COMPORD_TYPE), InputError::WARN);
        BOOST_CHECK_EQUAL(ctx.get(ParseContext::PARSE_RANDOM_SLASH), InputError::IGNORE);
    }
}

BOOST_AUTO_TEST_CASE( test_invalid_keyword_combination_required ) {
    const std::string deckString = R"(
AQUCT
    1 2000.0 1.5 100 .3 3.0e-5 330 10 360.0 1 2 /
/
)";

    ParseContext parseContext;
    Parser parser;
    ErrorGuard errors;

    BOOST_CHECK_THROW(parser.parseString(deckString, parseContext, errors), OpmInputError);

    parseContext.update(ParseContext::PARSE_INVALID_KEYWORD_COMBINATION , InputError::IGNORE );
    BOOST_CHECK_NO_THROW(parser.parseString(deckString, parseContext, errors));

    parseContext.update(ParseContext::PARSE_INVALID_KEYWORD_COMBINATION , InputError::THROW_EXCEPTION );
    BOOST_CHECK_THROW(parser.parseString(deckString, parseContext, errors), OpmInputError);
}

BOOST_AUTO_TEST_CASE( test_invalid_keyword_combination_prohibited ) {
    const std::string deckString = R"(
EQLDIMS
/

TEMPVD
   0.5 0 /

RTEMPVD
   0.5 0 /
)";

    ParseContext parseContext;
    Parser parser;
    ErrorGuard errors;

    BOOST_CHECK_THROW(parser.parseString(deckString, parseContext, errors), OpmInputError);

    parseContext.update(ParseContext::PARSE_INVALID_KEYWORD_COMBINATION , InputError::IGNORE );
    BOOST_CHECK_NO_THROW(parser.parseString(deckString, parseContext, errors));

    parseContext.update(ParseContext::PARSE_INVALID_KEYWORD_COMBINATION , InputError::THROW_EXCEPTION );
    BOOST_CHECK_THROW(parser.parseString(deckString, parseContext, errors), OpmInputError);
}

BOOST_AUTO_TEST_CASE( test_invalid_wtemplate_config ) {
    const std::string defDeckString = R"(
    START  -- 0
     10 'JAN' 2000 /
    RUNSPEC
    DIMENS
      10 10 10 /
    GRID
    DX
    1000*0.25 /
    DY
    1000*0.25 /
    DZ
    1000*0.25 /
    TOPS
    100*0.25 /
    PERMX
       1000*1 /
    PERMY
       1000*1 /
    PERMZ
       1000*1 /
    SCHEDULE
    DATES             -- 1
     10  OKT 2008 /
    /
    WELSPECS
        'PROD' 'G1'  10 10 100 'OIL' /
        'INJ'  'G1'   3  3 100 'WATER' /
    /
    )";

    ParseContext parseContext;
    Parser parser;
    ErrorGuard errors;

    parseContext.update(ParseContext::SCHEDULE_INVALID_NAME , InputError::THROW_EXCEPTION );

    std::vector < std::string > testSamples;
    std::string testSample;

    // Invalid well name in COMPDAT
    testSample = R"(
    COMPDAT
    'SOMETHINGELSE' 10 10 3 3 'OPEN' 1* 1* 0.5 /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid well name in WCONPROD
    testSample = R"(
    COMPDAT
    'PROD' 10 10 3 3 'OPEN' 1* 1* 0.5 /
    /
    WCONPROD
    'SOMETHINGELSE' 'OPEN' 'ORAT' 20000 4* 1000 /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid well name in WCONINJE
    testSample = R"(
    COMPDAT
    'INJ' 10 10 3 3 'OPEN' 1* 1* 0.5 /
    /
    WCONINJE
    'SOMETHINGELSE' 'WATER' 'OPEN' 'RATE' 20000 4* 1000 /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid well name in WCONINJH
    testSample = R"(
    COMPDAT
    'INJ' 10 10 3 3 'OPEN' 1* 1* 0.5 /
    /
    WCONINJH
    'SOMETHINGELSE' 'WAT' 'OPEN' 20000 /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid well name in WTEMP
    testSample = R"(
    COMPDAT
    'INJ' 10 10 3 3 'OPEN' 1* 1* 0.5 /
    /
    WCONINJE
    'INJ' 'WATER' 'OPEN' 'RATE' 20000 4*  /
    /
    DATES
    15  OKT 2008 /
    /
    WTEMP
    'SOMETHINGELSE' 40.0 /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid well name in WINJTEMP
    testSample = R"(
    COMPDAT
    'INJ' 10 10 3 3 'OPEN' 1* 1* 0.5 /
    /
    WCONINJE
    'INJ' 'WATER' 'OPEN' 'RATE' 20000 4*  /
    /
    DATES
    15  OKT 2008 /
    /
    WINJTEMP
    'SOMETHINGELSE' 1* 40.0 1*  /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid well name in WSOLVENT
    testSample = R"(
    COMPDAT
    'INJ' 10 10 3 3 'OPEN' 1* 1* 0.5 /
    /
    WCONINJE
    'INJ' 'WATER' 'OPEN' 'RATE' 20000 4*  /
    /
    DATES
    15  OKT 2008 /
    /
    WSOLVENT
    'SOMETHINGELSE' 1.0  /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid well name in WPOLYMER
    testSample = R"(
    COMPDAT
    'INJ' 10 10 3 3 'OPEN' 1* 1* 0.5 /
    /
    WCONINJE
    'INJ' 'WATER' 'OPEN' 'RATE' 20000 4*  /
    /
    DATES
    15  OKT 2008 /
    /
    WPOLYMER
    'SOMETHINGELSE' 1.0  0.0 /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid well name in WFOAM
    testSample = R"(
    COMPDAT
    'INJ' 10 10 3 3 'OPEN' 1* 1* 0.5 /
    /
    WCONINJE
    'INJ' 'WATER' 'OPEN' 'RATE' 20000 4*  /
    /
    DATES
    15  OKT 2008 /
    /
    WFOAM
    'SOMETHINGELSE' 0.02 /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid well name in WELOPEN
    testSample = R"(
    COMPDAT
    'INJ' 10 10 3 3 'OPEN' 1* 1* 0.5 /
    /
    WCONINJE
    'INJ' 'WATER' 'OPEN' 'RATE' 20000 4*  /
    /
    DATES
    15  OKT 2008 /
    /
    WELOPEN
    'SOMETHINGELSE' /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid well name in WELTARG
    testSample = R"(
    COMPDAT
    'PROD' 10 10 3 3 'OPEN' 1* 1* 0.5 /
    /
    WCONHIST
    'PROD' 'OPEN' 'ORAT' 20000 4* 1000 /
    /
    WELTARG
    'SOMETHINGELSE' 'ORAT' 15000 /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid well name in WECON
    testSample = R"(
    COMPDAT
    'PROD' 10 10 3 3 'OPEN' 1* 1* 0.5 /
    /
    WCONPROD
    'PROD' 'OPEN' 'ORAT' 20000 4* 1000 /
    /
    WECON
    'SOMETHINGELSE' 15000 /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid well name in WEFAC
    testSample = R"(
    COMPDAT
    'PROD' 10 10 3 3 'OPEN' 1* 1* 0.5 /
    /
    WCONPROD
    'PROD' 'OPEN' 'ORAT' 20000 4* 1000 /
    /
    WEFAC
    'SOMETHINGELSE' 0.5 /
    /
    )";

    testSamples.push_back(testSample);
    // Invalid group name in GCONPROD
    testSample = R"(
    GCONPROD
    'SOMETHINGELSE' 'ORAT' 20000 /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid group name in GEFAC
    testSample = R"(
    GEFAC
    'SOMETHINGELSE' 0.5 /
    /
    )";
    testSamples.push_back(testSample);

    // Invalid group name in GCONINJE
    testSample = R"(
    GCONINJE
    'SOMETHINGELSE' 'WAT' 'RATE' 20000 /
    /
    )";
    testSamples.push_back(testSample);

    std::string deckinput;
    for (std::string sample : testSamples) {

        deckinput = defDeckString + sample;
        auto deckUnSupported = parser.parseString( deckinput , parseContext, errors );

        auto python = std::make_shared<Python>();
        EclipseGrid grid( deckUnSupported );
        TableManager table ( deckUnSupported );
        FieldPropsManager fp( deckUnSupported, Phases{true, true, true}, grid, table);
        Runspec runspec( deckUnSupported);

        BOOST_CHECK_THROW( Schedule( deckUnSupported , grid , fp, runspec , parseContext, errors, python), OpmInputError);
    }
}


/*
  If there are errors the ErrorGuard destructor will print error messages and
  call std::terminate(); if you do not accept the std::terminate in the
  destructor you should call the clear() method first.
*/


BOOST_AUTO_TEST_CASE(Test_ERRORGUARD) {
    ErrorGuard eg;

    BOOST_CHECK(!eg);

    eg.addWarning(ParseContext::SUMMARY_UNKNOWN_WELL, "Unknown well");
    BOOST_CHECK(!eg);

    eg.addError(ParseContext::SUMMARY_UNKNOWN_GROUP, "Unknwon Group");
    BOOST_CHECK(eg);
    eg.clear();
    BOOST_CHECK(!eg);
}





BOOST_AUTO_TEST_CASE(LONG_KEYWORDS) {
    const std::string deck_string = R"(
RPTRUNSPEC
)";
    Parser parser;
    ParseContext context;
    ErrorGuard error;

    context.update(ParseContext::PARSE_LONG_KEYWORD, InputError::IGNORE);
    auto deck = parser.parseString(deck_string, context, error);
    BOOST_CHECK( deck.hasKeyword("RPTRUNSP") );

    context.update(ParseContext::PARSE_LONG_KEYWORD, InputError::THROW_EXCEPTION);
    BOOST_CHECK_THROW( parser.parseString(deck_string, context, error), OpmInputError);
}


Deck parse(bool throw_opm, bool& opm_caught, bool& std_caught) {
    KeywordLocation location("kw", "file", 100);
    try {
        if (throw_opm)
            throw OpmInputError("{}:{}:{}", location);
        else
            throw std::runtime_error("Runtime");
    }
    catch (const OpmInputError& opm_error) {
        opm_caught = true;
        throw;
    }
    catch (const std::exception& std_error) {
        std_caught = true;
        throw;
    }
}



BOOST_AUTO_TEST_CASE(OPM_ERROR) {
    KeywordLocation location("kw", "file", 100);
    OpmInputError error1("Error", location);
    OpmInputError error4("{keyword}:{line}:{keyword}", location);

    /*
      This test is meant to emulate the typical parsing process, the blocks here
      in the main test function represent main() in the simulator and the main
      function call the parse() function create a deck. The parse function will
      fail with either OpmInputError or std::runtime_error, in the main scope we
      should catch them both and test that the correct exception has been
      thrown.
    */
    {
        bool std_caught = false;
        bool opm_caught = false;
        std::unique_ptr<Deck> deck_ptr;
        try {
            deck_ptr = std::make_unique<Deck>( parse(true, opm_caught, std_caught) );
        } catch( ... ) {
            BOOST_CHECK(!std_caught);
            BOOST_CHECK(opm_caught);
        }

    }

    {
        bool std_caught = false;
        bool opm_caught = false;
        std::unique_ptr<Deck> deck_ptr;
        try {
            deck_ptr = std::make_unique<Deck>( parse(false, opm_caught, std_caught) );
        } catch( ... ) {
            BOOST_CHECK(std_caught);
            BOOST_CHECK(!opm_caught);
        }

    }
}


