/*
 * \file       trc_frame_deformatter.cpp
 * \brief      OpenCSD : 
 * 
 * \copyright  Copyright (c) 2015, ARM Limited. All Rights Reserved.
 */

/* 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution. 
 * 
 * 3. Neither the name of the copyright holder nor the names of its contributors 
 * may be used to endorse or promote products derived from this software without 
 * specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */ 
#include <cstring>

#include "common/trc_frame_deformatter.h"
#include "trc_frame_deformatter_impl.h"

/***************************************************************/
/* Implementation */
/***************************************************************/

#ifdef __GNUC__
// G++ doesn't like the ## pasting
#define DEFORMATTER_NAME "DFMT_CSFRAMES"
#else
// VC is fine
#define DEFORMATTER_NAME OCSD_CMPNAME_PREFIX_FRAMEDEFORMATTER##"_CSFRAMES"
#endif

TraceFmtDcdImpl::TraceFmtDcdImpl() : TraceComponent(DEFORMATTER_NAME),
    m_cfgFlags(0),
    m_force_sync_idx(0),
    m_use_force_sync(false),
    m_alignment(16), // assume frame aligned data as default.
    m_b_output_packed_raw(false),
    m_b_output_unpacked_raw(false)

{
    resetStateParams();
    setRawChanFilterAll(true);
}

TraceFmtDcdImpl::TraceFmtDcdImpl(int instNum) : TraceComponent(DEFORMATTER_NAME, instNum),
    m_cfgFlags(0),
    m_force_sync_idx(0),
    m_use_force_sync(false),
    m_alignment(16)
{
    resetStateParams();
    setRawChanFilterAll(true);
}

TraceFmtDcdImpl::~TraceFmtDcdImpl()
{
}

ocsd_datapath_resp_t TraceFmtDcdImpl::TraceDataIn(
    const ocsd_datapath_op_t op, 
    const ocsd_trc_index_t index, 
    const uint32_t dataBlockSize, 
    const uint8_t *pDataBlock, 
    uint32_t *numBytesProcessed)
{
    ocsd_datapath_resp_t resp = OCSD_RESP_FATAL_INVALID_OP;
    InitCollateDataPathResp();

    m_b_output_packed_raw = m_RawTraceFrame.num_attached() && ((m_cfgFlags & OCSD_DFRMTR_PACKED_RAW_OUT) != 0);
    m_b_output_unpacked_raw = m_RawTraceFrame.num_attached() && ((m_cfgFlags & OCSD_DFRMTR_UNPACKED_RAW_OUT) != 0);

    switch(op)
    {
    case OCSD_OP_RESET: 
        resp = Reset();
        break;

    case OCSD_OP_FLUSH:
        resp = Flush();
        break;

    case OCSD_OP_EOT:
        // local 'flush' here?
        // pass on EOT to connected ID streams
        resp = executeNoneDataOpAllIDs(OCSD_OP_EOT);
        break;

    case OCSD_OP_DATA:
        if((dataBlockSize <= 0) || ( pDataBlock == 0) || (numBytesProcessed == 0))
            resp = OCSD_RESP_FATAL_INVALID_PARAM;
        else
            resp = processTraceData(index,dataBlockSize, pDataBlock, numBytesProcessed);
        break;

    default:
        break;
    }

    return resp;
}

/* enable / disable ID streams - default as all enabled */
ocsd_err_t TraceFmtDcdImpl::OutputFilterIDs(std::vector<uint8_t> &id_list, bool bEnable)
{
    ocsd_err_t err =  OCSD_OK;
    std::vector<uint8_t>::iterator iter = id_list.begin();
    uint8_t id = 0;

    while((iter < id_list.end()) && (err == OCSD_OK))
    {
        id = *iter;
        if(id > 128)
            err = OCSD_ERR_INVALID_ID;
        else
        {
            m_IDStreams[id].set_enabled(bEnable);
            m_raw_chan_enable[id] = bEnable;
        }
        iter++;
    }
    return err;
}

