

const double PI = 4.0 * atan( 1.0 );

string vec3dString( const vec3d v )
{
    return "(" + v.x() + ", " + v.y() + ", " + v.z() + ")";
}

string intString( int i )
{
    return " " + i;
}

bool CloseVec3d( vec3d a, vec3d b, double tol )
{
    double sum = ( a.x() - b.x() ) + ( a.y() - b.y() ) + ( a.z() - b.z() );

    if ( abs( sum ) < tol )
    { return true; }

    return false;
}

double tol = 0.00001;
int main()
{
    int failure = 0;

    Print( string( "Begin Scripting Test" ) );
    int errcount = 0;
    errcount = TestErrorHandling();

    failure += TestVec3d();
    failure += TestMatrix();
    failure += TestProxy();
    failure += TestAPIScript();
    failure += TestVarPreset();

    Print( string( "End Scripting Test" ) );
    failure += DumpErrorsAndExit( errcount );

    return failure;
}

int TestVec3d()
{
    int failure = 0;

    Print( string( "--> Testing Vec3d" ) );

    //==== Test Vec3d ====//
    vec3d a();                                // Default Constructor
    vec3d b( 1.0, 2.0, 3.0 );                  // Init Constructor

    //===== Test Copy ====//
    a = b;                                    // Copy Constructor
    if ( !CloseVec3d( a, b, tol ) )                    { Print( " ---> Error: Vec3d Copy " ); failure++; }

    //===== Test Assignment ====//
    a.set_xyz( 2.0, 4.0, 6.0 );
    b.set_x( 2.0 );    b.set_y( 4.0 );    b.set_z( 6.0 );
    if ( !CloseVec3d( a, b, tol ) )                    { Print( "---> Error: Vec3d Assignment " ); failure++; }

    //===== Test Rotate ====//
    a.set_xyz( 1.0, 0.0, 0.0 );
    // rotate_x/y/z take the angle itself.  They used to be handed cos and sin
    // of the angle, which no longer matches the registered signature.
    a.rotate_z( 0.5 * PI );
    a.rotate_x( 0.5 * PI );
    a.rotate_y( 0.5 * PI );
    if ( !CloseVec3d( a, vec3d( -1, 0, 0 ), tol ) )        { Print( "---> Error: Vec3d Rotate " ); failure++; }

    //===== Test Scale/Offset/Reflect ====//
    a.set_xyz( 2.0, 2.0, 2.0 );
    a.scale_x( 2.0 );        a.scale_y( 2.0 );        a.scale_z( 2.0 );
    a.offset_x( 10.0 );        a.offset_y( 10.0 );        a.offset_z( 10.0 );
    if ( !CloseVec3d( a, vec3d( 14.0, 14.0, 14.0 ), tol ) )     { Print( "---> Error: Vec3d Scale/Offset " ); failure++; }

    a.set_xyz( 1.0, 2.0, 3.0 );
    b = a.reflect_xy();
    a = b.reflect_xz();
    b = a.reflect_yz();
    if ( !CloseVec3d( b, vec3d( -1.0, -2.0, -3.0 ), tol ) )     { Print( "---> Error: Vec3d Reflect " ); failure++; }

    //==== Test Mag ====//
    a.set_xyz( 1.0, 2.0, 3.0 );
    if ( abs( a.mag() - sqrt( 14 ) ) > tol )                        { Print( "---> Error: Vec3d Mag " ); failure++; }

    //===== Test Operators ====//
    a.set_xyz( 2.0, 3.0, 4.0 );
    b.set_xyz( 3.0, 4.0, 5.0 );
    vec3d c = a + b;
    a = c - b;
    b = a * 2.0;
    c = b * a;
    a = c / 2.0;
    if ( !CloseVec3d( a, vec3d( 4.0, 9.0, 16.0 ), tol ) )         { Print( "---> Error: Vec3d Operators " ); failure++; }

    //==== Test Dist ====//
    a.set_xyz( 2.0, 2.0, 2.0 );
    b.set_xyz( 3.0, 4.0, 5.0 );
    double d = dist( a, b );
    double d2 = dist_squared( a, b );
    if ( abs( d - sqrt( 14 ) ) > tol && abs( d2 - 14 ) > tol )    { Print( "---> Error: Vec3d Dist " ); failure++; }

    //==== Test Dot ====//
    a.set_xyz( 1.0, 2.0, 3.0 );
    b.set_xyz( 2.0, 3.0, 4.0 );
    if ( abs( dot( a, b ) - 20 ) > tol )                            { Print( "---> Error: Vec3d Dot " ); failure++; }

    //==== Test Cross ====//
    a.set_xyz( 4.0, 0.0, 0.0 );
    b.set_xyz( 0.0, 3.0, 0.0 );
    c = cross( a, b );
    c.normalize();
    if ( !CloseVec3d( c, vec3d( 0.0, 0.0, 1.0 ), tol ) )         { Print( "---> Error: Vec3d Cross  " ); failure++; }

    //==== Test Angle/Rotate ====//
    a.set_xyz( 1.0, 1.0, 0.0 );
    b.set_xyz( 1.0, 0.0, 0.0 );
    if ( abs( angle( a, b ) - PI / 4 ) > tol )                    { Print( "---> Error: Vec3d Angle " ); failure++; }
    c.set_xyz( 0.0, 0.0, 1.0 );
    if ( abs( signed_angle( a, b, c ) - -PI / 4 ) > tol )            { Print( "---> Error: Vec3d SignedAngle " ); failure++; }

    c = RotateArbAxis( b, PI, a );
    if ( !CloseVec3d( c, vec3d( 0.0, 1.0, 0.0 ), tol ) )         { Print( "---> Error: Vec3d RotateArbAxis  " ); failure++; }

    return failure;
}

