/******************************************************************************
 *
 * Project:  SEG-Y Translator
 * Purpose:  Implements OGRSEGYLayer class.
 * Author:   Even Rouault, <even dot rouault at mines dash paris dot org>
 *
 ******************************************************************************
 * Copyright (c) 2011, Even Rouault <even dot rouault at mines-paris dot org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMSEGYS OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ****************************************************************************/

#include "ogr_segy.h"
#include "cpl_conv.h"
#include "cpl_string.h"
#include "ogr_p.h"
#include "ogr_srs_api.h"

CPL_CVSID("$Id: ogrsegylayer.cpp 22f8ae3bf7bc3cccd970992655c63fc5254d3206 2018-04-08 20:13:05 +0200 Even Rouault $")

// #define SEGY_EXTENSIONS

constexpr int DT_IBM_4BYTES_FP       = 1;
constexpr int DT_4BYTES_INT          = 2;
constexpr int DT_2BYTES_INT          = 3;
constexpr int DT_4BYTES_FP_WITH_GAIN = 4;
constexpr int DT_IEEE_4BYTES_FP      = 5;
constexpr int DT_1BYTE_INT           = 8;

typedef struct
{
    const char*     pszName;
    OGRFieldType    eType;
} FieldDesc;

static const FieldDesc SEGYFields[] =
{
    { "TRACE_NUMBER_WITHIN_LINE", OFTInteger },
    { "TRACE_NUMBER_WITHIN_FILE", OFTInteger },
    { "ORIGINAL_FIELD_RECORD_NUMBER", OFTInteger },
    { "TRACE_NUMBER_WITHIN_ORIGINAL_FIELD_RECORD", OFTInteger },
    { "TRACE_IDENTIFICATION_CODE", OFTInteger },
    { "ENSEMBLE_NUMBER", OFTInteger },
    { "TRACE_NUMBER_WITHIN_ENSEMBLE", OFTInteger },
    { "NUMBER_VERTICAL_SUMMED_TRACES", OFTInteger },
    { "NUMBER_HORIZONTAL_STACKED_TRACES", OFTInteger },
    { "DATA_USE", OFTInteger },
    { "DISTANCE_SOURCE_GROUP", OFTInteger },
    { "RECEIVER_GROUP_ELEVATION", OFTInteger },
    { "SURFACE_ELEVATION_AT_SOURCE", OFTInteger },
    { "SOURCE_DEPTH_BELOW_SURFACE", OFTInteger },
    { "DATUM_ELEVATION_AT_RECEIVER_GROUP", OFTInteger },
    { "DATUM_ELEVATION_AT_SOURCE", OFTInteger },
    { "WATER_DEPTH_AT_SOURCE", OFTInteger },
    { "WATER_DEPTH_AT_GROUP", OFTInteger },
    { "VERTICAL_SCALAR", OFTInteger },
    { "HORIZONTAL_SCALAR", OFTInteger },
    { "SOURCE_X", OFTInteger },
    { "SOURCE_Y", OFTInteger },
    { "GROUP_X", OFTInteger },
    { "GROUP_Y", OFTInteger },
    { "COORDINATE_UNITS", OFTInteger },
    { "WEATHERING_VELOCITY", OFTInteger },
    { "SUB_WEATHERING_VELOCITY", OFTInteger },
    { "UPHOLE_TIME_AT_SOURCE", OFTInteger },
    { "UPHOLE_TIME_AT_GROUP", OFTInteger },
    { "SOURCE_STATIC_CORRECTION", OFTInteger },
    { "GROUP_STATIC_CORRECTION", OFTInteger },
    { "TOTAL_STATIC_CORRECTION", OFTInteger },
    { "LAG_TIME_A", OFTInteger },
    { "LAG_TIME_B", OFTInteger },
    { "DELAY_RECORDING_TIME", OFTInteger },
    { "MUTE_TIME_START", OFTInteger },
    { "MUTE_TIME_END", OFTInteger },
    { "SAMPLES", OFTInteger },
    { "SAMPLE_INTERVAL", OFTInteger },
    { "GAIN_TYPE", OFTInteger },
    { "INSTRUMENT_GAIN_CONSTANT", OFTInteger },
    { "INSTRUMENT_INITIAL_GAIN", OFTInteger },
    { "CORRELATED", OFTInteger },
    { "SWEEP_FREQUENCY_AT_START", OFTInteger },
    { "SWEEP_FREQUENCY_AT_END", OFTInteger },
    { "SWEEP_LENGTH", OFTInteger },
    { "SWEEP_TYPE", OFTInteger },
    { "SWEEP_TRACE_TAPER_LENGTH_AT_START", OFTInteger },
    { "SWEEP_TRACE_TAPER_LENGTH_AT_END", OFTInteger },
    { "TAPER_TYPE", OFTInteger },
    { "ALIAS_FILTER_FREQUENCY", OFTInteger },
    { "ALIAS_FILTER_SLOPE", OFTInteger },
    { "NOTCH_FILTER_FREQUENCY", OFTInteger },
    { "NOTCH_FILTER_SLOPE", OFTInteger },
    { "LOW_CUT_FREQUENCY", OFTInteger },
    { "HIGH_CUT_FREQUENCY", OFTInteger },
    { "LOW_CUT_SLOPE", OFTInteger },
    { "HIGH_CUT_SLOPE", OFTInteger },
    { "YEAR", OFTInteger },
    { "DAY_OF_YEAR", OFTInteger },
    { "HOUR", OFTInteger },
    { "MINUTE", OFTInteger },
    { "SECOND", OFTInteger },
    { "TIME_BASIC_CODE", OFTInteger },
    { "TRACE_WEIGHTING_FACTOR", OFTInteger },
    { "GEOPHONE_GROUP_NUMBER_OF_ROLL_SWITH", OFTInteger },
    { "GEOPHONE_GROUP_NUMBER_OF_TRACE_NUMBER_ONE", OFTInteger },
    { "GEOPHONE_GROUP_NUMBER_OF_LAST_TRACE", OFTInteger },
    { "GAP_SIZE", OFTInteger },
    { "OVER_TRAVEL", OFTInteger },
};

/* SEGY >= 1.0 */
static const FieldDesc SEGYFields10[] =
{
    { "INLINE_NUMBER", OFTInteger },
    { "CROSSLINE_NUMBER", OFTInteger },
    { "SHOTPOINT_NUMBER", OFTInteger },
    { "SHOTPOINT_SCALAR", OFTInteger },
    { "VALUE_MEASUREMENT_UNIT", OFTInteger },
    { "TRADUCTION_CONSTANT_MANTISSA", OFTInteger },
    { "TRADUCTION_CONSTANT_POWER", OFTInteger },
    { "DEVICE_IDENTIFIER", OFTInteger },
    { "TIME_SCALAR", OFTInteger },
    { "ORIENTATION_TYPE", OFTInteger },
    { "SOURCE_EFFORT_MANTISSA", OFTInteger },
    { "SOURCE_EFFORT_POWER", OFTInteger },
    { "SOURCE_MEASUREMENT_UNIT", OFTInteger },
    { "X_COORDINATE", OFTInteger },
    { "Y_COORDINATE", OFTInteger },
};