ocsd_err_t TraceFmtDcdImpl::OutputFilterAllIDs(bool bEnable)
{
    for(uint8_t id = 0; id < 128; id++)
    {
        m_IDStreams[id].set_enabled(bEnable);
    }
    setRawChanFilterAll(bEnable);
    return OCSD_OK;
}

void TraceFmtDcdImpl::setRawChanFilterAll(bool bEnable)
{
    for(int i=0; i<128; i++) 
    {
        m_raw_chan_enable[i] = bEnable;
    }
}

const bool TraceFmtDcdImpl::rawChanEnabled(const uint8_t id) const
{
    if(id < 128)
        return m_raw_chan_enable[id];
    return false;
}

/* decode control */
ocsd_datapath_resp_t TraceFmtDcdImpl::Reset()
{
    resetStateParams();
    InitCollateDataPathResp();
    return executeNoneDataOpAllIDs(OCSD_OP_RESET);
}

ocsd_datapath_resp_t TraceFmtDcdImpl::Flush()
{
    executeNoneDataOpAllIDs(OCSD_OP_FLUSH);    // flush any upstream data.
    if(dataPathCont())
        outputFrame();  // try to flush any partial frame data remaining
    return highestDataPathResp();
}

ocsd_datapath_resp_t TraceFmtDcdImpl::executeNoneDataOpAllIDs(ocsd_datapath_op_t op,
                                                              const ocsd_trc_index_t index /* = 0*/)
{
    ITrcDataIn *pTrcComp = 0;
    for(uint8_t id = 0; id < 128; id++)
    {
        if(m_IDStreams[id].num_attached())
        {
            pTrcComp = m_IDStreams[id].first();
            while(pTrcComp)
            {
                CollateDataPathResp(pTrcComp->TraceDataIn(op,index,0,0,0));
                pTrcComp = m_IDStreams[id].next();
            }
        }
    }

    if( m_RawTraceFrame.num_attached())
    {
        if(m_RawTraceFrame.first())
            m_RawTraceFrame.first()->TraceRawFrameIn(op,0,OCSD_FRM_NONE,0,0,0);
    }
    return highestDataPathResp();
}

void TraceFmtDcdImpl::outputRawMonBytes(const ocsd_datapath_op_t op, 
                           const ocsd_trc_index_t index, 
                           const ocsd_rawframe_elem_t frame_element, 
                           const int dataBlockSize, 
                           const uint8_t *pDataBlock,
                           const uint8_t traceID)                           
{
    if( m_RawTraceFrame.num_attached())
    {
        if(m_RawTraceFrame.first())
            m_RawTraceFrame.first()->TraceRawFrameIn(op,index,frame_element,dataBlockSize, pDataBlock,traceID);
    }
}

void TraceFmtDcdImpl::CollateDataPathResp(const ocsd_datapath_resp_t resp)
{
    // simple most severe error across multiple IDs.
    if(resp > m_highestResp) m_highestResp = resp;
}

