#include <iostream>
#include <cassert>
using namespace std;

#include "pictapi.h"

#define PAIRWISE 2

#define checkNull( x )                                    \
    if( nullptr == x )                                    \
    {                                                     \
        wcout << L"Error: Out of memory" << endl;         \
        goto cleanup;                                     \
    }

#define checkRetCode( x )                                 \
    switch( x )                                           \
    {                                                     \
    case PICT_SUCCESS:                                    \
        break;                                            \
    case PICT_OUT_OF_MEMORY:                              \
        wcout << L"Error: Out of memory" << endl;         \
        goto cleanup;                                     \
    case PICT_GENERATION_ERROR:                           \
        wcout << L"Error: Internal engine error" << endl; \
        goto cleanup;                                     \
    default:                                              \
        assert( ! L"Unexpected error code" );             \
        goto cleanup;                                     \
    }

void __cdecl wmain()
{
    PICT_RET_CODE ret = PICT_SUCCESS;

    PICT_HANDLE task = PictCreateTask();

    //
    // In a general case,  models might form a tree-like hierarchy
    // Let's try with only one model for now,  pairwise is default
    //

    PICT_HANDLE model = PictCreateModel();
    checkNull( task );
    checkNull( model );

    //
    // The root of the model tree should be attached  to  the task
    // In this simple case,  we will attach the only model we have
    //
    
    PictSetRootModel( task, model );

    //
    // We will add five parameters with different number of values
    // to the model.  Let's store pointers to our parameters since
    // we will use them to define exclusions.
    //
    // The order of combinations for each parameter must be greater 
    // than 0  and less or equal the number  of parameters defined 
    // in the model, we will use order = 2, i.e. pairwise.
    //
    // An array of weights determines which values are more likely
    // to be picked.

    //
    // The order of calls here  determines  the order of values in 
    // the resulting test cases.
    //

    unsigned int weights[] = {1, 2, 1, 1};
    PICT_HANDLE p1 = PictAddParameter( model, 4, PAIRWISE, weights );
    checkNull( p1 );

    PICT_HANDLE p2 = PictAddParameter( model, 3, PAIRWISE );
    checkNull( p2 );

    PICT_HANDLE p3 = PictAddParameter( model, 5, PAIRWISE );
    checkNull( p3 );

    PICT_HANDLE p4 = PictAddParameter( model, 2, PAIRWISE );
    checkNull( p4 );

    PICT_HANDLE p5 = PictAddParameter( model, 4, PAIRWISE );
    checkNull( p5 );

    //
    // Exclusions determine  which combinations should not show up
    // in the output. An exclusion is a list of param-value pairs.
    // Make sure you're not exceeding a value count for each param
    // when using 0-based value indices.
    //

    //
    // Exclude a combination of
    //   (1st value of 1st param) and
    //   (1st value of 2nd param)
    //

    const size_t EXCLUSION_1_SIZE = 2;

    PICT_EXCLUSION_ITEM excl1[ EXCLUSION_1_SIZE ];
    
    excl1[ 0 ].Parameter  = p1;
    excl1[ 0 ].ValueIndex = 0;
    
    excl1[ 1 ].Parameter  = p2;
    excl1[ 1 ].ValueIndex = 0;

    ret = PictAddExclusion( task, excl1, EXCLUSION_1_SIZE );
    checkRetCode( ret );

    //
    // Exclude a combination of
    //   (2nd value of 4th param) and
    //   (3rd value of 5th param)
    //

    const size_t EXCLUSION_2_SIZE = 2;

    PICT_EXCLUSION_ITEM excl2[ EXCLUSION_2_SIZE ];

    excl2[ 0 ].Parameter  = p4;
    excl2[ 0 ].ValueIndex = 1;
    
    excl2[ 1 ].Parameter  = p5;
    excl2[ 1 ].ValueIndex = 2;

    ret = PictAddExclusion( task, excl2, EXCLUSION_2_SIZE );
    checkRetCode( ret );

    //
    // Seeding rows tell the engine which combinations must appear
    // in the output. If they don't violate exclusions, of course.
    // Let's add one of those; it is similar to adding exclusions.
    //

    //
    // Make sure that a test case  where all parameters are set to
    // their 2nd values is present in the generated output.  Again
    // the 0-based indices cannot go beyond the number of possible
    // values for any of the parameters.
    //

    const size_t SEED_1_SIZE = 5;

    PICT_SEED_ITEM seed1[ SEED_1_SIZE ];
    
    seed1[ 0 ].Parameter  = p1;
    seed1[ 0 ].ValueIndex = 1;
    
    seed1[ 1 ].Parameter  = p2;
    seed1[ 1 ].ValueIndex = 1;
    
    seed1[ 2 ].Parameter  = p3;
    seed1[ 2 ].ValueIndex = 1;
    
    seed1[ 3 ].Parameter  = p4;
    seed1[ 3 ].ValueIndex = 1;
    
    seed1[ 4 ].Parameter  = p5;
    seed1[ 4 ].ValueIndex = 1;

    ret = PictAddSeed( task, seed1, SEED_1_SIZE );
    checkRetCode( ret );

    //
    // The main event: generation.
    //

    ret = PictGenerate( task );
    checkRetCode( ret );

    //
    // The result is a collection of rows.Each row is a collection
    // of values:  one value for every parameter in the order they
    // were added to the models; i.e. in the order of AddParameter
    // calls. We can only get one row at a time.
    //
    // Per exclusion 1, we should not see "0 0" as first two items
    // in any row. Per exclusion 2,  "1 2" will not appear as last
    // two items in any of the rows.  Also, per seed 1 we must see
    // a test "1 1 1 1 1".
    //

    //
    // First let's allocate a piece of memory big enough to hold a
    // row. Every row of the result is of the same size because it
    // contains the same number of values. Each value corresponds
    // to one parameter we defined earlier.
    //
    // We could allocate the memory by hand since we know how many
    // parameters we have in all models of the task  (moreover, we 
    // have a handy PictGetTotalParameterCount function available)
    // but PictAllocateResultBuffer can do it for us.
    //

    PICT_RESULT_ROW row = PictAllocateResultBuffer( task );
    checkNull( row );

    //
    // We will get rows by repeatedly calling PictGetNextResultRow
    // until it returns false. Before we do that however a call to
    // PictResetResultFetching will reset the retrieval procedure.
    //
    // If  the result fetching  ever needs to be repeated for this
    // task,  a call to PictResetResultFetching  will let us start 
    // over.
    //

    size_t paramCount = PictGetTotalParameterCount( task );

    PictResetResultFetching( task );

    while( PictGetNextResultRow( task, row ))
    {
        for( size_t index = 0; index < paramCount; ++index )
        {
            wcout << static_cast<unsigned int>( row[ index ] )<< L" ";
        }
        wcout << endl;
    }

    //
    // Memory allocated for the buffer needs to be freed.
    //

    PictFreeResultBuffer( row );

cleanup:

    if( model != nullptr )
    {
        PictDeleteModel( model );
    }

    if( task != nullptr )
    {
        PictDeleteTask( task );
    }
};