/** \file
 *
 *  Contains unit tests for the InternalEnvSim class
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#ifndef _INTERNALENVSIM_TEST_H_
#define _INTERNALENVSIM_TEST_H_

#include "InternalEnvSim.h"
#include "component/ComponentRegistry.h"
#include "data/BinaryDataType.h"
#include "logger/Logger.h"
#include "logger/LogEngine.h"
#include "logger/LoggerRules.h"
#include "logger/TextLogWriter.h"
#include "module/Module.h"

#include <cxxtest/TestSuite.h>

/**
 *  Unit tests for InternalEnvSim class
 *
 *  \ingroup modules_simulator
 */
class InternalEnvSim_Test : public CxxTest::TestSuite
{
public:

    class TestModule : public Module
    {
    public:

        TestModule()
            : Module( "TestModule", "description" )
        {}

        void loadComponents( Logger &logger )
        {
            return;
            //registerComponent(new InternalEnvSim(this));
            //logger.syslog("loaded components.");
        }
    };

    class TestInternalEnvSim : public InternalEnvSim
    {
    public :

        TestInternalEnvSim( const TestModule *testmodule )
            : InternalEnvSim( testmodule )
        {}

        virtual void simulateSensors( double time, double depthM, double latDeg, double lonDeg, float *values )
        {
            InternalEnvSim::simulateSensors( time, depthM, latDeg, lonDeg, values );
        }
    };

    FileOutStream *stdOutStream_;
    LogEngine syslogEngine_;
    Logger* logger_;

    void setUp()
    {
        stdOutStream_ = new FileOutStream( stdout );
        syslogEngine_.addAssociation( new TextLogWriter( *stdOutStream_ ),
                                      new TypeRule( LogEntry::SYSLOG_LOG_ENTRY ) );
        logger_ = new Logger( "TestLogger" );
    }

    // Ensures the syslog is empty at the end of the run.
    virtual void tearDown( void )
    {
        syslogEngine_.processQueue( Timespan::ZERO_TIMESPAN );
        delete logger_;
        delete stdOutStream_;
    }

    // Test!
    void testSim( void )
    {

        // Configure the slate for Unit testing
        Slate::SimulateDirectoryWrites();

        // Prep ComponentRegistry
        ComponentRegistry::Initialize();

        // Create a test Module that can be used to create the component
        const TestModule module;

        // Display status
        syslogEngine_.processQueue( Timespan::ZERO_TIMESPAN );

        /**
         *  Set the following configuration values
         *
         * InternalEnvSim.nc3File = "Resources/BurgerOilfieldSpillMed.nc3";
         * InternalEnvSim.var1 = "mass_concentration_of_petroleum_hydrocarbons_in_sea_water:kg/m3";
         * InternalEnvSim.att1 = "false_easting:480000";
         * InternalEnvSim.att2 = "false_northing:3840000";
         * InternalEnvSim.att3 = "longitude_of_central_meridian:-123";
         * InternalEnvSim.timeAdjust = 1 day;
         *
         */
        const int numConfigStrs = 14;
        const char *configStrs[numConfigStrs][2] =
        {
            {"nc3File", "Resources/BurgerOilfieldSpillMed.nc3"},
            {"var1", "mass_concentration_of_petroleum_hydrocarbons_in_sea_water:kg/m3"},
            {"var2", ""},
            {"var3", ""},
            {"var4", ""},
            {"var5", ""},
            {"var6", ""},
            {"var5", ""},
            {"att1", "false_easting:480000"},
            {"att2", "false_northing:3840000"},
            {"att3", "longitude_of_central_meridian:-123"},
            {"att4", ""},
            {"att5", ""},
            {"att6", ""},
        };
        for( int i = 0; i < numConfigStrs; ++ i )
        {
            ConfigURI cfgUri( "InternalEnvSim", configStrs[i][0], Units::NONE, STRVALUE );
            DataValue *cfgValue = new StrValue( configStrs[i][1] );
            ConfigDataElement *cfgEl = new ConfigDataElement( cfgUri, cfgValue );
            Slate::MapDataElement( ( ConfigURI * )&cfgUri, cfgEl );
        }
        ConfigURI cfgUri( "InternalEnvSim", "timeAdjust", Units::DAY );
        DataValue *cfgValue = cfgUri.getUnit().dataValue( 1 );
        ConfigDataElement *cfgEl = new ConfigDataElement( cfgUri, cfgValue );
        Slate::MapDataElement( ( ConfigURI * )&cfgUri, cfgEl );

        // Create the component
        TestInternalEnvSim internalEnvSim( &module );
        internalEnvSim.initialize();

        // Prep to run the test
        float values[InternalEnvSimIF::MAX_VARS];
        double dTime( Timestamp::Now().asDouble() );
        double depthM( nanf( "" ) ), latDeg( nanf( "" ) ), lonDeg( nanf( "" ) );

        double dTdepthLatLonValue[][5] =
        {
            {0, 0.33, 36.70, -122.19, 0.0080},
            {0, 0.33, 36.699, -122.19, 0.0050},
            {0, 0.33, 36.699, -122.192, 0.3933},
            {0, 1.33, 36.699, -122.192, 0.2339},
            {0, 2.33, 36.699, -122.192, 0},
            {3600, 2.33, 36.699, -122.192, 0.6877},
            {3600, 1.33, 36.699, -122.192, 1.6173},
            {3600, 0.33, 36.699, -122.192, 2.0931},
            {3600, 0.33, 36.699, -122.19, 0.3856},
            {3600, 0.33, 36.70, -122.19, 0.7860},
        };
        int rows = sizeof dTdepthLatLonValue / sizeof dTdepthLatLonValue[0];

        for( int i = 0l; i < rows; ++i )
        {
            values[0] = nanf( "" );
            internalEnvSim.simulateSensors(
                dTime + dTdepthLatLonValue[i][0],
                dTdepthLatLonValue[i][1],
                dTdepthLatLonValue[i][2],
                dTdepthLatLonValue[i][3],
                values );
            printf( "%s = %g%s\n", "mass_concentration_of_petroleum_hydrocarbons_in_sea_water", values[0], "kg/m3" );
            TS_ASSERT_DELTA( dTdepthLatLonValue[i][4], values[0], 0.01 );
        }

    }

};

#endif // _INTERNALENVSIM_TEST_H