ocsd_datapath_resp_t TraceFmtDcdImpl::processTraceData( 
                                        const ocsd_trc_index_t index, 
                                        const uint32_t dataBlockSize, 
                                        const uint8_t *pDataBlock, 
                                        uint32_t *numBytesProcessed
                                        )
{
    try {

        if(!m_first_data)  // is this the initial data block?
        {
            m_trc_curr_idx = index;
        }
        else
        {
            if(m_trc_curr_idx != index) // none continuous trace data - throw an error.
                throw ocsdError(OCSD_ERR_SEV_ERROR,OCSD_ERR_DFMTR_NOTCONTTRACE,index);
        }
        
        if(dataBlockSize % m_alignment) // must be correctly aligned data 
        {
            ocsdError err(OCSD_ERR_SEV_ERROR, OCSD_ERR_INVALID_PARAM_VAL);
            char msg_buffer[64];
            sprintf(msg_buffer,"Input block incorrect size, must be %d byte multiple", m_alignment);
            err.setMessage(msg_buffer);
            throw ocsdError(&err);
        }

        // record the incoming block for extraction routines to use.
        m_in_block_base = pDataBlock;
        m_in_block_size = dataBlockSize;
        m_in_block_processed = 0;

        // processing loop...
        if(checkForSync())
        {
            bool bProcessing = true;
            while(bProcessing) 
            {
                bProcessing = extractFrame();   // will stop on end of input data.
                if(bProcessing)
                    bProcessing = unpackFrame();
                if(bProcessing)
                    bProcessing = outputFrame(); // will stop on data path halt.
            }
        }
    }
    catch(const ocsdError &err) {
        LogError(err);
        CollateDataPathResp(OCSD_RESP_FATAL_INVALID_DATA);
    }
    catch(...) {
        LogError(ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_FAIL));
        CollateDataPathResp(OCSD_RESP_FATAL_SYS_ERR);
    }

    if(!m_first_data)
        m_first_data = true;
    
    // update the outputs.
    *numBytesProcessed = m_in_block_processed;

    return highestDataPathResp();
}

ocsd_err_t  TraceFmtDcdImpl::DecodeConfigure(uint32_t flags)
{
    const char *pszErrMsg = "";
    ocsd_err_t err = OCSD_OK;

    if((flags & ~OCSD_DFRMTR_VALID_MASK) != 0)
    {
        err = OCSD_ERR_INVALID_PARAM_VAL;
        pszErrMsg = "Unknown Config Flags";
    }

    if((flags & OCSD_DFRMTR_VALID_MASK) == 0)
    {
        err = OCSD_ERR_INVALID_PARAM_VAL;
        pszErrMsg = "No Config Flags Set";
    }

    if((flags & (OCSD_DFRMTR_HAS_FSYNCS | OCSD_DFRMTR_HAS_HSYNCS)) &&
       (flags & OCSD_DFRMTR_FRAME_MEM_ALIGN)
       )
    {
        err = OCSD_ERR_INVALID_PARAM_VAL;
        pszErrMsg = "Invalid Config Flag Combination Set";
    }

    if(err != OCSD_OK)
    {
        ocsdError errObj(OCSD_ERR_SEV_ERROR,OCSD_ERR_INVALID_PARAM_VAL);
        errObj.setMessage(pszErrMsg);
        LogError(errObj);
    }
    else
    {
        m_cfgFlags = flags;
        m_alignment = 16;
        if(flags & OCSD_DFRMTR_HAS_FSYNCS)
            m_alignment = 4;
        else if(flags & OCSD_DFRMTR_HAS_HSYNCS)
            m_alignment = 2;
    }
    return err;
}

void TraceFmtDcdImpl::resetStateParams()
{
    // overall dynamic state - intra frame
    m_trc_curr_idx = OCSD_BAD_TRC_INDEX;    /* source index of current trace data */
    m_frame_synced = false;
    m_first_data = false;    
    m_curr_src_ID = OCSD_BAD_CS_SRC_ID;

    // current frame processing
    m_ex_frm_n_bytes = 0;
    m_trc_curr_idx_sof = OCSD_BAD_TRC_INDEX;
}

