/*===========================================================================
*
*                            PUBLIC DOMAIN NOTICE
*               National Center for Biotechnology Information
*
*  This software/database is a "United States Government Work" under the
*  terms of the United States Copyright Act.  It was written as part of
*  the author's official duties as a United States Government employee and
*  thus cannot be copyrighted.  This software/database is freely available
*  to the public for use. The National Library of Medicine and the U.S.
*  Government have not placed any restriction on its use or reproduction.
*
*  Although all reasonable efforts have been taken to ensure the accuracy
*  and reliability of the software and data, the NLM and the U.S.
*  Government do not and cannot warrant the performance or results that
*  may be obtained by using this software or data. The NLM and the U.S.
*  Government disclaim all warranties, express or implied, including
*  warranties of performance, merchantability or fitness for any particular
*  purpose.
*
*  Please cite the author in any work or product based on this material.
*
* ===========================================================================
*
*/

#include "context.h"

#ifndef _h_helper_
#include "helper.h"
#endif

/*
 * helper-function to set a string inside the context
 * ( makes a copy ) with error detection
*/
static rc_t context_set_str( char **dst, const char *src ) {
    size_t len;
    if ( NULL == dst ) {
        return RC( rcVDB, rcNoTarg, rcWriting, rcParam, rcNull );
    }
    if ( NULL != *dst ) {
        free( *dst );
        *dst = NULL;
    }
    if ( NULL == src ) {
        return RC( rcVDB, rcNoTarg, rcWriting, rcParam, rcNull );
    }
    *dst = string_dup_measure ( src, &len );
    if ( 0 == len ) {
        return RC( rcVDB, rcNoTarg, rcWriting, rcItem, rcEmpty );
    }
    if ( NULL == *dst ) {
        return RC( rcVDB, rcNoTarg, rcWriting, rcMemory, rcExhausted );
    }
    return 0;
}

/*
 * helper-function to initialize the values of the context
*/
static void context_init_values( p_context ctx ) {
    ctx -> src_path = NULL;
    ctx -> dst_path = NULL;
    ctx -> kfg_path = NULL;
    ctx -> table = NULL;
    ctx -> columns = NULL;
    ctx -> excluded_columns = NULL;
    ctx -> platform_id = 0;
    ctx -> src_schema_list = NULL;

    ctx -> usage_requested = false;
    ctx -> ignore_reject = false;
    ctx -> ignore_redact = false;
    ctx -> dont_check_accession = false;
    ctx -> reindex = false;
    ctx -> show_redact = false;
    ctx -> show_meta = false;
    ctx -> md5_mode = MD5_MODE_AUTO;
    ctx -> force_kcmInit = false;
    ctx -> force_unlock = false;

    ctx -> dont_remove_target = false;
    config_values_init( &( ctx -> config ) );
    redact_vals_init( &( ctx -> rvals ) );
    ctx -> dst_schema_tabname = NULL;
    ctx -> legacy_schema_file = NULL;
    ctx -> legacy_dont_copy = NULL;
}

/*
 * generates a new context, initializes values
*/
rc_t context_init( context **ctx ) {
    rc_t rc;
    if ( NULL == ctx ) {
        return RC( rcVDB, rcNoTarg, rcConstructing, rcParam, rcNull );
    }
    ( *ctx ) = ( p_context )calloc( 1, sizeof( context ) );
    if ( NULL == *ctx ) {
        return RC( rcVDB, rcNoTarg, rcConstructing, rcMemory, rcExhausted );
    }
    context_init_values( *ctx );
    rc = num_gen_make( &( ( *ctx ) -> row_generator ) );
    DISP_RC( rc, "num_gen_make() failed" );
    return rc;
}