int TestMatrix()
{
    int failure = 0;

    Print( string( "--> Testing Matrix4d" ) );

    //==== Test Matrix4d ====//
    Matrix4d m();                            // Default Constructor
    m.loadIdentity();
    m.translatef( 1.0, 0.0, 0.0 );
    vec3d a = m.xform( vec3d( 0.0, 0.0, 0.0 ) );
    m.loadIdentity();
    m.rotateY( 90.0 );
    m.rotateX( 90.0 );
    m.rotateZ( 90.0 );
    m.rotate( PI / 4, vec3d( 0.0, 0.0, 1.0 ) );      // Radians
    vec3d b = m.xform( a );
    if ( !CloseVec3d( b, vec3d( 0.707107, -0.707107, 0.0 ), tol ) )
    { Print( "---> Error: Matrix4d Rotate " ); failure++; }

    m.loadXZRef();
    m.scale( 10.0 );
    b = m.xform( vec3d( 1, 2, 3 ) );
    m.loadXYRef();
    a = m.xform( b );
    m.loadYZRef();
    b = m.xform( a );
    if ( !CloseVec3d( b, vec3d( -10, -20, -30 ), tol ) )    { Print( "---> Error: Matrix4d Reflect " ); failure++; }

    m.loadIdentity();
    m.rotateY( 10.0 );
    m.rotateX( 20.0 );
    m.rotateZ( 30.0 );
    a = vec3d( 1.0, 1.0, 1.0 );
    vec3d c = m.xform( a );
    m.affineInverse();
    b = m.xform( c );
    if ( !CloseVec3d( a, b, tol ) )                        { Print( "---> Error: Matrix4d Affine " ); failure++; }

    return failure;
}

int TestProxy()
{
    int failure = 0;

    Print( string( "--> Testing Proxy" ) );

    //==== Test Proxy Stuff =====//
//    SetSaveInt( 23 );

//    //==== Get Vector of Vec3d From App =====//
    array< vec3d > @vec3d_array = GetProxyVec3dArray();
//    Print( formatInt( vec3d_array.size(), '' ) );
    //for ( uint i = 0 ; i < vec3d_array.size() ; i++ )
    //{
    //    Print( vec3dString( vec3d_array[i] ) );
    //}

    //array< vec3d > test_vec3d_array;
    //test_vec3d_array.insertLast( vec3d(1,2,3) );
    //test_vec3d_array.insertLast( vec3d(2,3,4) );
    //test_vec3d_array.insertLast( vec3d(3,4,5) );
    //SetVec3dArray( test_vec3d_array );

//    TestProxy2();

    return failure;
}