bool TraceFmtDcdImpl::checkForSync()
{
    // we can sync on:-
    // 16 byte alignment - standard input buffers such as ETB
    // FSYNC packets in the stream
    // forced index programmed into the object.
    uint32_t unsynced_bytes = 0;

    if(!m_frame_synced)
    {
        if(m_use_force_sync)
        {
            // is the force sync point in this block?
            if((m_force_sync_idx >= m_trc_curr_idx) && (m_force_sync_idx < (m_trc_curr_idx + m_in_block_size)))
            {
                unsynced_bytes = m_force_sync_idx - m_trc_curr_idx;
                m_frame_synced = true;
            }
            else
            {
                unsynced_bytes = m_in_block_size;
            }
        }
        else if( m_cfgFlags & OCSD_DFRMTR_HAS_FSYNCS)   // memory aligned data
        {
             unsynced_bytes = findfirstFSync();

        }
        else
        {
            // OCSD_DFRMTR_FRAME_MEM_ALIGN - this has guaranteed 16 byte frame size and alignment.
            m_frame_synced = true;
        }

        if(unsynced_bytes)
        {
            outputUnsyncedBytes(unsynced_bytes);
            m_in_block_processed = unsynced_bytes;
            m_trc_curr_idx += unsynced_bytes;
        }
    }
    return m_frame_synced;
}

uint32_t TraceFmtDcdImpl::findfirstFSync()
{
    uint32_t processed = 0;
    const uint32_t FSYNC_PATTERN = 0x7FFFFFFF;    // LE host pattern for FSYNC	 
    const uint8_t *dataPtr = m_in_block_base;

    while (processed < (m_in_block_size - 3))
    {
        if (*((uint32_t *)(dataPtr)) == FSYNC_PATTERN)
        {
            m_frame_synced = true;
            break;
        }
        processed++;
        dataPtr++;
    }
    return processed;
}

void TraceFmtDcdImpl::outputUnsyncedBytes(uint32_t /*num_bytes*/)
{
    //**TBD:
}

int TraceFmtDcdImpl::checkForResetFSyncPatterns()
{
	const uint32_t FSYNC_PATTERN = 0x7FFFFFFF;    // LE host pattern for FSYNC	 
	bool check_for_fsync = true;
	int num_fsyncs = 0;
	const uint8_t *dataPtr = m_in_block_base + m_in_block_processed;

	while (check_for_fsync && (m_in_block_processed < m_in_block_size))
	{
		// look for consecutive fsyncs as padding or for reset downstream - both cases will reset downstream....
		if (*((uint32_t *)(dataPtr)) == FSYNC_PATTERN)
		{
			dataPtr += sizeof(uint32_t);
			num_fsyncs++;		
		}
		else
			check_for_fsync = false;
	}

	if (num_fsyncs)
	{
        printf("Frame deformatter: Found %d FSYNCS\n",num_fsyncs);
		if ((num_fsyncs % 4) == 0)
        {
            // reset the upstream decoders            
			executeNoneDataOpAllIDs(OCSD_OP_RESET,m_trc_curr_idx);
            
            // reset the intra frame parameters
            m_curr_src_ID = OCSD_BAD_CS_SRC_ID;
            m_ex_frm_n_bytes = 0;
            m_trc_curr_idx_sof = OCSD_BAD_TRC_INDEX;
        }
		else
		{
			// TBD: throw processing error, none frame size block of fsyncs
		}
	}
    return num_fsyncs * 4;
}