/*
 * destroys a context, frees all pointers the context owns
*/
rc_t context_destroy( p_context ctx ) {
    if ( NULL == ctx ) {
        return RC( rcVDB, rcNoTarg, rcDestroying, rcParam, rcNull );
    }
    if ( NULL != ctx -> src_path ) {
        free( ( void* )ctx -> src_path );
        ctx -> src_path = NULL;
    }
    if ( NULL != ctx -> dst_path ) {
        free( ( void* )ctx -> dst_path );
        ctx -> dst_path = NULL;
    }
    if ( NULL != ctx -> kfg_path ) {
        free( ( void* )ctx -> kfg_path );
        ctx -> kfg_path = NULL;
    }
    if ( NULL != ctx -> src_schema_list ) {
        VNamelistRelease( ctx -> src_schema_list );
        ctx -> src_schema_list = NULL;
    }
    if ( NULL != ctx -> table ) {
        free( ( void* )ctx -> table );
        ctx -> table = NULL;
    }
    if ( NULL != ctx -> columns ) {
        free( ( void* )ctx -> columns );
        ctx -> columns = NULL;
    }
    if ( NULL != ctx -> excluded_columns ) {
        free( ( void* )ctx -> excluded_columns );
        ctx -> excluded_columns = NULL;
    }
    num_gen_destroy( ctx -> row_generator );
    config_values_destroy( &( ctx -> config ) );
    redact_vals_destroy( ctx -> rvals );
    if ( NULL != ctx -> dst_schema_tabname ) {
        free( ( void* )ctx -> dst_schema_tabname );
        ctx -> dst_schema_tabname = NULL;
    }
    if ( NULL != ctx -> legacy_schema_file ) {
        free( ( void* )ctx -> legacy_schema_file );
        ctx -> legacy_schema_file = NULL;
    }
    if ( NULL != ctx -> legacy_dont_copy ) {
        free( ( void* )ctx -> legacy_dont_copy );
        ctx -> legacy_dont_copy = NULL;
    }
    free( ctx );
    return 0;
}

/*
 * clear's the number-generator and sets the given intervall
*/
rc_t context_set_range( p_context ctx, 
                        const int64_t first, const uint64_t count ) {
    rc_t rc = num_gen_clear( ctx -> row_generator );
    if ( 0 == rc ) {
        rc = num_gen_add( ctx -> row_generator, first, count );
    }
    return rc;
}

/*
 * performs the range check to trim the internal number
 * generator to the given range
*/
rc_t context_range_check( p_context ctx, 
                          const int64_t first, const uint64_t count ) {
    return num_gen_range_check( ctx -> row_generator, first, count );
}

/*
 * helper-function to set the source-path
*/
static rc_t context_set_src_path( p_context ctx, const char *src ) {
    return context_set_str( ( char** )&( ctx -> src_path ), src );
}

/*
 * helper-function to set the destination-path
*/
static rc_t context_set_dst_path( p_context ctx, const char *src ) {
    return context_set_str( ( char** )&( ctx -> dst_path ), src );
}

/*
 * helper-function to set the path for additional kfg-files
*/
static rc_t context_set_kfg_path( p_context ctx, const char *src ) {
    rc_t rc = context_set_str( ( char** )&( ctx -> kfg_path ), src );
    if ( NULL == ctx -> kfg_path ) {
        context_set_kfg_path( ctx, "." );
    }
    return rc;
}

/*
 * helper-function to set the name of the source-table
*/
static rc_t context_set_table( p_context ctx, const char *src ) {
    rc_t rc;
    if ( NULL == ctx || NULL == src ) {
        return RC( rcVDB, rcNoTarg, rcWriting, rcParam, rcNull );
    }
    rc = context_set_str( (char**)&(ctx->table), src );
    DISP_RC( rc, "context_set_table:context_set_str() failed" );
    return rc;
}

#if ALLOW_COLUMN_SPEC
static rc_t context_set_columns( p_context ctx, const char *src ) {
    rc_t rc;
    if ( NULL == ctx || NULL == src ) {
        return RC( rcVDB, rcNoTarg, rcWriting, rcParam, rcNull );
    }
    rc = context_set_str( (char**)&(ctx->columns), src );
    DISP_RC( rc, "context_set_columns:dump_context_set_str() failed" );
    return rc;
}
#endif