//void TestProxy2()
//{
//    if ( GetSaveInt() != 23 )                        Print( "---> Error: Int Proxy  " );
//}

int TestErrorHandling()
{
    SilenceErrors(); // Prevent known errors from printing during test.
    //==== Bogus Call To Create API Error ====//
    Print( string( "---> Test Error Handling" ) );
    SetParmVal( "BogusParmID", 23.0 );

    if ( !GetErrorLastCallFlag() )                        { Print( "---> Error: API GetErrorLastCallFlag " ); }

    int errcount = GetNumTotalErrors();

    ErrorObj err = PopLastError();
    if ( err.GetErrorCode() != VSP_CANT_FIND_PARM )            { Print( "---> Error: API PopLast" ); }

    PrintOnErrors(); // Restore printing of future unknown errors.
    return errcount;
}

int DumpErrorsAndExit( int errcount )
{
    errcount += GetNumTotalErrors();

    //==== Check For API Errors ====//
    while ( GetNumTotalErrors() > 0 )
    {
        ErrorObj err = PopLastError();
        Print( err.GetErrorString() );
    }

    // TestErrorHandling raises one error on purpose to exercise the error
    // system, so it does not count against the test.
    return errcount - 1;
}

int TestAPIScript()
{
    int failure = 0;

    Print( string( "--> Testing API Code" ) );

    string pod1 = AddGeom( "POD", "" );
    string pod2 = AddGeom( "POD", "" );

    Print( string( "---> Test Write/Read" ) );
    WriteVSPFile( "TestWrite.vsp3", SET_ALL );
    ClearVSPModel();
    ReadVSPFile( "TestWrite.vsp3" );

    //==== There Should Be Two Geoms in the File =====//
    array< string > @geom_ids = FindGeoms();
    if ( geom_ids.size() != 2 )                        { Print( "---> Error: API Read/Find  " ); failure++; }

    array< string > @type_array = GetGeomTypes();
    if ( type_array.size() < 1 )                    { Print( "---> Error: API GetGeomTypes  " ); failure++; }
    else if ( type_array[0] != "POD" )                { Print( "---> Error: API GetGeomTypes  " ); failure++; }

    Print( string( "---> Test Cut/Paste" ) );
    string gid0 = AddGeom( "POD", "" );
    SetGeomName( gid0, "ParentPod" );
    CutGeomToClipboard( gid0 );
    PasteGeomClipboard( "" );
    geom_ids = FindGeomsWithName( "ParentPod" );

    if ( geom_ids.size() != 1 )
    {
        Print( "---> Error: API Add/Cut/Paste Geom " );
        return failure + 1;
    }

    Print( string( "---> Test Copy/Paste" ) );
    string gid1 = AddGeom( "POD", geom_ids[0] );
    SetGeomName( gid1, "ChildPod" );
    CopyGeomToClipboard( gid1 );
    PasteGeomClipboard( geom_ids[0] );
    geom_ids = FindGeoms();
    if ( geom_ids.size() != 5 )                { Print( "---> Error: API Copy/Paste Geom  " ); failure++; }

    string geom_name = GetGeomName( gid1 );
    if ( geom_name != "ChildPod" )            { Print( "---> Error: API GetGeomName  " ); failure++; }

    Print( string( "---> Test Get Parm Arrays" ) );
    array< string > @parm_array = GetGeomParmIDs( gid1 );
    if ( parm_array.size() < 1 )            { Print( "---> Error: API GetGeomParmIDs " ); failure++; }

    string lenid = GetParm( gid1, "Length", "Design" );
    if ( !ValidParm( lenid ) )                { Print( "---> Error: API GetParm  " ); failure++; }

    Print( string( "---> Test Get Num XSec Surfs" ) );
    string fuseid = AddGeom( "FUSELAGE", "" );
    int num_xsec_surfs = GetNumXSecSurfs( fuseid );
    if ( num_xsec_surfs != 1 )                { Print( "---> Error: API GetNumXSecSurfs  " ); failure++; }

    string xsec_surf = GetXSecSurf( fuseid, 0 );

    if ( GetNumXSec( xsec_surf ) < 1 )                    { Print( "---> Error: API GetXSecSurf/GetNumXSec  " ); failure++; }

    Print( string( "---> Test Get XSec" ) );
    string xsec = GetXSec( xsec_surf, 0 );
    if ( xsec.size() == 0 )                    { Print( "---> Error: API GetXSec " ); failure++; }

    Print( string( "---> Test Change XSec Shape" ) );
    ChangeXSecShape( xsec_surf, GetNumXSec( xsec_surf ) - 1, XS_ROUNDED_RECTANGLE );

    xsec = GetXSec( xsec_surf, GetNumXSec( xsec_surf ) - 1 );
    if ( GetXSecShape( xsec ) != XS_ROUNDED_RECTANGLE )
    { Print( "---> Error: API ChangeXSec/GetShape " ); failure++; }

    Print( string( "---> Test Change Set XSec W/H" ) );
    SetXSecWidthHeight( xsec, 3.0, 6.0 );
    if ( abs( GetXSecWidth( xsec ) - 3.0 ) > tol )        { Print( "---> Error: API Get/Set Width " ); failure++; }
    if ( abs( GetXSecHeight( xsec ) - 6.0 ) > tol )        { Print( "---> Error: API Get/Set Height " ); failure++; }

    parm_array = GetXSecParmIDs( xsec );
    if ( parm_array.size() < 1 )                        { Print( "---> Error: API GetXSecParmIDs " ); failure++; }

    string wid = GetXSecParm( xsec, "RoundedRect_Width" );
    if ( !ValidParm( wid ) )                            { Print( "---> Error: API GetXSecParm " ); failure++; }

    Print( string( "---> Test Read Fuse File" ) );
    ChangeXSecShape( xsec_surf, 2, XS_FILE_FUSE );
    xsec = GetXSec( xsec_surf, 2 );
    array< vec3d > @vec_array = ReadFileXSec( xsec, "TestXSec.fxs" ); // FIXME: File is not transferred from REPO

    if ( vec_array.size() > 0 )
    {
        vec_array[1] = vec_array[1] * 2.0;
        vec_array[3] = vec_array[3] * 2.0;
        SetXSecPnts( xsec, vec_array );
    }

    double wh_ratio = GetXSecWidth( xsec ) / GetXSecHeight( xsec );
    if ( ( abs( wh_ratio ) - 2.0 ) > tol )                { Print( "---> Error: API Read/Set XSecPnts " ); failure++; }

    Print( string( "---> Test Read Airfoil File" ) );
    ChangeXSecShape( xsec_surf, 1, XS_FILE_AIRFOIL );
    xsec = GetXSec( xsec_surf, 1 );
    ReadFileAirfoil( xsec, "airfoil/N0012_VSP.af" );

    array< vec3d > @up_array = GetAirfoilUpperPnts( xsec );
    array< vec3d > @low_array = GetAirfoilLowerPnts( xsec );

    for ( int i = 0 ; i < int( up_array.size() ) ; i++ )
    {
        up_array[i].scale_y( 2.0 );
        low_array[i].scale_y( 0.5 );
    }

    SetAirfoilPnts( xsec, up_array, low_array );

    //==== Sets ====//
    Print( string( "---> Test Sets" ) );
    if ( GetNumSets() <= 0 )                            { Print( "---> Error: API GetNumSets " ); failure++; }
    SetSetName( 3, "SetFromScript" );
    if ( GetSetName( 3 ) != "SetFromScript" )            { Print( "---> Error: API Get/Set Set Name " ); failure++; }

    array<string> @geom_arr1 = GetGeomSetAtIndex( 3 );
    array<string> @geom_arr2 = GetGeomSet( "SetFromScript" );

    if ( geom_arr1.size() != geom_arr2.size() )            { Print( "---> Error: API GetGeomSet " ); failure++; }

    SetSetFlag( fuseid, 3, true );
    if ( !GetSetFlag( fuseid, 3 ) )                        { Print( "---> Error: API Set/Get Set Flag " ); failure++; }

    //==== Parms ====//
    Print( string( "---> Test Parms" ) );
    SetParmVal( wid, 23.0 );
    if ( abs( GetParmVal( wid ) - 23 ) > tol )                { Print( "---> Error: API Parm Val Set/Get " ); failure++; }

    SetParmUpperLimit( wid, 13.0 );
    if ( abs( GetParmVal( wid ) - 13 ) > tol )                { Print( "---> Error: API SetParmUpperLimit " ); failure++; }

    SetParmUpperLimit( wid, 20.0 );
    SetParmLowerLimit( wid, 15.0 );
    if ( abs( GetParmVal( wid ) - 15 ) > tol )                { Print( "---> Error: API SetParmLowerLimit " ); failure++; }

    if ( GetParmType( wid ) != PARM_DOUBLE_TYPE )        { Print( "---> Error: API GetParmType " ); failure++; }
    if ( GetParmName( wid ) != "RoundedRect_Width" )    { Print( "---> Error: API GetParmName " ); failure++; }

    string cid = GetParmContainer( wid );
    if ( cid.size() == 0 )                                { Print( "---> Error: API GetParmContainer " ); failure++; }

    Update();

    ClearVSPModel();

    //==== Write Some Fake Test Results =====//
    WriteTestResults();

    Print( string( "---> Test Results" ) );
    array<string> @res_names_vec = GetAllResultsNames();

    bool found_name = false;
    for( int n = 0 ; n < int( res_names_vec.length() ) ; n++ )
        if ( res_names_vec[n] == "Test_Results" ) { found_name = true; }

    if ( !found_name )                                        { Print( "---> Error: API GetAllResultsNames" ); failure++; }


    if ( GetNumResults( "Test_Results" ) != 2 )                { Print( "---> Error: API GetNumResults" ); failure++; }
    string res_id = FindResultsID( "Test_Results" );
    if ( res_id.size() == 0 )                                { Print( "---> Error: API FindResultsID" ); failure++; }

    array< string > @data_names = GetAllDataNames( res_id );
    if ( data_names.size() != 5 )                            { Print( "---> Error: API GetAllDataNames" ); failure++; }

    if ( GetNumData( res_id, "Test_Int" ) != 2 )            { Print( "---> Error: API GetNumData " ); failure++; }
    array<int> @int_arr = GetIntResults( res_id, "Test_Int", 0 );
    if ( int_arr[0] != 1 )                                    { Print( "---> Error: API GetIntResults" ); failure++; }
    int_arr = GetIntResults( res_id, "Test_Int", 1 );
    if ( int_arr[0] != 2 )                                    { Print( "---> Error: API GetIntResults" ); failure++; }

    array<string> @str_arr = GetStringResults( res_id, "Test_String" );
    if ( str_arr[0] != "This Is A Test" )                    { Print( "---> Error: API GetStringResults" ); failure++; }

    array<double> @double_arr = GetDoubleResults( res_id, "Test_Double_Vec" );
    if ( double_arr.size() != 5 )                            { Print( "---> Error: API GetDoubleResults" ); failure++; }
    if ( abs( double_arr[4] - 4 ) > tol )                    { Print( "---> Error: API GetDoubleResults" ); failure++; }

    res_id = FindLatestResultsID( "Test_Results" );
    array<vec3d> @vec3d_vec = GetVec3dResults( res_id, "Test_Vec3d" );
    if ( !CloseVec3d( vec3d_vec[0], vec3d( 1.0, 2.0, 4.0 ), tol ) )        { Print( "---> Error: API GetVec3dResults" ); failure++; }

    //==== Test Mass Props ====//
    Print( string( "---> Test Mass Properties" ) );
    gid0 = AddGeom( "POD", "" );
    string sliced_id = ComputeMassProps( 0, 4 );
    string mass_res_id = FindLatestResultsID( "Mass_Properties" );
    double_arr = GetDoubleResults( mass_res_id, "Total_Mass" );
    if ( double_arr.size() != 1 )                                    { Print( "---> Error: API ComputeMassProps" ); failure++; }
    CutGeomToClipboard( mass_res_id );

    ClearVSPModel();

    //==== Test Comp Geom ====//
    Print( string( "---> Test Comp Geom" ) );
    gid1 = AddGeom( "POD", "" );
    string p0 = GetParm( gid1, "Length", "Design" );
    string p1 = GetParm( gid1, "X_Location", "XForm" );
    SetParmVal( p0, 12.0 );
    SetParmVal( p1, 4.0 );

    string mesh_id = ComputeCompGeom( 0, false, 0 );
    string comp_res_id = FindLatestResultsID( "Comp_Geom" );
    double_arr = GetDoubleResults( comp_res_id, "Wet_Area" );
    if ( double_arr.size() != 1 )                                    { Print( "---> Error: API ComputeCompGeom" ); failure++; }

    //==== Test Comp Geom Mesh Results ====//
    string mesh_geom_res_id = CreateGeomResults( mesh_id, "Comp_Mesh" );
    int_arr = GetIntResults( mesh_geom_res_id, "Num_Tris" );
    if ( int_arr[0] < 4 )                                            { Print( "---> Error: API CreateGeomResults" ); failure++; }
    CutGeomToClipboard( mesh_id );

    //==== Test Geom Results ====//
    Print( string( "---> Test Geom Export Results" ) );
    string geom_xsec_results = CreateGeomResults( gid1, "Geom_XSecs" );
    file f;
    if( f.open( "geom_xsecs.stl", "w" ) >= 0 )
    {
        f.writeString( "solid " + "\r\n" );

        int_arr = GetIntResults( geom_xsec_results, "Num_XSecs" );
        int num_xsecs = int_arr[0];

        for ( int i = 0 ; i < num_xsecs ; i++ )
        {
            array<vec3d> @p_vec = GetVec3dResults( geom_xsec_results, "XSec_Pnts", i );
            for ( int p = 0 ; p < int( p_vec.size() ) - 1 ; p++ )
            {
                f.writeString( "facet normal 1 0 0 \r\n" );
                f.writeString( "  outer loop \r\n" );
                f.writeString( "    vertex " + p_vec[p].x() + " " + p_vec[p].y() + " " + p_vec[p].z() + "\r\n" );
                f.writeString( "    vertex " + p_vec[p + 1].x() + " " + p_vec[p + 1].y() + " " + p_vec[p + 1].z() + "\r\n" );
                f.writeString( "    vertex " + ( p_vec[p].x() + 0.1 ) + " " + p_vec[p].y() + " " + p_vec[p].z() + "\r\n" );
                f.writeString( "  endloop \r\n" );
                f.writeString( "endfacet \r\n" );
            }
        }

        f.writeString( "endsolid \r\n" );
        f.close();
    }


    //==== Test Plane Slice ====//
    Print( string( "---> Test Plane Slice" ) );
    string slice_mesh_id = ComputePlaneSlice( 0, 6, vec3d( 0.0, 0.0, 1.0 ), true );
    string pslice_results = FindLatestResultsID( "Slice" );
    double_arr = GetDoubleResults( pslice_results, "Slice_Area" );
    if ( double_arr.size() != 6 )                                    { Print( "---> Error: API ComputePlaneSlice" ); failure++; }

    string slice_geom_results = CreateGeomResults( slice_mesh_id, "Slice_Mesh" );
    int_arr = GetIntResults( slice_geom_results, "Num_Slices" );
    if ( int_arr[0] != 4 )                                            { Print( "---> Error: API CreateGeomResults (Slice)" ); failure++; }

    //===== Write File With Slice Triangles  ====//
    if( f.open( "slice_tris.stl", "w" ) >= 0 )
    {
        int num_slices = int_arr[0];
        f.writeString( "solid " + "\r\n" );
        for ( int s = 0 ; s < num_slices ; s++ )
        {
            array<vec3d> @p0_vec =  GetVec3dResults( slice_geom_results, "Slice_Tris_Pnt_0", s );
            array<vec3d> @p1_vec =  GetVec3dResults( slice_geom_results, "Slice_Tris_Pnt_1", s );
            array<vec3d> @p2_vec =  GetVec3dResults( slice_geom_results, "Slice_Tris_Pnt_2", s );

            for ( int p = 0 ; p < int( p0_vec.size() ) ; p++ )
            {
                f.writeString( "facet normal 1 0 0 \r\n" );
                f.writeString( "  outer loop \r\n" );
                f.writeString( "    vertex " + p0_vec[p].x() + " " + p0_vec[p].y() + " " + p0_vec[p].z() + "\r\n" );
                f.writeString( "    vertex " + p1_vec[p].x() + " " + p1_vec[p].y() + " " + p1_vec[p].z() + "\r\n" );
                f.writeString( "    vertex " + p2_vec[p].x() + " " + p2_vec[p].y() + " " + p2_vec[p].z() + "\r\n" );
                f.writeString( "  endloop \r\n" );
                f.writeString( "endfacet \r\n" );
            }
        }
        f.writeString( "endsolid \r\n" );
        f.close();
    }

    CutGeomToClipboard( slice_mesh_id );

    ClearVSPModel();

    array<string> @ctr_arr = FindContainers();
    if ( ctr_arr.size() != 28 )            { Print( "---> Error: API FindContainers " ); failure++; }

    ctr_arr = FindContainersWithName( "UserParms" );
    if ( ctr_arr.size() != 1 )            { Print( "---> Error: API FindContainersWithName " ); failure++; }

    string user_ctr = FindContainer( "UserParms", 0 );
    if ( user_ctr.size() == 0 )                                { Print( "---> Error: API FindContainer" ); failure++; }
    if ( res_id.size() == 0 )                                { Print( "---> Error: API FindContainer" ); failure++; }

    if ( GetContainerName( user_ctr ) != "UserParms" )         { Print( "---> Error: API GetContainerName" ); failure++; }

    array<string> @grp_arr = FindContainerGroupNames( user_ctr );
    if ( grp_arr.size() != 1 )                                { Print( "---> Error: API FindContainerGroupNames " ); failure++; }
    if ( grp_arr[0] != "User_Group" )                         { Print( "---> Error: API FindContainerGroupNames" ); failure++; }

    string uparm = FindParm( user_ctr, "User_0", "User_Group" );
    if ( uparm.size() == 0 )                                { Print( "---> Error: API FindParm" ); failure++; }

    array<string> @uparm_arr  = FindContainerParmIDs( user_ctr );
    if ( uparm_arr.size() == 0 )                            { Print( "---> Error: API FindContainerParmIDs" ); failure++; }

    ClearVSPModel();

    return failure;
}