bool TraceFmtDcdImpl::extractFrame()
{
	const uint32_t FSYNC_PATTERN = 0x7FFFFFFF;    // LE host pattern for FSYNC	 
	const uint16_t HSYNC_PATTERN = 0x7FFF;        // LE host pattern for HSYNC
	
	
	bool cont_process = true;   // continue processing after extraction.
    uint32_t f_sync_bytes = 0; // skipped f sync bytes
    uint32_t h_sync_bytes = 0; // skipped h sync bytes
    uint32_t ex_bytes = 0;  // extracted this pass (may be filling out part frame)

    // memory aligned sources are always multiples of frames, aligned to start.
    if( m_cfgFlags & OCSD_DFRMTR_FRAME_MEM_ALIGN)
    {
		// some linux drivers (e.g. for perf) will insert FSYNCS to pad or differentiate
        // between blocks of aligned data, always in frame aligned complete 16 byte frames.       
        if (m_cfgFlags & OCSD_DFRMTR_RESET_ON_4X_FSYNC)
        {
            f_sync_bytes = checkForResetFSyncPatterns();

            /* in this case the FSYNC pattern is output on both packed and unpacked cases */
            if (f_sync_bytes && (m_b_output_packed_raw || m_b_output_unpacked_raw))
            {
                outputRawMonBytes(OCSD_OP_DATA,
                    m_trc_curr_idx,
                    OCSD_FRM_FSYNC,
                    f_sync_bytes,
                    m_in_block_base + m_in_block_processed,
                    0);
            }
        }

        if((m_in_block_processed+f_sync_bytes) == m_in_block_size)
        {
            m_ex_frm_n_bytes = 0;
            cont_process = false;   // end of input data.
        }
		else
		{
			// always a complete frame.
			m_ex_frm_n_bytes = OCSD_DFRMTR_FRAME_SIZE;
			memcpy(m_ex_frm_data, m_in_block_base + m_in_block_processed + f_sync_bytes, m_ex_frm_n_bytes);
			m_trc_curr_idx_sof = m_trc_curr_idx + f_sync_bytes;
			ex_bytes = OCSD_DFRMTR_FRAME_SIZE;
        }
    }
    else
    {
        // extract data accounting for frame syncs and hsyncs if present.
        // we know we are aligned at this point - could be FSYNC or HSYNCs here.

        // check what we a looking for
        bool hasFSyncs =  ((m_cfgFlags & OCSD_DFRMTR_HAS_FSYNCS) == OCSD_DFRMTR_HAS_FSYNCS);
        bool hasHSyncs =  ((m_cfgFlags & OCSD_DFRMTR_HAS_HSYNCS) == OCSD_DFRMTR_HAS_HSYNCS);

        const uint8_t *dataPtr = m_in_block_base+m_in_block_processed;
        const uint8_t *eodPtr = m_in_block_base+m_in_block_size;
        
        cont_process = (bool)(dataPtr < eodPtr);
        
        // can have FSYNCS at start of frame (in middle is an error).
        if(hasFSyncs && cont_process && (m_ex_frm_n_bytes == 0))
        {
            while((*((uint32_t *)(dataPtr)) == FSYNC_PATTERN) && cont_process)
            {
                f_sync_bytes += 4;
                dataPtr += 4;
                cont_process = (bool)(dataPtr < eodPtr);
            }
        }

        // not an FSYNC
        while((m_ex_frm_n_bytes < OCSD_DFRMTR_FRAME_SIZE) && cont_process)
        {
            // check for illegal out of sequence FSYNC
            if((m_ex_frm_n_bytes % 4) == 0)
            {
                if(*((uint32_t *)(dataPtr)) == FSYNC_PATTERN) 
                {
                    // throw an illegal FSYNC error
                    throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_DFMTR_BAD_FHSYNC, m_trc_curr_idx, "Bad FSYNC in frame.");
                }
            }

            // mark start of frame after FSyncs 
            if(m_ex_frm_n_bytes == 0)
                m_trc_curr_idx_sof = m_trc_curr_idx + f_sync_bytes;

            m_ex_frm_data[m_ex_frm_n_bytes] = dataPtr[0];
            m_ex_frm_data[m_ex_frm_n_bytes+1] = dataPtr[1];
            m_ex_frm_n_bytes+=2;
            ex_bytes +=2;

            // check pair is not HSYNC
            if(*((uint16_t *)(dataPtr)) == HSYNC_PATTERN)
            {
                if(hasHSyncs)
                {
                    m_ex_frm_n_bytes-=2;
                    ex_bytes -= 2;
                    h_sync_bytes+=2;
                }
                else
                {
                    // throw illegal HSYNC error.
                    throw ocsdError(OCSD_ERR_SEV_ERROR, OCSD_ERR_DFMTR_BAD_FHSYNC, m_trc_curr_idx, "Bad HSYNC in frame.");
                }
            }

            dataPtr += 2;
            cont_process = (bool)(dataPtr < eodPtr);
        }

        // if we hit the end of data but still have a complete frame waiting, 
        // need to continue processing to allow it to be used.
        if(!cont_process && (m_ex_frm_n_bytes == OCSD_DFRMTR_FRAME_SIZE))
            cont_process = true;
    }

    // total bytes processed this pass 
    uint32_t total_processed = ex_bytes + f_sync_bytes + h_sync_bytes;

    // output raw data on raw frame channel - packed raw. 
    if (((m_ex_frm_n_bytes == OCSD_DFRMTR_FRAME_SIZE) || !cont_process) && m_b_output_packed_raw)
    {
        outputRawMonBytes(  OCSD_OP_DATA, 
                            m_trc_curr_idx, 
                            OCSD_FRM_PACKED,
                            total_processed,
                            m_in_block_base+m_in_block_processed,
                            0);
    }

    // update the processed count for the buffer
    m_in_block_processed += total_processed;

    // update index past the processed data   
    m_trc_curr_idx += total_processed;

    return cont_process;
}