constexpr int TRACE_NUMBER_WITHIN_LINE = 0;
constexpr int TRACE_NUMBER_WITHIN_FILE = 1;
constexpr int ORIGINAL_FIELD_RECORD_NUMBER = 2;
constexpr int TRACE_NUMBER_WITHIN_ORIGINAL_FIELD_RECORD = 3;
constexpr int TRACE_IDENTIFICATION_CODE = 4;
constexpr int ENSEMBLE_NUMBER = 5;
constexpr int TRACE_NUMBER_WITHIN_ENSEMBLE = 6;
constexpr int NUMBER_VERTICAL_SUMMED_TRACES = 7;
constexpr int NUMBER_HORIZONTAL_STACKED_TRACES = 8;
constexpr int DATA_USE = 9;
constexpr int DISTANCE_SOURCE_GROUP = 10;
constexpr int RECEIVER_GROUP_ELEVATION = 11;
constexpr int SURFACE_ELEVATION_AT_SOURCE = 12;
constexpr int SOURCE_DEPTH_BELOW_SURFACE = 13;
constexpr int DATUM_ELEVATION_AT_RECEIVER_GROUP = 14;
constexpr int DATUM_ELEVATION_AT_SOURCE = 15;
constexpr int WATER_DEPTH_AT_SOURCE = 16;
constexpr int WATER_DEPTH_AT_GROUP = 17;
constexpr int VERTICAL_SCALAR = 18;
constexpr int HORIZONTAL_SCALAR = 19;
constexpr int SOURCE_X = 20;
constexpr int SOURCE_Y = 21;
constexpr int GROUP_X = 22;
constexpr int GROUP_Y = 23;
constexpr int COORDINATE_UNITS = 24;
constexpr int WEATHERING_VELOCITY = 25;
constexpr int SUB_WEATHERING_VELOCITY = 26;
constexpr int UPHOLE_TIME_AT_SOURCE = 27;
constexpr int UPHOLE_TIME_AT_GROUP = 28;
constexpr int SOURCE_STATIC_CORRECTION = 29;
constexpr int GROUP_STATIC_CORRECTION = 30;
constexpr int TOTAL_STATIC_CORRECTION = 31;
constexpr int LAG_TIME_A = 32;
constexpr int LAG_TIME_B = 33;
constexpr int DELAY_RECORDING_TIME = 34;
constexpr int MUTE_TIME_START = 35;
constexpr int MUTE_TIME_END = 36;
constexpr int SAMPLES = 37;
constexpr int SAMPLE_INTERVAL = 38;
constexpr int GAIN_TYPE = 39;
constexpr int INSTRUMENT_GAIN_CONSTANT = 40;
constexpr int INSTRUMENT_INITIAL_GAIN = 41;
constexpr int CORRELATED = 42;
constexpr int SWEEP_FREQUENCY_AT_START = 43;
constexpr int SWEEP_FREQUENCY_AT_END = 44;
constexpr int SWEEP_LENGTH = 45;
constexpr int SWEEP_TYPE = 46;
constexpr int SWEEP_TRACE_TAPER_LENGTH_AT_START = 47;
constexpr int SWEEP_TRACE_TAPER_LENGTH_AT_END = 48;
constexpr int TAPER_TYPE = 49;
constexpr int ALIAS_FILTER_FREQUENCY = 50;
constexpr int ALIAS_FILTER_SLOPE = 51;
constexpr int NOTCH_FILTER_FREQUENCY = 52;
constexpr int NOTCH_FILTER_SLOPE = 53;
constexpr int LOW_CUT_FREQUENCY = 54;
constexpr int HIGH_CUT_FREQUENCY = 55;
constexpr int LOW_CUT_SLOPE = 56;
constexpr int HIGH_CUT_SLOPE = 57;
constexpr int YEAR = 58;
constexpr int DAY_OF_YEAR = 59;
constexpr int HOUR = 60;
constexpr int MINUTE = 61;
constexpr int SECOND = 62;
constexpr int TIME_BASIC_CODE = 63;
constexpr int TRACE_WEIGHTING_FACTOR = 64;
constexpr int GEOPHONE_GROUP_NUMBER_OF_ROLL_SWITH = 65;
constexpr int GEOPHONE_GROUP_NUMBER_OF_TRACE_NUMBER_ONE = 66;
constexpr int GEOPHONE_GROUP_NUMBER_OF_LAST_TRACE = 67;
constexpr int GAP_SIZE = 68;
constexpr int OVER_TRAVEL = 69;
constexpr int INLINE_NUMBER = 70;
constexpr int CROSSLINE_NUMBER = 71;
constexpr int SHOTPOINT_NUMBER = 72;
constexpr int SHOTPOINT_SCALAR = 73;
constexpr int VALUE_MEASUREMENT_UNIT = 74;
constexpr int TRADUCTION_CONSTANT_MANTISSA = 75;
constexpr int TRADUCTION_CONSTANT_POWER = 76;
constexpr int DEVICE_IDENTIFIER = 77;
constexpr int TIME_SCALAR = 78;
constexpr int ORIENTATION_TYPE = 79;
constexpr int SOURCE_EFFORT_MANTISSA = 80;
constexpr int SOURCE_EFFORT_POWER = 81;
constexpr int SOURCE_MEASUREMENT_UNIT = 82;
constexpr int X_COORDINATE = 83;
constexpr int Y_COORDINATE = 84;

/************************************************************************/
/*                       SEGYReadMSBFloat32()                           */
/************************************************************************/

#ifdef SEGY_EXTENSIONS
static float SEGYReadMSBFloat32(const GByte* pabyVal)
{
    float fVal = 0.0f;
    memcpy(&fVal, pabyVal, 4);
    CPL_MSBPTR32(&fVal);
    return fVal;
}
#endif

/************************************************************************/
/*                           OGRSEGYLayer()                            */
/************************************************************************/

OGRSEGYLayer::OGRSEGYLayer( const char* pszFilename,
                            VSILFILE* fpIn,
                            SEGYBinaryFileHeader* psBFH ) :
    poFeatureDefn(new OGRFeatureDefn(CPLGetBasename(pszFilename))),
    bEOF(false),
    nNextFID(0),
    fp(fpIn),
    nDataSize(0)
{
    memcpy(&sBFH, psBFH, sizeof(sBFH));

    switch( sBFH.nDataSampleType )
    {
        case DT_IBM_4BYTES_FP: nDataSize = 4; break;
        case DT_4BYTES_INT: nDataSize = 4; break;
        case DT_2BYTES_INT: nDataSize = 2; break;
        case DT_4BYTES_FP_WITH_GAIN: nDataSize = 4; break;
        case DT_IEEE_4BYTES_FP: nDataSize = 4; break;
        case DT_1BYTE_INT: nDataSize = 1; break;
        default: break;
    }

    poFeatureDefn->Reference();
    poFeatureDefn->SetGeomType( wkbPoint );

    for( int i = 0; i < static_cast<int>(sizeof(SEGYFields) /
                                         sizeof(SEGYFields[0]));
         i++ )
    {
        OGRFieldDefn oField( SEGYFields[i].pszName,
                             SEGYFields[i].eType );
        poFeatureDefn->AddFieldDefn( &oField );
    }

    if( sBFH.dfSEGYRevisionNumber >= 1.0 )
    {
        for( int i = 0;
             i < static_cast<int>(sizeof(SEGYFields10) /
                                  sizeof(SEGYFields10[0]));
             i++ )
        {
            OGRFieldDefn oField( SEGYFields10[i].pszName,
                                 SEGYFields10[i].eType );
            poFeatureDefn->AddFieldDefn( &oField );
        }
    }

    OGRFieldDefn oField( "SAMPLE_ARRAY", OFTRealList );
    poFeatureDefn->AddFieldDefn(&oField);

    OGRSEGYLayer::ResetReading();
}