static rc_t context_set_excluded_columns( p_context ctx, const char *src ) {
    rc_t rc;
    if ( NULL == ctx || NULL == src ) {
        return RC( rcVDB, rcNoTarg, rcWriting, rcParam, rcNull );
    }
    rc = context_set_str( (char**)&(ctx->excluded_columns), src );
    DISP_RC( rc, "context_set_excluded_columns:dump_context_set_str() failed" );
    return rc;
}

static rc_t context_set_md5_mode( p_context ctx, const char *src ) {
    if ( NULL == ctx ) {
        return RC( rcVDB, rcNoTarg, rcWriting, rcParam, rcNull );
    }
    ctx -> md5_mode = MD5_MODE_AUTO;
    if ( NULL == src ) {
        return RC( rcVDB, rcNoTarg, rcWriting, rcParam, rcNull );
    }
    switch( src[ 0 ] ) {
        case 'O' :
        case '1' :
        case 'o' : ctx -> md5_mode = MD5_MODE_ON; break;

        case '0' :
        case 'x' :
        case 'X' : ctx -> md5_mode = MD5_MODE_OFF; break;
    }
    return 0;
}

static rc_t context_set_blob_checksum( p_context ctx, const char *src ) {
    if ( NULL == ctx ) {
        return RC( rcVDB, rcNoTarg, rcWriting, rcParam, rcNull );
    }
    ctx -> blob_checksum = BLOB_CHECKSUM_AUTO;
    if ( NULL == src ) {
        return RC( rcVDB, rcNoTarg, rcWriting, rcParam, rcNull );
    }
    switch( src[ 0 ] ) {
        case 'O' :
        case '1' :
        case 'o' : ctx -> blob_checksum = BLOB_CHECKSUM_CRC32; break;

        case '5' :
        case 'M' :
        case 'm' : ctx -> blob_checksum = BLOB_CHECKSUM_MD5; break;

        case '0' :
        case 'x' :
        case 'X' : ctx -> blob_checksum = BLOB_CHECKSUM_OFF; break;
    }
    return 0;
}

static rc_t context_set_row_range( p_context ctx, const char *src ) {
    rc_t rc;
    if ( ( NULL == ctx )||( NULL == src ) ) {
        rc = RC( rcVDB, rcNoTarg, rcWriting, rcParam, rcNull );
    } else {
        rc = num_gen_parse( ctx -> row_generator, src );
    }
    return rc;
}

static bool context_check_if_usage_necessary( p_context ctx ) {
    if ( NULL == ctx ) { return false; }
    if ( NULL == ctx -> src_path || NULL == ctx -> dst_path ) {
        ctx -> usage_requested = true;
    }
    return ctx -> usage_requested;
}

static rc_t context_evaluate_arguments( const Args *my_args, p_context ctx ) {
    uint32_t count, idx;
    rc_t rc = ArgsParamCount( my_args, &count );
    DISP_RC( rc, "ArgsParamCount() failed" );
    if ( 0 != rc ) { return rc; }

    for ( idx = 0; idx < count && rc == 0; ++idx )
    {
        const char *value = NULL;
        rc = ArgsParamValue( my_args, idx, (const void **)&value );
        DISP_RC( rc, "ArgsParamValue() failed" );
        if ( rc == 0 )
        {
            switch( idx )
            {
            case 0 : rc = context_set_src_path( ctx, value );
                     DISP_RC( rc, "context_set_src_path() failed" );
                     break;

            case 1 : rc = context_set_dst_path( ctx, value );
                     DISP_RC( rc, "context_set_dst_path() failed" );
                     break;

            }
        }
    }
    return rc;
}

static bool context_get_bool_option( const Args *my_args,
                                     const char *name,
                                     const bool def ) {
    bool res = def;
    uint32_t count = 0;
    rc_t rc = ArgsOptionCount( my_args, name, &count );
    if ( ( 0 == rc ) && ( count > 0 ) ) {
        res = true;
    }
    return res;
}