bool TraceFmtDcdImpl::unpackFrame()
{
    // unpack cannot fail as never called on incomplete frame.
    uint8_t frameFlagBit = 0x1;
    uint8_t newSrcID = OCSD_BAD_CS_SRC_ID;
    bool PrevIDandIDChange = false;

    // init output processing
    m_out_data_idx = 0;   
    m_out_processed = 0;

    // set up first out data packet...
    m_out_data[m_out_data_idx].id = m_curr_src_ID;
    m_out_data[m_out_data_idx].valid = 0;
    m_out_data[m_out_data_idx].index =  m_trc_curr_idx_sof;
    m_out_data[m_out_data_idx].used = 0;

    // work on byte pairs - bytes 0 - 13.
    for(int i = 0; i < 14; i+=2)
    {
        PrevIDandIDChange = false;

        // it's an ID + data
        if(m_ex_frm_data[i] & 0x1)
        {
            newSrcID = (m_ex_frm_data[i] >> 1) & 0x7f;
            if(newSrcID != m_curr_src_ID)   // ID change
            {
                PrevIDandIDChange = ((frameFlagBit & m_ex_frm_data[15]) != 0);

                // following byte for old id? 
                if(PrevIDandIDChange)
                    // 2nd byte always data
                    m_out_data[m_out_data_idx].data[m_out_data[m_out_data_idx].valid++] = m_ex_frm_data[i+1];

                // change ID
                m_curr_src_ID = newSrcID;

                // if we already have data in this buffer
                if(m_out_data[m_out_data_idx].valid > 0)
                {
                    m_out_data_idx++; // move to next buffer
                    m_out_data[m_out_data_idx].valid = 0;
                    m_out_data[m_out_data_idx].used = 0;
                    m_out_data[m_out_data_idx].index = m_trc_curr_idx_sof + i;
                }

                // set new ID on buffer
                m_out_data[m_out_data_idx].id = m_curr_src_ID;

                /// TBD - ID indexing in here.
            }
        }
        else
        // it's just data
        {
            m_out_data[m_out_data_idx].data[m_out_data[m_out_data_idx].valid++] = m_ex_frm_data[i] | ((frameFlagBit & m_ex_frm_data[15]) ? 0x1 : 0x0);             
        }

        // 2nd byte always data
        if(!PrevIDandIDChange) // output only if we didn't for an ID change + prev ID.
            m_out_data[m_out_data_idx].data[m_out_data[m_out_data_idx].valid++] = m_ex_frm_data[i+1];

        frameFlagBit <<= 1;
    }

    // unpack byte 14;

    // it's an ID
    if(m_ex_frm_data[14] & 0x1)
    {
        // no matter if change or not, no associated data in byte 15 anyway so just set.
        m_curr_src_ID = (m_ex_frm_data[14] >> 1) & 0x7f;
    }
    // it's data
    else
    {
        m_out_data[m_out_data_idx].data[m_out_data[m_out_data_idx].valid++] = m_ex_frm_data[14] | ((frameFlagBit & m_ex_frm_data[15]) ? 0x1 : 0x0); 
    }
    m_ex_frm_n_bytes = 0;   // mark frame as empty;
    return true;
}