/************************************************************************/
/*                            ~OGRSEGYLayer()                          */
/************************************************************************/

OGRSEGYLayer::~OGRSEGYLayer()

{
    poFeatureDefn->Release();

    VSIFCloseL( fp );
}

/************************************************************************/
/*                            ResetReading()                            */
/************************************************************************/

void OGRSEGYLayer::ResetReading()

{
    nNextFID = 0;
    bEOF = false;

    VSIFSeekL( fp, 3200 + 400 + 3200 * sBFH.nNumberOfExtendedTextualFileHeader,
               SEEK_SET );
}

/************************************************************************/
/*                           GetNextFeature()                           */
/************************************************************************/

OGRFeature *OGRSEGYLayer::GetNextFeature()
{
    while( true )
    {
        OGRFeature *poFeature = GetNextRawFeature();
        if( poFeature == nullptr )
            return nullptr;

        if( (m_poFilterGeom == nullptr
             || FilterGeometry( poFeature->GetGeometryRef() ) )
            && (m_poAttrQuery == nullptr
                || m_poAttrQuery->Evaluate( poFeature )) )
        {
            return poFeature;
        }

        delete poFeature;
    }
}

/************************************************************************/
/*                             GetIBMFloat()                            */
/************************************************************************/

static float GetIBMFloat(const GByte* pabyData)
{
    int nVal = 0;
    memcpy(&nVal, pabyData, 4);
    CPL_MSBPTR32(&nVal);
    const int nSign = 1 - 2 * (((unsigned)nVal >> 31) & 0x01);
    const int nExp = (nVal >> 24) & 0x7f;
    const int nMant = nVal & 0xffffff;

    if( nExp == 0x7f )
    {
        nVal = (nVal & 0x80000000) | (0xff << 23) | (nMant >> 1);
        float fVal = 0;
        memcpy(&fVal, &nVal, 4);
        return fVal;
    }

    return
        static_cast<float>(
            static_cast<double>(nSign) *
            nMant *
            pow(2.0, 4 * (nExp - 64) - 24));
}

/************************************************************************/
/*                         GetNextRawFeature()                          */
/************************************************************************/