int TestVarPreset()
{
    int failure = 0;

    Print( string( "--> Testing Variable Presets" ) );

    // The variable preset API is addressed by group and setting id now.  It used
    // to be addressed by name, with a single implicit "current" group, which is
    // why this test needed rewriting.

    string pod1 = AddGeom( "POD", "" );
    SetGeomName( pod1, "PodGeom" );
    Update();

    Print( string( "--> Testing Add Group/Setting" ) );

    string tess_group = AddVarPresetGroup( "Tess" );
    if ( GetVarPresetGroups().size() != 1 )                 { Print( "---> Error: API AddVarPresetGroup" ); failure++; }

    string coarse = AddVarPresetSetting( tess_group, "Coarse" );
    if ( GetVarPresetSettings( tess_group ).size() != 1 )   { Print( "---> Error: API AddVarPresetSetting" ); failure++; }

    string p1 = FindParm( pod1, "Tess_U", "Shape" );
    string p2 = FindParm( pod1, "Tess_W", "Shape" );

    AddVarPresetParm( tess_group, p1 );
    AddVarPresetParm( tess_group, p2 );

    array < string > @p_IDs = GetVarPresetParmIDs( tess_group );
    if ( p_IDs.size() != 2 )                                { Print( "---> Error: API AddVarPresetParm" ); failure++; }

    //==== Store a value per parm in the Coarse setting ====//
    SetVarPresetParmVal( tess_group, coarse, p1, 5 );
    SetVarPresetParmVal( tess_group, coarse, p2, 5 );

    if ( !closeTo( GetVarPresetParmVal( tess_group, coarse, p1 ), 5, 1e-9 ) ||
         !closeTo( GetVarPresetParmVal( tess_group, coarse, p2 ), 5, 1e-9 ) )
    {
        Print( "---> Error: API SetVarPresetParmVal" );
        failure++;
    }

    //==== A second setting in the same group holds different values ====//
    string fine = AddVarPresetSetting( tess_group, "Fine" );
    if ( GetVarPresetSettings( tess_group ).size() != 2 )   { Print( "---> Error: API AddVarPresetSetting" ); failure++; }

    SetVarPresetParmVal( tess_group, fine, p1, 25 );
    SetVarPresetParmVal( tess_group, fine, p2, 25 );

    array < double > @fine_vals = GetVarPresetParmVals( fine );
    if ( fine_vals.size() != 2 )                            { Print( "---> Error: API GetVarPresetParmVals" ); failure++; }
    for( int i = 0; i < int( fine_vals.size() ); i++ )
    {
        if ( !closeTo( fine_vals[i], 25, 1e-9 ) )           { Print( "---> Error: API SetVarPresetParmVal" ); failure++; }
    }

    //==== Applying a setting has to push the stored values onto the parms ====//
    ApplyVarPresetSetting( tess_group, coarse );
    Update();
    if ( !closeTo( GetParmVal( p1 ), 5, 1e-9 ) || !closeTo( GetParmVal( p2 ), 5, 1e-9 ) )
    {
        Print( "---> Error: API ApplyVarPresetSetting did not apply Coarse" );
        failure++;
    }

    ApplyVarPresetSetting( tess_group, fine );
    Update();
    if ( !closeTo( GetParmVal( p1 ), 25, 1e-9 ) || !closeTo( GetParmVal( p2 ), 25, 1e-9 ) )
    {
        Print( "---> Error: API ApplyVarPresetSetting did not apply Fine" );
        failure++;
    }

    //==== A second group is independent of the first ====//
    string cfg_group = AddVarPresetGroup( "Config" );
    if ( GetVarPresetGroups().size() != 2 )                 { Print( "---> Error: API AddVarPresetGroup" ); failure++; }

    string dflt = AddVarPresetSetting( cfg_group, "Default" );
    if ( GetVarPresetSettings( cfg_group ).size() != 1 )    { Print( "---> Error: API AddVarPresetSetting" ); failure++; }

    string p3 = FindParm( pod1, "Y_Rel_Rotation", "XForm" );
    string p4 = FindParm( pod1, "Z_Rel_Rotation", "XForm" );

    AddVarPresetParm( cfg_group, p3 );
    AddVarPresetParm( cfg_group, p4 );

    @p_IDs = GetVarPresetParmIDs( cfg_group );
    if ( p_IDs.size() != 2 )                                { Print( "---> Error: API AddVarPresetParm" ); failure++; }

    string rotated = AddVarPresetSetting( cfg_group, "Rotated" );
    SetVarPresetParmVal( cfg_group, rotated, p3, -45 );
    SetVarPresetParmVal( cfg_group, rotated, p4, -45 );

    ApplyVarPresetSetting( cfg_group, rotated );
    Update();
    if ( !closeTo( GetParmVal( p3 ), -45, 1e-9 ) || !closeTo( GetParmVal( p4 ), -45, 1e-9 ) )
    {
        Print( "---> Error: API ApplyVarPresetSetting did not apply Rotated" );
        failure++;
    }

    //==== Deleting a parm drops it from the group ====//
    DeleteVarPresetParm( cfg_group, p4 );
    @p_IDs = GetVarPresetParmIDs( cfg_group );
    if ( p_IDs.size() != 1 )                                { Print( "---> Error: API DeleteVarPresetParm" ); failure++; }

    //==== Deleting settings and groups drops them too ====//
    DeleteVarPresetSetting( cfg_group, rotated );
    if ( GetVarPresetSettings( cfg_group ).size() != 1 )    { Print( "---> Error: API DeleteVarPresetSetting" ); failure++; }

    DeleteVarPresetGroup( cfg_group );
    if ( GetVarPresetGroups().size() != 1 )                 { Print( "---> Error: API DeleteVarPresetGroup" ); failure++; }

    DeleteVarPresetGroup( tess_group );
    if ( GetVarPresetGroups().size() != 0 )                 { Print( "---> Error: API DeleteVarPresetGroup" ); failure++; }

    ClearVSPModel();

    return failure;
}