// output data to channels.
bool TraceFmtDcdImpl::outputFrame()
{
    bool cont_processing = true;
    ITrcDataIn *pDataIn = 0;
    uint32_t bytes_used;

    // output each valid ID within the frame - stopping if we get a wait or error
    while((m_out_processed < (m_out_data_idx + 1)) && cont_processing)
    {
        
        // may have data prior to a valid ID appearing
        if(m_out_data[m_out_processed].id != OCSD_BAD_CS_SRC_ID)
        {
            if((pDataIn = m_IDStreams[m_out_data[m_out_processed].id].first()) != 0)
            {
                // log the stuff we are about to put out early so as to make it visible before interpretation
                // however, don't re-output if only part used first time round.
                if(m_b_output_unpacked_raw && (m_out_data[m_out_processed].used == 0) && rawChanEnabled( m_out_data[m_out_processed].id))
                {
                    outputRawMonBytes( OCSD_OP_DATA, 
                            m_out_data[m_out_processed].index, 
                            OCSD_FRM_ID_DATA,
                            m_out_data[m_out_processed].valid,
                            m_out_data[m_out_processed].data,
                            m_out_data[m_out_processed].id);
                }

                // output to the connected packet process
                CollateDataPathResp(pDataIn->TraceDataIn(OCSD_OP_DATA,
                    m_out_data[m_out_processed].index + m_out_data[m_out_processed].used,
                    m_out_data[m_out_processed].valid - m_out_data[m_out_processed].used,
                    m_out_data[m_out_processed].data + m_out_data[m_out_processed].used,
                    &bytes_used));               
                
                if(!dataPathCont())
                {
                    cont_processing = false;
                    m_out_data[m_out_processed].used += bytes_used;
                    if(m_out_data[m_out_processed].used == m_out_data[m_out_processed].valid)
                        m_out_processed++; // we have used up all this data.
                }
                else
                {
                    m_out_processed++; // we have sent this data;
                }
            }
            else
            {
                // optional raw output for debugging / monitor tools
                if(m_b_output_unpacked_raw && rawChanEnabled( m_out_data[m_out_processed].id))
                {
                    outputRawMonBytes(  OCSD_OP_DATA, 
                        m_out_data[m_out_processed].index, 
                        OCSD_FRM_ID_DATA,
                        m_out_data[m_out_processed].valid,
                        m_out_data[m_out_processed].data,
                        m_out_data[m_out_processed].id);
                }                
                m_out_processed++; // skip past this data.
            }
        }
        else
        {
            // optional raw output for debugging / monitor tools of unknown src ID data
            if(m_b_output_unpacked_raw)
            {
                outputRawMonBytes(  OCSD_OP_DATA, 
                    m_out_data[m_out_processed].index, 
                    OCSD_FRM_ID_DATA,
                    m_out_data[m_out_processed].valid,
                    m_out_data[m_out_processed].data,
                    m_out_data[m_out_processed].id);
            }             
            m_out_processed++; // skip past this data.
        }
    }
    return cont_processing;
}

/***************************************************************/
/* interface */
/***************************************************************/
TraceFormatterFrameDecoder::TraceFormatterFrameDecoder() : m_pDecoder(0)
{
    m_instNum = -1;
}

TraceFormatterFrameDecoder::TraceFormatterFrameDecoder(int instNum) : m_pDecoder(0)
{
    m_instNum = instNum;
}