OGRFeature *OGRSEGYLayer::GetNextRawFeature()
{
    if( bEOF )
        return nullptr;

    GByte abyTraceHeader[240];

    if( (int)VSIFReadL(abyTraceHeader, 1, 240, fp) != 240 )
    {
        bEOF = true;
        return nullptr;
    }

    const int nTraceNumberWithinLine = SEGYReadMSBInt32(abyTraceHeader + 0);
    const int nTraceNumberWithinFile = SEGYReadMSBInt32(abyTraceHeader + 4);
    const int nOriginalFieldRecordNumber = SEGYReadMSBInt32(abyTraceHeader + 8);
    const int nTraceNumberWithinOriginalFieldRecord = SEGYReadMSBInt32(abyTraceHeader + 12);
    const int nEnsembleNumber = SEGYReadMSBInt32(abyTraceHeader + 20);
    const int nTraceNumberWithinEnsemble = SEGYReadMSBInt32(abyTraceHeader + 24);
    const int nTraceIdentificationCode = SEGYReadMSBInt16(abyTraceHeader + 28);
    const int nNumberVerticalSummedTraces = SEGYReadMSBInt16(abyTraceHeader + 30);
    const int nNumberHorizontalStackedTraces = SEGYReadMSBInt16(abyTraceHeader + 32);
    const int nDataUse = SEGYReadMSBInt16(abyTraceHeader + 34);
    const int nDistanceSourceGroup = SEGYReadMSBInt32(abyTraceHeader + 36);
    const int nReceiverGroupElevation = SEGYReadMSBInt32(abyTraceHeader + 40);
    const int nSurfaceElevationAtSource = SEGYReadMSBInt32(abyTraceHeader + 44);
    const int nSourceDepthBelowSurface = SEGYReadMSBInt32(abyTraceHeader + 48);
    const int nDatumElevationAtReceiverGroup = SEGYReadMSBInt32(abyTraceHeader + 52);
    const int nDatumElevationAtSource = SEGYReadMSBInt32(abyTraceHeader + 56);
    const int nWaterDepthAtSource = SEGYReadMSBInt32(abyTraceHeader + 60);
    const int nWaterDepthAtGroup = SEGYReadMSBInt32(abyTraceHeader + 64);
    const int nVerticalScalar = SEGYReadMSBInt16(abyTraceHeader + 68);
    const int nHorizontalScalar = SEGYReadMSBInt16(abyTraceHeader + 70);
    const int nSourceX = SEGYReadMSBInt32(abyTraceHeader + 72);
    const int nSourceY = SEGYReadMSBInt32(abyTraceHeader + 76);
    const int nGroupX = SEGYReadMSBInt32(abyTraceHeader + 80);
    const int nGroupY = SEGYReadMSBInt32(abyTraceHeader + 84);
    const int nCoordinateUnits = SEGYReadMSBInt16(abyTraceHeader + 88);
    const int nWeatheringVelocity = SEGYReadMSBInt16(abyTraceHeader + 90);
    const int nSubWeatheringVelocity = SEGYReadMSBInt16(abyTraceHeader + 92);

    const int nUpholeTimeAtSource = SEGYReadMSBInt16(abyTraceHeader + 94);
    const int nUpholeTimeAtGroup = SEGYReadMSBInt16(abyTraceHeader + 96);
    const int nSourceStaticCorrection = SEGYReadMSBInt16(abyTraceHeader + 98);
    const int nGroupStaticCorrection = SEGYReadMSBInt16(abyTraceHeader + 100);
    const int nTotalStaticCorrection = SEGYReadMSBInt16(abyTraceHeader + 102);
    const int nLagTimeA = SEGYReadMSBInt16(abyTraceHeader + 104);
    const int nLagTimeB = SEGYReadMSBInt16(abyTraceHeader + 106);
    const int nDelayRecordingTime = SEGYReadMSBInt16(abyTraceHeader + 108);
    const int nMuteTimeStart = SEGYReadMSBInt16(abyTraceHeader + 110);
    const int nMuteTimeEnd = SEGYReadMSBInt16(abyTraceHeader + 112);

    int nSamples = SEGYReadMSBInt16(abyTraceHeader + 114);
    // Happens with
    // ftp://software.seg.org/pub/datasets/2D/Hess_VTI/timodel_c11.segy.gz
    if( nSamples == 0 )
        nSamples = sBFH.nSamplesPerDataTrace;

    if( nSamples < 0 )
    {
        bEOF = true;
        return nullptr;
    }
    const int nSampleInterval = SEGYReadMSBInt16(abyTraceHeader + 116);

    const int nGainType = SEGYReadMSBInt16(abyTraceHeader + 118);
    const int nInstrumentGainConstant = SEGYReadMSBInt16(abyTraceHeader + 120);
    const int nInstrumentInitialGain = SEGYReadMSBInt16(abyTraceHeader + 122);
    const int nCorrelated = SEGYReadMSBInt16(abyTraceHeader + 124);
    const int nSweepFrequencyAtStart = SEGYReadMSBInt16(abyTraceHeader + 126);
    const int nSweepFrequencyAtEnd = SEGYReadMSBInt16(abyTraceHeader + 128);
    const int nSweepLength = SEGYReadMSBInt16(abyTraceHeader + 130);
    const int nSweepType = SEGYReadMSBInt16(abyTraceHeader + 132);
    const int nSweepTraceTaperLengthAtStart = SEGYReadMSBInt16(abyTraceHeader + 134);
    const int nSweepTraceTaperLengthAtEnd = SEGYReadMSBInt16(abyTraceHeader + 136);
    const int nTaperType = SEGYReadMSBInt16(abyTraceHeader + 138);
    const int nAliasFilterFrequency = SEGYReadMSBInt16(abyTraceHeader + 140);
    const int nAliasFilterSlope = SEGYReadMSBInt16(abyTraceHeader + 142);
    const int nNotchFilterFrequency = SEGYReadMSBInt16(abyTraceHeader + 144);
    const int nNotchFilterSlope = SEGYReadMSBInt16(abyTraceHeader + 146);
    const int nLowCutFrequency = SEGYReadMSBInt16(abyTraceHeader + 148);
    const int nHighCutFrequency = SEGYReadMSBInt16(abyTraceHeader + 150);
    const int nLowCutSlope = SEGYReadMSBInt16(abyTraceHeader + 152);
    const int nHighCutSlope = SEGYReadMSBInt16(abyTraceHeader + 154);

    const int nYear = SEGYReadMSBInt16(abyTraceHeader + 156);
    const int nDayOfYear = SEGYReadMSBInt16(abyTraceHeader + 158);
    const int nHour = SEGYReadMSBInt16(abyTraceHeader + 160);
    const int nMinute = SEGYReadMSBInt16(abyTraceHeader + 162);
    const int nSecond = SEGYReadMSBInt16(abyTraceHeader + 164);
    const int nTimeBasicCode = SEGYReadMSBInt16(abyTraceHeader + 166);

    const int nTraceWeightingFactor = SEGYReadMSBInt16(abyTraceHeader + 168);
    const int nGeophoneGroupNumberOfRollSwith = SEGYReadMSBInt16(abyTraceHeader + 170);
    const int nGeophoneGroupNumberOfTraceNumberOne = SEGYReadMSBInt16(abyTraceHeader + 172);
    const int nGeophoneGroupNumberOfLastTrace = SEGYReadMSBInt16(abyTraceHeader + 174);
    const int nGapSize = SEGYReadMSBInt16(abyTraceHeader + 176);
    const int nOverTravel = SEGYReadMSBInt16(abyTraceHeader + 178);

    const int nInlineNumber = SEGYReadMSBInt32(abyTraceHeader + 188);
    const int nCrosslineNumber = SEGYReadMSBInt32(abyTraceHeader + 192);
    const int nShotpointNumber = SEGYReadMSBInt32(abyTraceHeader + 196);
    const int nShotpointScalar = SEGYReadMSBInt16(abyTraceHeader + 200);

    const int nValueMeasurementUnit = SEGYReadMSBInt16(abyTraceHeader + 202);
    const int nTraductionConstantMantissa = SEGYReadMSBInt32(abyTraceHeader + 204);
    const int nTraductionConstantPower = SEGYReadMSBInt16(abyTraceHeader + 208);
    const int nDeviceIdentifier = SEGYReadMSBInt16(abyTraceHeader + 212);
    const int nTimeScalar = SEGYReadMSBInt16(abyTraceHeader + 214);
    const int nOrientationType = SEGYReadMSBInt16(abyTraceHeader + 216);
    const int nSourceEffortMantissa = SEGYReadMSBInt32(abyTraceHeader + 224);
    const int nSourceEffortPower = SEGYReadMSBInt16(abyTraceHeader + 228);
    const int nSourceMeasurementUnit = SEGYReadMSBInt16(abyTraceHeader + 230);
    const int nXCoordinate = SEGYReadMSBInt32(abyTraceHeader + 180);
    const int nYCoordinate = SEGYReadMSBInt32(abyTraceHeader + 184);

#ifdef SEGY_EXTENSIONS
#if DEBUG_VERBOSE
    // Extensions of http://sioseis.ucsd.edu/segy.header.html
    const float fDeepWaterDelay = SEGYReadMSBFloat32(abyTraceHeader + 180);
    const float fStartMuteTime  = SEGYReadMSBFloat32(abyTraceHeader + 184);
    const float fEndMuteTime  = SEGYReadMSBFloat32(abyTraceHeader + 188);
    const float fSampleInterval  = SEGYReadMSBFloat32(abyTraceHeader + 192);
    const float fWaterBottomTime  = SEGYReadMSBFloat32(abyTraceHeader + 196);
    const int nEndOfRp = SEGYReadMSBInt16(abyTraceHeader + 200);
    // TODO(schwehr): Use the extension vars and move DEBUG_VERBOSE here.
    CPLDebug("SEGY", "fDeepWaterDelay = %f", fDeepWaterDelay);
    CPLDebug("SEGY", "fStartMuteTime = %f", fStartMuteTime);
    CPLDebug("SEGY", "fEndMuteTime = %f", fEndMuteTime);
    CPLDebug("SEGY", "fSampleInterval = %f", fSampleInterval);
    CPLDebug("SEGY", "fWaterBottomTime = %f", fWaterBottomTime);
    CPLDebug("SEGY", "nEndOfRp = %d", nEndOfRp);
#endif  // DEBUG_VERBOSE
#endif  // SEGY_EXTENSIONS

    double dfHorizontalScale =
        (nHorizontalScalar > 0) ? nHorizontalScalar :
        (nHorizontalScalar < 0) ? 1.0 / -nHorizontalScalar : 1.0;
    if( nCoordinateUnits == 2 )
        dfHorizontalScale /= 3600;

    const double dfGroupX = nGroupX * dfHorizontalScale;
    const double dfGroupY = nGroupY * dfHorizontalScale;

#if DEBUG_VERBOSE
    const double dfSourceX = nSourceX * dfHorizontalScale;
    const double dfSourceY = nSourceY * dfHorizontalScale;

    CPLDebug("SEGY", "nTraceNumberWithinLine = %d", nTraceNumberWithinLine);
    CPLDebug("SEGY", "nTraceNumberWithinFile = %d", nTraceNumberWithinFile);
    CPLDebug("SEGY", "nOriginalFieldRecordNumber = %d", nOriginalFieldRecordNumber);
    CPLDebug("SEGY", "nTraceNumberWithinOriginalFieldRecord = %d", nTraceNumberWithinOriginalFieldRecord);
    CPLDebug("SEGY", "nTraceIdentificationCode = %d", nTraceIdentificationCode);
    CPLDebug("SEGY", "nEnsembleNumber = %d", nEnsembleNumber);
    CPLDebug("SEGY", "nTraceNumberWithinEnsemble = %d", nTraceNumberWithinEnsemble);
    CPLDebug("SEGY", "nNumberVerticalSummedTraces = %d", nNumberVerticalSummedTraces);
    CPLDebug("SEGY", "nNumberHorizontalStackedTraces = %d", nNumberHorizontalStackedTraces);
    CPLDebug("SEGY", "nDataUse = %d", nDataUse);
    CPLDebug("SEGY", "nDistanceSourceGroup = %d", nDistanceSourceGroup);
    CPLDebug("SEGY", "nReceiverGroupElevation = %d", nReceiverGroupElevation);
    CPLDebug("SEGY", "nSurfaceElevationAtSource = %d", nSurfaceElevationAtSource);
    CPLDebug("SEGY", "nSourceDepthBelowSurface = %d", nSourceDepthBelowSurface);
    CPLDebug("SEGY", "nDatumElevationAtReceiverGroup = %d", nDatumElevationAtReceiverGroup);
    CPLDebug("SEGY", "nDatumElevationAtSource = %d", nDatumElevationAtSource);
    CPLDebug("SEGY", "nWaterDepthAtSource = %d", nWaterDepthAtSource);
    CPLDebug("SEGY", "nWaterDepthAtGroup = %d", nWaterDepthAtGroup);
    CPLDebug("SEGY", "nVerticalScalar = %d", nVerticalScalar);
    CPLDebug("SEGY", "nHorizontalScalar = %d", nHorizontalScalar);
    CPLDebug("SEGY", "nSourceX = %d", nSourceX);
    CPLDebug("SEGY", "nSourceY = %d", nSourceY);
    CPLDebug("SEGY", "dfSourceX = %f", dfSourceX);
    CPLDebug("SEGY", "dfSourceY = %f", dfSourceY);
    CPLDebug("SEGY", "nGroupX = %d", nGroupX);
    CPLDebug("SEGY", "nGroupY = %d", nGroupY);
    CPLDebug("SEGY", "dfGroupX = %f", dfGroupX);
    CPLDebug("SEGY", "dfGroupY = %f", dfGroupY);
    CPLDebug("SEGY", "nCoordinateUnits = %d", nCoordinateUnits);

    CPLDebug("SEGY", "nWeatheringVelocity = %d", nWeatheringVelocity);
    CPLDebug("SEGY", "nSubWeatheringVelocity = %d", nSubWeatheringVelocity);
    CPLDebug("SEGY", "nUpholeTimeAtSource = %d", nUpholeTimeAtSource);
    CPLDebug("SEGY", "nUpholeTimeAtGroup = %d", nUpholeTimeAtGroup);
    CPLDebug("SEGY", "nSourceStaticCorrection = %d", nSourceStaticCorrection);
    CPLDebug("SEGY", "nGroupStaticCorrection = %d", nGroupStaticCorrection);
    CPLDebug("SEGY", "nTotalStaticCorrection = %d", nTotalStaticCorrection);
    CPLDebug("SEGY", "nLagTimeA = %d", nLagTimeA);
    CPLDebug("SEGY", "nLagTimeB = %d", nLagTimeB);
    CPLDebug("SEGY", "nDelayRecordingTime = %d", nDelayRecordingTime);
    CPLDebug("SEGY", "nMuteTimeStart = %d", nMuteTimeStart);
    CPLDebug("SEGY", "nMuteTimeEnd = %d", nMuteTimeEnd);

    CPLDebug("SEGY", "nSamples = %d", nSamples);
    CPLDebug("SEGY", "nSampleInterval = %d", nSampleInterval);

    CPLDebug("SEGY", "nGainType = %d", nGainType);
    CPLDebug("SEGY", "nInstrumentGainConstant = %d", nInstrumentGainConstant);
    CPLDebug("SEGY", "nInstrumentInitialGain = %d", nInstrumentInitialGain);
    CPLDebug("SEGY", "nCorrelated = %d", nCorrelated);
    CPLDebug("SEGY", "nSweepFrequencyAtStart = %d", nSweepFrequencyAtStart);
    CPLDebug("SEGY", "nSweepFrequencyAtEnd = %d", nSweepFrequencyAtEnd);
    CPLDebug("SEGY", "nSweepLength = %d", nSweepLength);
    CPLDebug("SEGY", "nSweepType = %d", nSweepType);
    CPLDebug("SEGY", "nSweepTraceTaperLengthAtStart = %d", nSweepTraceTaperLengthAtStart);
    CPLDebug("SEGY", "nSweepTraceTaperLengthAtEnd = %d", nSweepTraceTaperLengthAtEnd);
    CPLDebug("SEGY", "nTaperType = %d", nTaperType);
    CPLDebug("SEGY", "nAliasFilterFrequency = %d", nAliasFilterFrequency);
    CPLDebug("SEGY", "nAliasFilterSlope = %d", nAliasFilterSlope);
    CPLDebug("SEGY", "nNotchFilterFrequency = %d", nNotchFilterFrequency);
    CPLDebug("SEGY", "nNotchFilterSlope = %d", nNotchFilterSlope);
    CPLDebug("SEGY", "nLowCutFrequency = %d", nLowCutFrequency);
    CPLDebug("SEGY", "nHighCutFrequency = %d", nHighCutFrequency);
    CPLDebug("SEGY", "nLowCutSlope = %d", nLowCutSlope);
    CPLDebug("SEGY", "nHighCutSlope = %d", nHighCutSlope);
    CPLDebug("SEGY", "nYear = %d", nYear);
    CPLDebug("SEGY", "nDayOfYear = %d", nDayOfYear);
    CPLDebug("SEGY", "nHour = %d", nHour);
    CPLDebug("SEGY", "nMinute = %d", nMinute);
    CPLDebug("SEGY", "nSecond = %d", nSecond);
    CPLDebug("SEGY", "nTimeBasicCode = %d", nTimeBasicCode);
    CPLDebug("SEGY", "nTraceWeightingFactor = %d", nTraceWeightingFactor);
    CPLDebug("SEGY", "nGeophoneGroupNumberOfRollSwith = %d", nGeophoneGroupNumberOfRollSwith);
    CPLDebug("SEGY", "nGeophoneGroupNumberOfTraceNumberOne = %d", nGeophoneGroupNumberOfTraceNumberOne);
    CPLDebug("SEGY", "nGeophoneGroupNumberOfLastTrace = %d", nGeophoneGroupNumberOfLastTrace);
    CPLDebug("SEGY", "nGapSize = %d", nGapSize);
    CPLDebug("SEGY", "nOverTravel = %d", nOverTravel);

    if( sBFH.dfSEGYRevisionNumber >= 1.0 )
    {
        CPLDebug("SEGY", "nInlineNumber = %d", nInlineNumber);
        CPLDebug("SEGY", "nCrosslineNumber = %d", nCrosslineNumber);
        CPLDebug("SEGY", "nShotpointNumber = %d", nShotpointNumber);
        CPLDebug("SEGY", "nShotpointScalar = %d", nShotpointScalar);
        CPLDebug("SEGY", "nValueMeasurementUnit = %d", nValueMeasurementUnit);
        CPLDebug("SEGY", "nTraductionConstantMantissa = %d", nTraductionConstantMantissa);
        CPLDebug("SEGY", "nTraductionConstantPower = %d", nTraductionConstantPower);
        CPLDebug("SEGY", "nDeviceIdentifier = %d", nDeviceIdentifier);
        CPLDebug("SEGY", "nTimeScalar = %d", nTimeScalar);
        CPLDebug("SEGY", "nOrientationType = %d", nOrientationType);
        CPLDebug("SEGY", "nSourceEffortMantissa = %d", nSourceEffortMantissa);
        CPLDebug("SEGY", "nSourceEffortPower = %d", nSourceEffortPower);
        CPLDebug("SEGY", "nSourceMeasurementUnit = %d", nSourceMeasurementUnit);
    }
#endif

    GByte* pabyData = static_cast<GByte *>(
        VSI_MALLOC_VERBOSE(nDataSize * nSamples));
    double* padfValues = static_cast<double *>(
        VSI_CALLOC_VERBOSE(nSamples, sizeof(double)));
    if( pabyData == nullptr || padfValues == nullptr )
    {
        VSIFSeekL( fp, nDataSize * nSamples, SEEK_CUR );
    }
    else
    {
        if( static_cast<int>(VSIFReadL(pabyData, nDataSize,
                                       nSamples, fp)) != nSamples )
        {
            bEOF = true;
        }
        for( int i = 0; i < nSamples; i++ )
        {
            switch( sBFH.nDataSampleType )
            {
                case DT_IBM_4BYTES_FP:
                {
                    padfValues[i] = GetIBMFloat(pabyData + i * 4);
                    break;
                }

                case DT_4BYTES_INT:
                {
                    int nVal = 0;
                    memcpy(&nVal, pabyData + i * 4, 4);
                    CPL_MSBPTR32(&nVal);
                    padfValues[i] = nVal;
                    break;
                }

                case DT_2BYTES_INT:
                {
                    GInt16 nVal = 0;
                    memcpy(&nVal, pabyData + i * 2, 2);
                    CPL_MSBPTR16(&nVal);
                    padfValues[i] = nVal;
                    break;
                }

                case DT_IEEE_4BYTES_FP:
                {
                    float fVal = 0.0f;
                    memcpy(&fVal, pabyData + i * 4, 4);
                    CPL_MSBPTR32(&fVal);
                    padfValues[i] = fVal;
                    break;
                }

                case DT_1BYTE_INT:
                {
                    padfValues[i] = ((char*)pabyData)[i];
                    break;
                }

                default:
                    break;
            }
        }
    }
    CPLFree(pabyData);

    OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
    poFeature->SetFID(nNextFID ++);
    if( dfGroupX != 0.0 || dfGroupY != 0.0 )
        poFeature->SetGeometryDirectly(new OGRPoint(dfGroupX, dfGroupY));

    poFeature->SetField(TRACE_NUMBER_WITHIN_LINE, nTraceNumberWithinLine);
    poFeature->SetField(TRACE_NUMBER_WITHIN_FILE, nTraceNumberWithinFile);
    poFeature->SetField(ORIGINAL_FIELD_RECORD_NUMBER, nOriginalFieldRecordNumber);
    poFeature->SetField(TRACE_NUMBER_WITHIN_ORIGINAL_FIELD_RECORD, nTraceNumberWithinOriginalFieldRecord);
    poFeature->SetField(TRACE_IDENTIFICATION_CODE, nTraceIdentificationCode);
    poFeature->SetField(ENSEMBLE_NUMBER, nEnsembleNumber);
    poFeature->SetField(TRACE_NUMBER_WITHIN_ENSEMBLE, nTraceNumberWithinEnsemble);
    poFeature->SetField(NUMBER_VERTICAL_SUMMED_TRACES, nNumberVerticalSummedTraces);
    poFeature->SetField(NUMBER_HORIZONTAL_STACKED_TRACES, nNumberHorizontalStackedTraces);
    poFeature->SetField(DATA_USE, nDataUse);
    poFeature->SetField(DISTANCE_SOURCE_GROUP, nDistanceSourceGroup);
    poFeature->SetField(RECEIVER_GROUP_ELEVATION, nReceiverGroupElevation);
    poFeature->SetField(SURFACE_ELEVATION_AT_SOURCE, nSurfaceElevationAtSource);
    poFeature->SetField(SOURCE_DEPTH_BELOW_SURFACE, nSourceDepthBelowSurface);
    poFeature->SetField(DATUM_ELEVATION_AT_RECEIVER_GROUP, nDatumElevationAtReceiverGroup);
    poFeature->SetField(DATUM_ELEVATION_AT_SOURCE, nDatumElevationAtSource);
    poFeature->SetField(WATER_DEPTH_AT_SOURCE, nWaterDepthAtSource);
    poFeature->SetField(WATER_DEPTH_AT_GROUP, nWaterDepthAtGroup);
    poFeature->SetField(VERTICAL_SCALAR, nVerticalScalar);
    poFeature->SetField(HORIZONTAL_SCALAR, nHorizontalScalar);
    poFeature->SetField(SOURCE_X, nSourceX);
    poFeature->SetField(SOURCE_Y, nSourceY);
    poFeature->SetField(GROUP_X, nGroupX);
    poFeature->SetField(GROUP_Y, nGroupY);
    poFeature->SetField(COORDINATE_UNITS, nCoordinateUnits);
    poFeature->SetField(WEATHERING_VELOCITY, nWeatheringVelocity);
    poFeature->SetField(SUB_WEATHERING_VELOCITY, nSubWeatheringVelocity);
    poFeature->SetField(UPHOLE_TIME_AT_SOURCE, nUpholeTimeAtSource);
    poFeature->SetField(UPHOLE_TIME_AT_GROUP, nUpholeTimeAtGroup);
    poFeature->SetField(SOURCE_STATIC_CORRECTION, nSourceStaticCorrection);
    poFeature->SetField(GROUP_STATIC_CORRECTION, nGroupStaticCorrection);
    poFeature->SetField(TOTAL_STATIC_CORRECTION, nTotalStaticCorrection);
    poFeature->SetField(LAG_TIME_A, nLagTimeA);
    poFeature->SetField(LAG_TIME_B, nLagTimeB);
    poFeature->SetField(DELAY_RECORDING_TIME, nDelayRecordingTime);
    poFeature->SetField(MUTE_TIME_START, nMuteTimeStart);
    poFeature->SetField(MUTE_TIME_END, nMuteTimeEnd);
    poFeature->SetField(SAMPLES, nSamples);
    poFeature->SetField(SAMPLE_INTERVAL, nSampleInterval);
    poFeature->SetField(GAIN_TYPE, nGainType);
    poFeature->SetField(INSTRUMENT_GAIN_CONSTANT, nInstrumentGainConstant);
    poFeature->SetField(INSTRUMENT_INITIAL_GAIN, nInstrumentInitialGain);
    poFeature->SetField(CORRELATED, nCorrelated);
    poFeature->SetField(SWEEP_FREQUENCY_AT_START, nSweepFrequencyAtStart);
    poFeature->SetField(SWEEP_FREQUENCY_AT_END, nSweepFrequencyAtEnd);
    poFeature->SetField(SWEEP_LENGTH, nSweepLength);
    poFeature->SetField(SWEEP_TYPE, nSweepType);
    poFeature->SetField(SWEEP_TRACE_TAPER_LENGTH_AT_START, nSweepTraceTaperLengthAtStart);
    poFeature->SetField(SWEEP_TRACE_TAPER_LENGTH_AT_END, nSweepTraceTaperLengthAtEnd);
    poFeature->SetField(TAPER_TYPE, nTaperType);
    poFeature->SetField(ALIAS_FILTER_FREQUENCY, nAliasFilterFrequency);
    poFeature->SetField(ALIAS_FILTER_SLOPE, nAliasFilterSlope);
    poFeature->SetField(NOTCH_FILTER_FREQUENCY, nNotchFilterFrequency);
    poFeature->SetField(NOTCH_FILTER_SLOPE, nNotchFilterSlope);
    poFeature->SetField(LOW_CUT_FREQUENCY, nLowCutFrequency);
    poFeature->SetField(HIGH_CUT_FREQUENCY, nHighCutFrequency);
    poFeature->SetField(LOW_CUT_SLOPE, nLowCutSlope);
    poFeature->SetField(HIGH_CUT_SLOPE, nHighCutSlope);
    poFeature->SetField(YEAR, nYear);
    poFeature->SetField(DAY_OF_YEAR, nDayOfYear);
    poFeature->SetField(HOUR, nHour);
    poFeature->SetField(MINUTE, nMinute);
    poFeature->SetField(SECOND, nSecond);
    poFeature->SetField(TIME_BASIC_CODE, nTimeBasicCode);
    poFeature->SetField(TRACE_WEIGHTING_FACTOR, nTraceWeightingFactor);
    poFeature->SetField(GEOPHONE_GROUP_NUMBER_OF_ROLL_SWITH, nGeophoneGroupNumberOfRollSwith);
    poFeature->SetField(GEOPHONE_GROUP_NUMBER_OF_TRACE_NUMBER_ONE, nGeophoneGroupNumberOfTraceNumberOne);
    poFeature->SetField(GEOPHONE_GROUP_NUMBER_OF_LAST_TRACE, nGeophoneGroupNumberOfLastTrace);
    poFeature->SetField(GAP_SIZE, nGapSize);
    poFeature->SetField(OVER_TRAVEL, nOverTravel);

    if( sBFH.dfSEGYRevisionNumber >= 1.0 )
    {
        poFeature->SetField(INLINE_NUMBER, nInlineNumber);
        poFeature->SetField(CROSSLINE_NUMBER, nCrosslineNumber);
        poFeature->SetField(SHOTPOINT_NUMBER, nShotpointNumber);
        poFeature->SetField(SHOTPOINT_SCALAR, nShotpointScalar);
        poFeature->SetField(VALUE_MEASUREMENT_UNIT, nValueMeasurementUnit);
        poFeature->SetField(TRADUCTION_CONSTANT_MANTISSA, nTraductionConstantMantissa);
        poFeature->SetField(TRADUCTION_CONSTANT_POWER, nTraductionConstantPower);
        poFeature->SetField(DEVICE_IDENTIFIER, nDeviceIdentifier);
        poFeature->SetField(TIME_SCALAR, nTimeScalar);
        poFeature->SetField(ORIENTATION_TYPE, nOrientationType);
        poFeature->SetField(SOURCE_EFFORT_MANTISSA, nSourceEffortMantissa);
        poFeature->SetField(SOURCE_EFFORT_POWER, nSourceEffortPower);
        poFeature->SetField(SOURCE_MEASUREMENT_UNIT, nSourceMeasurementUnit);
        poFeature->SetField(X_COORDINATE, nXCoordinate);
        poFeature->SetField(Y_COORDINATE, nYCoordinate);
    }

    if( nSamples > 0 && padfValues != nullptr )
        poFeature->SetField(poFeature->GetFieldCount() - 1,
                            nSamples, padfValues);

    CPLFree(padfValues);
    return poFeature;
}

