#include <stdio.h>

#define SCPI_ARGS_N 1
#define SCPI_ARGS_MANDATORY_N 0
#include "app/scpi/scpi_handler.h"

// -----
// @argTokens, @argTypes
// Declare argument parser entities
// Supported arguments: 1=CHARACTER
DECLARE_SCPI_ARGS( eScpiArg_Character );

// Argument 1 Character Values allowed list / Memory Bank
DECLARE_ARGUMENT_CHARACTER_ALLOWED_LIST_IMPORT( MemTable_AllowedValues_Bank );

#include "app/scpi/commandHandlers/memory_table_freq_segm_data.h"
#include "app/nfm/nfm_base.h"

// Refer to:
// [1] SCPI Specification, revision 1999.0
//     "Standard Commands for Programmable Instruments (SCPI), VERSION 1999.0, May 1999"
// [2] Gpib Programming Tutorial, (http://g2pc1.bu.edu/~qzpeng/gpib/manual/GpibProgTut.pdf)
//     Electronics Group (http://www.few.vu.nl/~elec), 11 January 2000 Electronics Group
// [3] IEEE 488.2 Standard, revision IEEE Std 488.2-1987 (1992)
//     "IEEE Standard Codes, Formats, Protocols, and Common Commands for Use With IEEE Std 488.1-1987, IEEE

// =================================================================================
// @fsqvbl_CommandHandlerMemoryTableFrequencySegmentData
// State's virtual table

static void fsqe_CommandHandlerMemoryTableFrequencySegmentData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
static void fsql_CommandHandlerMemoryTableFrequencySegmentData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx );
static const struct fFSeqEntry_t * fsqf_CommandHandlerMemoryTableFrequencySegmentData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx, const struct fFSeqEntry_t * * pDeferredNext );

const fFSeqVTable_t fsqvbl_CommandHandlerMEMoryTABLeFREQuencySEGMentDATA = 
{
     .f = fsqf_CommandHandlerMemoryTableFrequencySegmentData,
     .enter = fsqe_CommandHandlerMemoryTableFrequencySegmentData,  
     .leave = fsql_CommandHandlerMemoryTableFrequencySegmentData
};

static void fsqe_CommandHandlerMemoryTableFrequencySegmentData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx )
{
    sProcessProgramDataCommonContext_t * common_ctx = ctx;
    
    SCPI_PARSE_ARGUMENTS( common_ctx ); (void)common_ctx->argsParserStatus; // status is modified

    common_ctx->MemTableFreqSegmData.idx = 0;
    common_ctx->MemTableFreqSegmData.nchs = 0;
    common_ctx->MemTableFreqSegmData.bank = 0;
    common_ctx->MemTableFreqSegmData.iSeg = 0;
    common_ctx->MemTableFreqSegmData.nSeg = 0;
    common_ctx->MemTableFreqSegmData.init = false;   
    common_ctx->MemTableFreqSegmData.isdone = false;
    (void)common_ctx->MemTableFreqSegmData.Segment;
}

static void fsql_CommandHandlerMemoryTableFrequencySegmentData( const struct fFSeqEntry_t * this, tFSeqCtx_t ctx )
{
}