TraceFormatterFrameDecoder::~TraceFormatterFrameDecoder()
{
    if(m_pDecoder) 
    {
        delete m_pDecoder;
        m_pDecoder = 0;
    }
}

    /* the data input interface from the reader / source */
ocsd_datapath_resp_t TraceFormatterFrameDecoder::TraceDataIn(  const ocsd_datapath_op_t op, 
                                                                const ocsd_trc_index_t index, 
                                                                const uint32_t dataBlockSize, 
                                                                const uint8_t *pDataBlock, 
                                                                uint32_t *numBytesProcessed)
{
    return (m_pDecoder == 0) ? OCSD_RESP_FATAL_NOT_INIT : m_pDecoder->TraceDataIn(op,index,dataBlockSize,pDataBlock,numBytesProcessed);
}

/* attach a data processor to a stream ID output */
componentAttachPt<ITrcDataIn> *TraceFormatterFrameDecoder::getIDStreamAttachPt(uint8_t ID)
{
    componentAttachPt<ITrcDataIn> *pAttachPt = 0;
    if((ID < 128) && (m_pDecoder != 0))
        pAttachPt = &(m_pDecoder->m_IDStreams[ID]);
    return pAttachPt;
}

/* attach a data processor to the raw frame output */
componentAttachPt<ITrcRawFrameIn> *TraceFormatterFrameDecoder::getTrcRawFrameAttachPt()
{
    return (m_pDecoder != 0) ? &m_pDecoder->m_RawTraceFrame : 0;
}


componentAttachPt<ITrcSrcIndexCreator> *TraceFormatterFrameDecoder::getTrcSrcIndexAttachPt()
{
    return (m_pDecoder != 0) ? &m_pDecoder->m_SrcIndexer : 0;
}

componentAttachPt<ITraceErrorLog> *TraceFormatterFrameDecoder::getErrLogAttachPt()
{
    return (m_pDecoder != 0) ? m_pDecoder->getErrorLogAttachPt() : 0;
}

/* configuration - set operational mode for incoming stream (has FSYNCS etc) */
ocsd_err_t TraceFormatterFrameDecoder::Configure(uint32_t cfg_flags)
{
    if(!m_pDecoder) 
    {  
        if(m_instNum >= 0)
            m_pDecoder = new (std::nothrow) TraceFmtDcdImpl(m_instNum);
        else
            m_pDecoder = new (std::nothrow) TraceFmtDcdImpl();
        if(!m_pDecoder) return OCSD_ERR_MEM;
    }
    m_pDecoder->DecodeConfigure(cfg_flags);
    return OCSD_OK;
}

const uint32_t TraceFormatterFrameDecoder::getConfigFlags() const
{
    uint32_t flags = 0;
    if(m_pDecoder)
        flags = m_pDecoder->m_cfgFlags;
    return flags;
}


/* enable / disable ID streams - default as all enabled */
ocsd_err_t TraceFormatterFrameDecoder::OutputFilterIDs(std::vector<uint8_t> &id_list, bool bEnable)
{
    return (m_pDecoder == 0) ? OCSD_ERR_NOT_INIT : m_pDecoder->OutputFilterIDs(id_list,bEnable);
}

ocsd_err_t TraceFormatterFrameDecoder::OutputFilterAllIDs(bool bEnable)
{
    return (m_pDecoder == 0) ? OCSD_ERR_NOT_INIT : m_pDecoder->OutputFilterAllIDs(bEnable);
}

/* decode control */
ocsd_datapath_resp_t TraceFormatterFrameDecoder::Reset()
{
    return (m_pDecoder == 0) ? OCSD_RESP_FATAL_NOT_INIT : m_pDecoder->Reset();
}

ocsd_datapath_resp_t TraceFormatterFrameDecoder::Flush()
{
    return (m_pDecoder == 0) ? OCSD_RESP_FATAL_NOT_INIT : m_pDecoder->Flush();
}


/* End of File trc_frame_deformatter.cpp */