static const FieldDesc SEGYHeaderFields[] =
{
    { "TEXT_HEADER", OFTString },
    { "JOB_ID_NUMBER", OFTInteger },
    { "LINE_NUMBER", OFTInteger },
    { "REEL_NUMBER", OFTInteger },
    { "DATA_TRACES_PER_ENSEMBLE", OFTInteger },
    { "AUX_TRACES_PER_ENSEMBLE", OFTInteger },
    { "SAMPLE_INTERVAL", OFTInteger },
    { "SAMPLE_INTERVAL_ORIGINAL", OFTInteger },
    { "SAMPLES_PER_DATA_TRACE", OFTInteger },
    { "SAMPLES_PER_DATA_TRACE_ORIGINAL", OFTInteger },
    { "DATA_SAMPLE_TYPE", OFTInteger },
    { "ENSEMBLE_FOLD", OFTInteger },
    { "TRACE_SORTING_CODE", OFTInteger },
    { "VERTICAL_SUM_CODE", OFTInteger },
    { "SWEEP_FREQUENCY_AT_START", OFTInteger },
    { "SWEEP_FREQUENCY_AT_END", OFTInteger },
    { "SWEEP_LENGTH", OFTInteger },
    { "SWEEP_TYPE", OFTInteger },
    { "TRACE_NUMBER_OF_SWEEP_CHANNEL", OFTInteger },
    { "SWEEP_TRACE_TAPER_LENGTH_AT_START", OFTInteger },
    { "SWEEP_TRACE_TAPER_LENGTH_AT_END", OFTInteger },
    { "TAPER_TYPE", OFTInteger },
    { "CORRELATED", OFTInteger },
    { "BINARY_GAIN_RECOVERED", OFTInteger },
    { "AMPLITUDE_RECOVERY_METHOD", OFTInteger },
    { "MEASUREMENT_SYSTEM", OFTInteger },
    { "IMPULSE_SIGNAL_POLARITY", OFTInteger },
    { "VIBRATORY_POLARY_CODE", OFTInteger },
    { "SEGY_REVISION_NUMBER", OFTInteger },
    { "SEGY_FLOAT_REVISION_NUMBER", OFTReal },
    { "FIXED_LENGTH_TRACE_FLAG", OFTInteger },
    { "NUMBER_OF_EXTENDED_TEXTUAL_FILE_HEADER", OFTInteger },
};