static const char* context_get_str_option( const Args *my_args,
                                           const char *name ) {
    const char* res = NULL;
    uint32_t count;
    rc_t rc = ArgsOptionCount( my_args, name, &count );
    if ( ( 0 == rc )&&( count > 0 ) ) {
        rc = ArgsOptionValue( my_args, name, 0, (const void **)&res );
    }
    return res;
}

/*
 * returns the number of schema's given on the commandline
*/
uint32_t context_schema_count( p_context ctx ) {
    uint32_t res = 0;
    if ( NULL != ctx ) {
        if ( 0 != ctx -> src_schema_list ) {
            uint32_t count;
            if ( 0 == VNameListCount( ctx -> src_schema_list, &count ) ) {
                res = count;
            }
        }
    }
    return res;
}

static void context_evaluate_options( const Args *my_args, p_context ctx ) {
    if ( ( NULL != my_args ) && ( NULL != ctx ) ) {

        ctx -> dont_check_accession = context_get_bool_option( my_args, OPTION_WITHOUT_ACCESSION, false );
        ctx -> ignore_reject = context_get_bool_option( my_args, OPTION_IGNORE_REJECT, true );
        ctx -> ignore_redact = context_get_bool_option( my_args, OPTION_IGNORE_REDACT, false );
        ctx -> show_matching = context_get_bool_option( my_args, OPTION_SHOW_MATCHING, false );
        ctx -> show_progress = context_get_bool_option( my_args, OPTION_SHOW_PROGRESS, false );
        ctx -> ignore_incomp = context_get_bool_option( my_args, OPTION_IGNORE_INCOMP, false );
        ctx -> reindex       = context_get_bool_option( my_args, OPTION_REINDEX, true );
        ctx -> show_redact   = context_get_bool_option( my_args, OPTION_SHOW_REDACT, false );
        ctx -> show_meta     = context_get_bool_option( my_args, OPTION_SHOW_META, false );
        ctx -> force_kcmInit = context_get_bool_option( my_args, OPTION_FORCE, false );
        ctx -> force_unlock  = context_get_bool_option( my_args, OPTION_UNLOCK, false );

        context_set_md5_mode( ctx, context_get_str_option( my_args, OPTION_MD5_MODE ) );
        context_set_blob_checksum( ctx, context_get_str_option( my_args, OPTION_BLOB_CHECKSUM ) );

    #if ALLOW_EXTERNAL_CONFIG
        context_set_kfg_path( ctx, context_get_str_option( my_args, OPTION_KFG_PATH ) );
    #endif

        context_set_table( ctx, context_get_str_option( my_args, OPTION_TABLE ) );
    #if ALLOW_COLUMN_SPEC
        context_set_columns( ctx, context_get_str_option( my_args, OPTION_COLUMNS ) );
    #endif
        context_set_excluded_columns( ctx, context_get_str_option( my_args, OPTION_EXCLUDED_COLUMNS ) );

        {
            const char * row_range = context_get_str_option( my_args, OPTION_ROWS );
            context_set_row_range( ctx, row_range );
        }
        nlt_make_VNamelist_from_string( &( ctx -> src_schema_list ), 
                                        context_get_str_option( my_args, OPTION_SCHEMA ) );
    }
}

/*
 * reads all arguments and options, fills the context
 * with copies (if strings) of this data
*/
rc_t context_capture_arguments_and_options( const Args * args, p_context ctx ) {
    rc_t rc = context_evaluate_arguments( args, ctx );
    DISP_RC( rc, "evaluate_arguments() failed" );
    if ( 0 == rc ) {
        context_evaluate_options( args, ctx );
        context_check_if_usage_necessary( ctx );

        rc = ArgsHandleLogLevel( args );
        DISP_RC( rc, "ArgsHandleLogLevel() failed" );
    }
    return rc;
}