static const struct fFSeqEntry_t * fsqf_CommandHandlerMemoryTableFrequencySegmentData( const struct fFSeqEntry_t * this, 
                                                                                        tFSeqCtx_t ctx, 
                                                                                         const struct fFSeqEntry_t * * pDeferredNext )
{
    const fFSeqEntry_t * nextstate = NULL;
    
    sProcessProgramDataCommonContext_t * common_ctx = ctx;
    sScpiParserContext_t * global_ctx = common_ctx->global_ctx;

    switch( common_ctx->event )
    {
        case eProgramData_Event_Write:
        {
            if( ! common_ctx->isQuery )
            {
               common_ctx->status = eProgramDataSyntaxError; // invalid command header type: COMMAND not supported
            }
            else if( eScpiStatus_success != common_ctx->argsParserStatus ) // check argument parser status
            {
               common_ctx->status = eProgramDataArgumentSyntax;  // parameter syntax error, caller should generate error message
            }
            else
            {
               common_ctx->status = eProgramDataIllegalArgument;  // forward set, illegal parameter value, caller should generate error message

               if( common_ctx->args > 0 )
               // process first argument (bank)
               common_ctx->MemTableFreqSegmData.bank = SCPI_PROCESS_ARGUMENT_CHARACTER( common_ctx, MemTable_AllowedValues_Bank, 0 );
               else
               // default parameter
               common_ctx->MemTableFreqSegmData.bank = 0; // Factory

               // check result
               if( SCPI_ARGUMENT_CHARACTER_INVALID_ID != common_ctx->MemTableFreqSegmData.bank )
               {
                   common_ctx->status = eProgramDataNeedRead;  // request processed, wait for reading...
               }
            }
        }
        break;

        case eProgramData_Event_Read:
        {
             do
             {

             // @idx - current position of the source data to be outputed
             if( common_ctx->MemTableFreqSegmData.idx >= common_ctx->MemTableFreqSegmData.nchs )
             {
                 if( common_ctx->MemTableFreqSegmData.isdone ) break;

                 common_ctx->MemTableFreqSegmData.idx = common_ctx->MemTableFreqSegmData.nchs = 0;

                 eChrz_t chrz = (eChrz_t)((eChFactory) + common_ctx->MemTableFreqSegmData.bank); 

                 // @PRINT_VALUE_MIN_SPACE
                 // Response format: <N>, <Start 1>, <Stop 1>, <NOP 1>, <Start 2>, <Stop 2>, <NOP 2>, … <Start N>, <Stop N>, <NOP N>
                 // where "<N>, " is an initial record, number of segments;
                 // where "<Start 1>, <Stop 1>, <NOP 1>, " is a frequency record;
                 // A sum of a couple lengths of the following strings:
                 // - number of segments, comma and space
                 // - start frequency, comma, space, stop frequency, comma, space, number of points, comma, space
                 // One frequency number has 16 symbols length plus 2 (comma and space)
                 // A number of segments takes 5 characters max plus 2 (comma and space)
                 // A number of points takes 5 characters max plus 2 (comma and space)
                 // So, whole record takes 46 characters, and 1 for null-char: let it be 48
                 // So the temporary buffer must have enough room for store at least one segment record plus the initial record.
                 #define PRINT_VALUE_MIN_SPACE 48
                 #if SCPI_MAX_CMD_TEMP_BUFFER < PRINT_VALUE_MIN_SPACE
                 #error Invalid value 'SCPI_MAX_CMD_TEMP_BUFFER'
                 #endif

                 // Have already initialized?
                 if( ! common_ctx->MemTableFreqSegmData.init )
                 {
                     // No, get number of segments:
                     common_ctx->MemTableFreqSegmData.nSeg = NFMClass->methods.xCharacterization.getScaleSegment( chrz, NULL, 0 );

                     // Check number of segments:
                     if( 0 == common_ctx->MemTableFreqSegmData.nSeg ) goto L_FrequencySegment_DATAERR;

                     // ok. initialized
                     common_ctx->MemTableFreqSegmData.init = true;

                     // see @PRINT_VALUE_MIN_SPACE, this call always prints correctly
                     common_ctx->MemTableFreqSegmData.nchs = _snprintf( common_ctx->tempBuffer, 
                                                                      sizeof(common_ctx->tempBuffer),
                                                                      "%d%c%c",
                                                                      common_ctx->MemTableFreqSegmData.nSeg,
                                                                      ',',' ' );
                 }

                 if( common_ctx->MemTableFreqSegmData.iSeg < common_ctx->MemTableFreqSegmData.nSeg )
                 {
                     if( 1 != NFMClass->methods.xCharacterization.getScaleSegment( chrz, 
                                                                                   &common_ctx->MemTableFreqSegmData.Segment,
                                                                                   common_ctx->MemTableFreqSegmData.iSeg ) )
                     {
                         goto L_FrequencySegment_DATAERR; // can not load a segment
                     }

                     common_ctx->MemTableFreqSegmData.iSeg++;
                     common_ctx->MemTableFreqSegmData.isdone = (common_ctx->MemTableFreqSegmData.iSeg >= common_ctx->MemTableFreqSegmData.nSeg);
                 }

                 size_t bufsz = sizeof(common_ctx->tempBuffer) - common_ctx->MemTableFreqSegmData.nchs;
                 common_ctx->MemTableFreqSegmData.nchs += _snprintf( &common_ctx->tempBuffer[ common_ctx->MemTableFreqSegmData.nchs ],
                                                                bufsz,
                                                                "%1.10e%c%c%1.10e%c%c%d%c%c",
                                                                common_ctx->MemTableFreqSegmData.Segment.Fstart, ',',' ',
                                                                common_ctx->MemTableFreqSegmData.Segment.Fstop, ',',' ',
                                                                common_ctx->MemTableFreqSegmData.Segment.Points, ',',' ' );
                 if( 0 == common_ctx->MemTableFreqSegmData.nchs 
                  || common_ctx->MemTableFreqSegmData.nchs >= bufsz )
                 {
                     goto L_FrequencySegment_INTERR; // invalid length, forbidden state, must be covered by correct value of PRINT_VALUE_MIN_SPACE
                 }

                 if( common_ctx->MemTableFreqSegmData.isdone )
                 {
                     common_ctx->MemTableFreqSegmData.nchs -= 2;
                     common_ctx->tempBuffer[ common_ctx->MemTableFreqSegmData.nchs ] = '\0'; // cut off ", " in the end
                 }
             }

             // Since @done flag is set, this dispatcher shall not be called anymore.
             // Since this handler is implemented as a single-state automat, there no
             // ... other states to go to:
             (void)nextstate;

             // modify current postion index:
             SCPI_RESPONSE_HELPER_EXTDONE_IDXINC( common_ctx, common_ctx->MemTableFreqSegmData.idx, common_ctx->MemTableFreqSegmData.isdone );
             
             }
             // While output buffer swallows all the prepared data from @tempBuffer
             while( (common_ctx->MemTableFreqSegmData.nchs > 0) && (common_ctx->MemTableFreqSegmData.idx >= common_ctx->MemTableFreqSegmData.nchs) );

             bool bufferEmpty = (common_ctx->MemTableFreqSegmData.idx >= common_ctx->MemTableFreqSegmData.nchs);

             if( bufferEmpty )
             {
                 common_ctx->MemTableFreqSegmData.idx = common_ctx->MemTableFreqSegmData.nchs = 0;
             }

             SCPI_RESPONSE_HELPER_EXTDONE_SET( common_ctx, bufferEmpty && common_ctx->MemTableFreqSegmData.isdone );
        }
        break;
    }

    return nextstate;

L_FrequencySegment_DATAERR:

    // Formal call: in case the parameter is optional, the token is unfilled.
    // So it is required to fill the token with correct values from allowed list.
    SCPI_ARGUMENT_CHARACTER_VALUE_TOKEN( MemTable_AllowedValues_Bank,
                                         common_ctx->MemTableFreqSegmData.bank,
                                        &common_ctx->argTokens[0] );

    fsq_RaiseErrorEx( SCPI_ERROR_DATA_CORRUPTED,
                                       SCPI_ERROR_DATA_CORRUPTED_MSG,
                                       common_ctx->argTokens[0].shead,
                                       common_ctx->argTokens[0].stail,
                                       global_ctx->sParser.xHandlerToken.shead,
                                       global_ctx->sParser.xHandlerToken.stail );

    common_ctx->status = eProgramData_SpecificError; // specific error already generated
    return NULL;

//L_FrequencySegment_PARAMERR:
L_FrequencySegment_INTERR:
   fsq_RaiseError( SCPI_ERROR_INTERNAL_DEVICE,
                                       SCPI_ERROR_INTERNAL_DEVICE_MSG,
                                       global_ctx->sParser.xHandlerToken.shead,
                                       global_ctx->sParser.xHandlerToken.stail );

   common_ctx->status = eProgramData_SpecificError; // specific error already generated   
   return NULL;
}