constexpr int HEADER_TEXT_HEADER = 0;
constexpr int HEADER_JOB_ID_NUMBER = 1;
constexpr int HEADER_LINE_NUMBER = 2;
constexpr int HEADER_REEL_NUMBER = 3;
constexpr int HEADER_DATA_TRACES_PER_ENSEMBLE = 4;
constexpr int HEADER_AUX_TRACES_PER_ENSEMBLE = 5;
constexpr int HEADER_SAMPLE_INTERVAL = 6;
constexpr int HEADER_SAMPLE_INTERVAL_ORIGINAL = 7;
constexpr int HEADER_SAMPLES_PER_DATA_TRACE = 8;
constexpr int HEADER_SAMPLES_PER_DATA_TRACE_ORIGINAL = 9;
constexpr int HEADER_DATA_SAMPLE_TYPE = 10;
constexpr int HEADER_ENSEMBLE_FOLD = 11;
constexpr int HEADER_TRACE_SORTING_CODE = 12;
constexpr int HEADER_VERTICAL_SUM_CODE = 13;
constexpr int HEADER_SWEEP_FREQUENCY_AT_START = 14;
constexpr int HEADER_SWEEP_FREQUENCY_AT_END = 15;
constexpr int HEADER_SWEEP_LENGTH = 16;
constexpr int HEADER_SWEEP_TYPE = 17;
constexpr int HEADER_TRACE_NUMBER_OF_SWEEP_CHANNEL = 18;
constexpr int HEADER_SWEEP_TRACE_TAPER_LENGTH_AT_START = 19;
constexpr int HEADER_SWEEP_TRACE_TAPER_LENGTH_AT_END = 20;
constexpr int HEADER_TAPER_TYPE = 21;
constexpr int HEADER_CORRELATED = 22;
constexpr int HEADER_BINARY_GAIN_RECOVERED = 23;
constexpr int HEADER_AMPLITUDE_RECOVERY_METHOD = 24;
constexpr int HEADER_MEASUREMENT_SYSTEM = 25;
constexpr int HEADER_IMPULSE_SIGNAL_POLARITY = 26;
constexpr int HEADER_VIBRATORY_POLARY_CODE = 27;
constexpr int HEADER_SEGY_REVISION_NUMBER = 28;
constexpr int HEADER_FLOAT_SEGY_REVISION_NUMBER = 29;
constexpr int HEADER_FIXED_LENGTH_TRACE_FLAG = 30;
constexpr int HEADER_NUMBER_OF_EXTENDED_TEXTUAL_FILE_HEADER = 31;

/************************************************************************/
/*                         OGRSEGYHeaderLayer()                         */
/************************************************************************/

OGRSEGYHeaderLayer::OGRSEGYHeaderLayer( const char* pszLayerName,
                                        SEGYBinaryFileHeader* psBFH,
                                        const char* pszHeaderTextIn ) :
    poFeatureDefn(new OGRFeatureDefn(pszLayerName)),
    bEOF(false),
    pszHeaderText(CPLStrdup(pszHeaderTextIn))
{
    memcpy(&sBFH, psBFH, sizeof(sBFH));

    SetDescription( poFeatureDefn->GetName() );
    poFeatureDefn->Reference();
    poFeatureDefn->SetGeomType( wkbNone );

    for( int i = 0;
         i < static_cast<int>(sizeof(SEGYHeaderFields)/
                              sizeof(SEGYHeaderFields[0]));
         i++ )
    {
        OGRFieldDefn oField( SEGYHeaderFields[i].pszName,
                             SEGYHeaderFields[i].eType );
        poFeatureDefn->AddFieldDefn( &oField );
    }
}

/************************************************************************/
/*                            ~OGRSEGYLayer()                          */
/************************************************************************/

OGRSEGYHeaderLayer::~OGRSEGYHeaderLayer()

{
    poFeatureDefn->Release();
    CPLFree(pszHeaderText);
}

/************************************************************************/
/*                            ResetReading()                            */
/************************************************************************/

void OGRSEGYHeaderLayer::ResetReading()

{
    bEOF = false;
}

/************************************************************************/
/*                           GetNextFeature()                           */
/************************************************************************/

OGRFeature *OGRSEGYHeaderLayer::GetNextFeature()
{
    while( true )
    {
        OGRFeature *poFeature = GetNextRawFeature();
        if( poFeature == nullptr )
            return nullptr;

        if( (m_poFilterGeom == nullptr
             || FilterGeometry( poFeature->GetGeometryRef() ) )
            && (m_poAttrQuery == nullptr
                || m_poAttrQuery->Evaluate( poFeature )) )
        {
            return poFeature;
        }
        else
        {
            delete poFeature;
        }
    }
}

/************************************************************************/
/*                         GetNextRawFeature()                          */
/************************************************************************/

OGRFeature *OGRSEGYHeaderLayer::GetNextRawFeature()
{
    if( bEOF )
        return nullptr;

    bEOF = true;

    OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
    poFeature->SetFID(0);

    poFeature->SetField(HEADER_TEXT_HEADER, pszHeaderText);
    poFeature->SetField(HEADER_JOB_ID_NUMBER, sBFH.nJobIdNumber);
    poFeature->SetField(HEADER_LINE_NUMBER, sBFH.nLineNumber);
    poFeature->SetField(HEADER_REEL_NUMBER, sBFH.nReelNumber);
    poFeature->SetField(HEADER_DATA_TRACES_PER_ENSEMBLE, sBFH.nDataTracesPerEnsemble);
    poFeature->SetField(HEADER_AUX_TRACES_PER_ENSEMBLE, sBFH.nAuxTracesPerEnsemble);
    poFeature->SetField(HEADER_SAMPLE_INTERVAL, sBFH.nSampleInterval);
    poFeature->SetField(HEADER_SAMPLE_INTERVAL_ORIGINAL, sBFH.nSampleIntervalOriginal);
    poFeature->SetField(HEADER_SAMPLES_PER_DATA_TRACE, sBFH.nSamplesPerDataTrace);
    poFeature->SetField(HEADER_SAMPLES_PER_DATA_TRACE_ORIGINAL, sBFH.nSamplesPerDataTraceOriginal);
    poFeature->SetField(HEADER_DATA_SAMPLE_TYPE, sBFH.nDataSampleType);
    poFeature->SetField(HEADER_ENSEMBLE_FOLD, sBFH.nEnsembleFold);
    poFeature->SetField(HEADER_TRACE_SORTING_CODE, sBFH.nTraceSortingCode);
    poFeature->SetField(HEADER_VERTICAL_SUM_CODE, sBFH.nVerticalSumCode);
    poFeature->SetField(HEADER_SWEEP_FREQUENCY_AT_START, sBFH.nSweepFrequencyAtStart);
    poFeature->SetField(HEADER_SWEEP_FREQUENCY_AT_END, sBFH.nSweepFrequencyAtEnd);
    poFeature->SetField(HEADER_SWEEP_LENGTH, sBFH.nSweepLength);
    poFeature->SetField(HEADER_SWEEP_TYPE, sBFH.nSweepType);
    poFeature->SetField(HEADER_TRACE_NUMBER_OF_SWEEP_CHANNEL, sBFH.nTraceNumberOfSweepChannel);
    poFeature->SetField(HEADER_SWEEP_TRACE_TAPER_LENGTH_AT_START, sBFH.nSweepTraceTaperLengthAtStart);
    poFeature->SetField(HEADER_SWEEP_TRACE_TAPER_LENGTH_AT_END, sBFH.nSweepTraceTaperLengthAtEnd);
    poFeature->SetField(HEADER_TAPER_TYPE, sBFH.nTaperType);
    poFeature->SetField(HEADER_CORRELATED, sBFH.nCorrelated);
    poFeature->SetField(HEADER_BINARY_GAIN_RECOVERED, sBFH.nBinaryGainRecovered);
    poFeature->SetField(HEADER_AMPLITUDE_RECOVERY_METHOD, sBFH.nAmplitudeRecoveryMethod);
    poFeature->SetField(HEADER_MEASUREMENT_SYSTEM, sBFH.nMeasurementSystem);
    poFeature->SetField(HEADER_IMPULSE_SIGNAL_POLARITY, sBFH.nImpulseSignalPolarity);
    poFeature->SetField(HEADER_VIBRATORY_POLARY_CODE, sBFH.nVibratoryPolaryCode);
    poFeature->SetField(HEADER_SEGY_REVISION_NUMBER, sBFH.nSEGYRevisionNumber);
    poFeature->SetField(HEADER_FLOAT_SEGY_REVISION_NUMBER, sBFH.dfSEGYRevisionNumber);
    poFeature->SetField(HEADER_FIXED_LENGTH_TRACE_FLAG, sBFH.nFixedLengthTraceFlag);
    poFeature->SetField(HEADER_NUMBER_OF_EXTENDED_TEXTUAL_FILE_HEADER, sBFH.nNumberOfExtendedTextualFileHeader);

    return poFeature;
}
