/* ====================================================================
 *  AuAHMF.h
 *  Version: 0.500
 * ==================================================================== */
#ifndef Au_AHMF_H
 #define Au_AHMF_H
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *   AuAHMF maintains the AHM file format and header information
 *
 * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 *  Copyright (C) 1998-2010 AuSIM, Inc.
 *
 *  This document contains UNPUBLISHED PROPRIETARY SOURCE CODE of
 *  AuSIM Engineering Solutions, Palo Alto, CA;
 *  the contents of this file may not be disclosed to third parties,
 *  copied or duplicated in any form, in whole or in part,
 *  without the prior written permission of AuSIM Engineering Solutions.
 *
 *  RESTRICTED RIGHTS LEGEND:
 *  Use, duplication or disclosure by the Government is subject to
 *  restrictions as set forth in subdivision (c)(1)(ii) of the Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7013,
 *  and/or in similar or successor clauses in the FAR, DOD or NASA FAR
 *  Supplements.
 *  Unpublished  -
 *     rights reserved under the Copyright Laws of the United States.
 *
 *  Authors:
 *     William L. Chapin (WLC)
 *     Agnieszka Roginska (ARO)
 * --------------------------------------------------------------------
 * Modification log:
 *  27Oct04 WLC  0.500   PCA support
 *  19Dec00 WLC  0.301   Completed redesign
 *  18Dec00 WLC  0.300   Redesigned for AHM v1.410, made public
 *  05Dec00 ARO  0.200   Revamped for AHM v1.4
 *  04Jan98 WlC  0.100   Created.
 * --------------------------------------------------------------------
 * Keywords: Acoustic Head Map, file format, filter, HRTF
 * ==================================================================== */

#ifndef Au_AHM_HDR_H
 #include "AuAHMhdr.h"
#endif


struct AuAHMFSizes;
#ifndef AuCORE_AHM_ERRORMGR_H
//#include "AHMErrorCodes.h"
#endif
class AHMErrorMgr;
enum AHMerrCode;

const float SpeedOfSound20CSealevel_mps = 343.26f;

// helper object for sequence fields
class AuSeqField : public ah_sequence_field {
  public:
    AuSeqField();
    AuSeqField(const AuSeqField &sf);
    AuSeqField(const ah_sequence_field &sf);
    AuSeqField &operator=(const AuSeqField &sf);
    AuSeqField &operator=(const ah_sequence_field &sf);
    bool operator==(const AuSeqField &sf);
    bool operator==(const ah_sequence_field &sf);
    bool IsConsistent(const AuSeqField &sf);
    bool IsConsistent(const ah_sequence_field &sf);
    void Set(const char flg, const char f0=0, const char f1=0, const char f2=0,
             const char f3=0, const char f4=0, const char f5=0, const char f6=0);
};


class AuAHMF {
  protected:
    aAHMF_th   txt_header;
    aAHMF_bh   bin_header;
    char      *p_asc_header;
    char      *p_data_buffer;
    size_t     l_data_bufsiz;
    size_t     l_actual_filesiz;  // bin_header.totlSiz is computed
    char       c_type_by_extension;

  public:
    AuAHMF(AFMfileType typ=AFMfileAHM);
    AuAHMF(const AuAHMF &ahmf);
    AuAHMF(const char *ahmfilename, AFMfileType typ=AFMfileAHM);
    virtual ~AuAHMF() { delete_asc_header(); delete_data_buffer(); }

    bool  Read(const char *ahmfilename);
    bool Write(const char *ahmfilename) const;
    bool  ReadUsingFILEptr(void *fileptr, AHMErrorMgr &emgr);  // lower level Read for file already open with *FILE handle

    float          Version() const { return decypher_version(txt_header.version); }  // cannot set the version
    const char *   VerText() const { return txt_header.version; }
    const char *    HeadID() const { return txt_header.head_id; }
    const char *   Comment() const { return txt_header.comment; }
    int     GetASCIIHeader(char *buffer, int bufsiz) const;
    const aAHMF_bh &GetBinHeader() const { return bin_header; }

    int           FileType() const { return bin_header.fileTyp; }
    size_t        FileSize() const { return bin_header.totlSiz; }  // only valid after ComputeTotals()
    size_t    TimeStampUTC() const { return bin_header.lotmDat; }
    bool      TimeStampUTC(long &lo_dword, long &hi_dword) const;
    bool      TimeStampUTC(int &year, int &month, int &day, int &hours, int &minutes, int &seconds) const;
    int      TimeEpochYear() const { return ((bin_header.hitmDat>>16)&0x0FFF); }
    int     TimeEpochHours() const { return (bin_header.hitmDat&0x0FFFF); }

    float       SampleRate() const { return  bin_header.smplRat; }  // sample rate in Hertz
    float         HeadSize() const { return  bin_header.headSiz; }  // interaural distance in range units
    float   FixedPointConv() const { return  bin_header.fixpCvt; }  // fixed point coefficient conversion value
    float   BassExtendFreq() const { return  bin_header.bassFrq; }  // bass extension low-pass cut-off frequency
    float TrebleExtendFreq() const { return  bin_header.trebFrq; }  // treble extension high-pass cut-off frequency
    float  FreqCutoffSlope() const { return  bin_header.cutoSlp; }  // frequency cut-off slope
    float     SpeedOfSound() const { return  bin_header.spdoSnd; }  // speed of sound at which measurements were taken

    size_t  TotalResponses() const { return  bin_header.totlRsp; }  // total responses stored (including caps)
    int      AzimResponses() const { return  bin_header.azimRsp; }  // grid azimuth responses
    int      ElevResponses() const { return  bin_header.elevRsp; }  // grid elevation responses
    int     RangeResponses() const { return  bin_header.rangRsp; }  // grid range responses
    int    MeasuredFilters() const { return  IsEQFType() ? bin_header.elevRsp : 1; }  // for EQF only!

    size_t    ResponseTaps() const { return CoefsPerFunc(); }  // Returns the total number of coefficients
    size_t  FilterSections() const;
    size_t    CoefsPerFunc() const;  // total coefficients per response function (all sections together)
    size_t CoefsPerSection() const;  // total coefficients per section
    int   ResponseTapZeros() const { return  bin_header.rtapZrs; }  // response tap zero coefficients
    int   ResponseTapPoles() const { return  bin_header.rtapPls; }  // response tap pole coefficients
    int   ZeroCoefficients() const;  // numerator coefficients
    int   PoleCoefficients() const { return  bin_header.rtapPls; }  // denominator coefficients
    int      StagesPerFunc() const;
    int ResponseComponents() const { return  bin_header.respCmp; }  // response components (number of weights if PCA)
// Must reinterpret this method, search all of AuSIMDev
    int      OptimizedTaps() const { return  exist_fld_in_seq(bin_header.bnsdSeq,AHMF_WEIGHTING) ? bin_header.respTps : bin_header.optmTps; }  // compression optimization size
    int      WeightingTaps() const { return  exist_fld_in_seq(bin_header.bnsdSeq,AHMF_WEIGHTING) ? bin_header.optmTps : 0; }  // weighting function size
    int          SinkCount() const { return  bin_header.sinkCnt; }  // 0 == 2 for backward compatibility
    int   VirtualNodeCount() const { return  bin_header.virtNod; }  // number of virtual nodes (affects sink vector, labels, triangles)
    int   VirtualSinkCount() const { return  bin_header.sinkCnt + bin_header.virtNod; }  // number of virtual sinks (affects sink vector, labels, triangles)
    int      TriangleCount() const { return  bin_header.triaCnt; }  // length of sink vector triangle list

    int    CoefficientBits() const { return  bin_header.coefSiz; }  // bits per coefficient
    int      TimeDelayBits() const { return  bin_header.iatdSiz; }  // bits per delay
    int         WeightBits() const { return  bin_header.wghtSiz; }  // bits per weight
    int    NormalLevelBits() const { return  bin_header.nlvlSiz; }  // bits per normal level

    int    CoefficientDataType() const { return  bin_header.coefDat; }  // DataType per coefficient
    int      TimeDelayDataType() const { return  bin_header.iatdDat; }  // DataType per delay
    int         WeightDataType() const { return  bin_header.wghtDat; }  // DataType per weight
    int    NormalLevelDataType() const { return  bin_header.nlvlDat; }  // DataType per normal level

    float       AzimOrigin() const { return  bin_header.azimOrg; }  // primary axis,   see AHMaxisConfig
    float     AzimInterval() const { return  bin_header.azimInt; }  // primary axis,   see AHMaxisConfig
    float       ElevOrigin() const { return  bin_header.elevOrg; }  // secondary axis, see AHMaxisConfig
    float     ElevInterval() const { return  bin_header.elevInt; }  // secondary axis, see AHMaxisConfig
    float      RangeOrigin() const { return  bin_header.rangOrg; }  // tertiary axis,  see AHMaxisConfig
    float    RangeInterval() const { return  bin_header.rangInt; }  // tertiary axis,  see AHMaxisConfig

    int  AxisConfiguration() const { return bin_header.axisCfg; }  // axis order and type, see AHMaxesConfig
    int         WindowType() const { return bin_header.wndoTyp; }  // filter window type,  see AHMwindowType enum
    int      DisplayEQType() const { return bin_header.dseqTyp; }  // display EQ type,     see AHMdisplayEQType enum
    int        FieldEQType() const { return bin_header.fdeqTyp; }  // field EQ method,     see AHMfieldEQType enum
    int        LevelEQType() const { return bin_header.lveqTyp; }  // level EQ method,     see AHMlevelEQType enum
    int     EncryptionType() const { return bin_header.crptTyp; }  // encryption type,     see AHMencryptType enum
    int     EnhanceMethods() const { return bin_header.enhcMtd; }  // enhancement method,  see AHMenhanceMethod OR bits
    int  NormalizationType() const { return bin_header.nrmlTyp; }  // normalization type (peak/RMS), see AHMnormalizationType enum
    int     LeftRespSource() const { return bin_header.leftSrc; }  // left responses,      see AHMrespSource enum
    int    RightRespSource() const { return bin_header.rghtSrc; }  // right responses,     see AHMrespSource enum
    int     NorthCapSource() const { return bin_header.NcapSrc; }  // north polarcap,      see AHMrespSource enum
    int     SouthCapSource() const { return bin_header.ScapSrc; }  // south polarcap,      see AHMrespSource enum
    int     TrackingDevice() const { return bin_header.trakDev; }  // head tracking device,see AHMtrackingDevice enum

    int      DistanceUnits() const { return  bin_header.distFmt; }  // dist units,     see AHMscaleUnits enum
    int         AngleUnits() const { return  bin_header.anglFmt; }  // angular units,  see AHMscaleUnits enum
    int          TimeUnits() const { return  bin_header.timeFmt; }  // time units,     see AHMscaleUnits enum
    int   NormalLevelUnits() const { return  bin_header.nlvlFmt; }  // lin/db units,   see AHMscaleUnits enum
    int         AzimIncrOp() const { return  bin_header.azimInc; }  // azim increment, see AHMincrOperation enum
    int         ElevIncrOp() const { return  bin_header.elevInc; }  // elev increment, see AHMincrOperation enum
    int        RangeIncrOp() const { return  bin_header.rangInc; }  // rang increment, see AHMincrOperation enum

    // queries
    bool         IsEQFType() const { return ((bin_header.fileTyp!=AFMfileAHM)&&(bin_header.fileTyp!=AFMfileAFM)); }
    bool  IsDistanceInches() const { return (bin_header.distFmt==AHMdistInches); }
    bool  IsDistanceMeters() const { return (bin_header.distFmt==AHMdistMeters); }
    bool    IsAngleRadians() const { return (bin_header.anglFmt==AHManglRadians); }
    bool    IsAngleDegrees() const { return (bin_header.anglFmt==AHManglDegrees); }
    bool      IsAngleGrads() const { return (bin_header.anglFmt==AHManglGradians); }
    bool     IsTimeSamples() const { return (bin_header.timeFmt==AHMtimeSamples); }
    bool     IsTimeSeconds() const { return (bin_header.timeFmt==AHMtimeSeconds); }
    bool   IsTimeMillisecs() const { return (bin_header.timeFmt==AHMtimeMSecs); }
    bool   IsTimeMicrosecs() const { return (bin_header.timeFmt==AHMtimeUSecs); }
    bool       IsEncrypted() const { return (bin_header.crptTyp!=0); }
    bool     IsBassBoosted() const { return ((bin_header.enhcMtd&AHM_ENH_BassExt)!=0); }
    bool        IsSmoothed() const { return ((bin_header.enhcMtd&AHM_ENH_CritBand)!=0); }
    bool       IsCoefFloat() const { return (bin_header.coefDat==AHMdataFloat); }
    bool     IsWeightFloat() const { return (bin_header.wghtDat==AHMdataFloat); }
    bool      IsDelayFloat() const { return (bin_header.iatdDat==AHMdataFloat); }
    bool      IsLevelFloat() const { return (bin_header.nlvlDat==AHMdataFloat); }
    bool     ExistNorthCap() const { return (bin_header.NcapSrc!=0); }
    bool     ExistSouthCap() const { return (bin_header.ScapSrc!=0); }
    bool  IsCommentFlagged() const { return (bin_header.readCmt!=0); }
    bool         IsTracked() const { return (bin_header.trakDev!=0); }
    bool IsDelayInteraural() const { return  exist_fld_in_seq(bin_header.respSeq,AHMF_IATD); }
    bool IsLevelInteraural() const { return  exist_fld_in_seq(bin_header.respSeq,AHMF_IALD); }
    int   ExistASCIIHeader() const { return  bin_header.thisMap; };  // returns number of bytes in ASCII allocation
    bool     ExistRespData() const;  // basis components are appended
    bool     ExistTimeData() const;  // basis components are appended
    bool    ExistLevelData() const;  // basis components are appended
    int    ExistBasisFuncs() const;  // basis components are appended
    bool  ExistRespVectors() const;  // map of vectors to each response are appended
    bool    ExistTrackData() const;  // map of tracking error for each response
    bool ExistTrackErrData() const;  // map of tracking error for each response resolved to 3DOF
    bool  ExistSinkVectors() const;  // table of vectors for each sink
    bool ExistSinkBearings() const;  // table of bearings for each sink
    bool   ExistSinkLabels() const;  // table of vectors for each sink
    bool    ExistWeighting() const;  // an EQ/normalization weighting filter of WeightingTaps() tap length
    bool ExistSinkTriangles() const;  // table of triples connecting sinks/virtual nodes
    bool ExistSinkChannelMap() const;  // list of channel for each sink
    bool IsAHM_1_43_RspSeq() const;  // formerly ahm_1_43_type()
    bool  IsSectionVersion() const;
    bool  IsStrictSections() const;
    float    LatestVersion() const;  // returns most current version, based on filetype
    bool     ComputeTotals(AuAHMFSizes &sd) const;  // gathers intermediate data
    char ExtensionFileType() const { return c_type_by_extension; }
    bool  ReportInconsistencies(char *report, int report_buf_size) const;
    bool  ReportInconsistencies(char *report, int report_buf_size, int &nerrs, int &errsev) const;
    bool        ErrorCheck(AHMerrCode *error_buffer, int errbufsiz, int &err_count, int &err_sev) const;
    bool        ErrorCheck(AHMErrorMgr &emgr) const;

    // sequences
    AuSeqField     VectorSequence() const { return (bin_header.vectSeq); }
    AuSeqField     RegionSequence() const { return (bin_header.regnSeq); }
    AuSeqField   ResponseSequence() const { return (bin_header.respSeq); }
    AuSeqField    SectionSequence() const { return (bin_header.coefSeq); }
    AuSeqField  BonusDataSequence() const { return (bin_header.bnsdSeq); }
    AuSeqField       CoefSequence() const { return (bin_header.coefSeq); }
    int     VectorSequence(int idx) const { return seq_fld(idx,bin_header.vectSeq); }
    int     RegionSequence(int idx) const { return seq_fld(idx,bin_header.regnSeq); }
    int   ResponseSequence(int idx) const { return seq_fld(idx,bin_header.respSeq); }
    int    SectionSequence(int idx) const { return seq_fld(idx,bin_header.coefSeq); }
    int  BonusDataSequence(int idx) const { return seq_fld(idx,bin_header.bnsdSeq); }
    int       CoefSequence(int idx) const { return seq_fld(idx,bin_header.coefSeq); }
    const int *      VectorSeqPtr() const { return (int*)(&bin_header.vectSeq); }
    const int *      RegionSeqPtr() const { return (int*)(&bin_header.regnSeq); }
    const int *    ResponseSeqPtr() const { return (int*)(&bin_header.respSeq); }
    const int *     SectionSeqPtr() const { return (int*)(&bin_header.coefSeq); }
    const int *   BonusDataSeqPtr() const { return (int*)(&bin_header.bnsdSeq); }
    const int *        CoefSeqPtr() const { return (int*)(&bin_header.coefSeq); }
    static int      FieldIndexInSequence(const AuSeqField &seq, int field, int test=0);
    static bool  AnyMoreFieldsInSequence(const AuSeqField &seq, int idx=0);
    static int     LastNonZeroFieldIndex(const AuSeqField &seq);

    // text header sets
    bool    HeadID(const char *hid);
    bool   Comment(const char *cmt);
    bool   VerText(const char *ver);

    bool      SetTimeStamp();  // current time&date
    bool   SetTimeStampUTC(long lo_dword, long hi_dword);
    bool      SetTimeEpoch(long lo_dword, int epoch_yr_ad, int epoch_hrs);
    bool      SetTimeStamp(int year, int month, int day, int hours, int minutes, int seconds=0, int gmt=0);

    bool     AzimResponses(int  resps) { return set_ushrt(bin_header.azimRsp,resps); }
    bool     ElevResponses(int  resps) { return set_ushrt(bin_header.elevRsp,resps); }
    bool    RangeResponses(int  resps) { return set_ushrt(bin_header.rangRsp,resps); }
    bool      ResponseTaps(size_t  taps);
    bool    FilterSections(size_t  sections);  // RespTps field is now indicates filterSections for IIR
    bool     OptimizedTaps(int   taps) { return exist_fld_in_seq(bin_header.bnsdSeq,AHMF_WEIGHTING) ? false : set_ushrt(bin_header.optmTps,taps); } // zero if un-smoothed
    bool     WeightingTaps(int   taps) { return exist_fld_in_seq(bin_header.bnsdSeq,AHMF_WEIGHTING) ? set_ushrt(bin_header.optmTps,taps) : false; } // zero if un-smoothed
    bool ResponseComponents(int comps) { return set_ushrt(bin_header.respCmp,comps); }
    bool  ResponseTapZeros(int  zeros) { return set_ushrt(bin_header.rtapZrs,zeros); }
    bool  ResponseTapPoles(int  poles) { return set_ushrt(bin_header.rtapPls,poles); }
    bool  ZeroCoefficients(size_t zeros);  // numerator coefficients
    bool  PoleCoefficients(int  poles);    // denominator coefficients
    bool         SinkCount(int  sinks) { return set_ushrt(bin_header.sinkCnt,sinks); }
    bool     TriangleCount(int  trias) { return set_ushrt(bin_header.triaCnt,trias); }
    bool  VirtualNodeCount(int  nodes) { return set_ushrt(bin_header.virtNod,nodes); }

    bool AxisConfiguration(int   type);  // axis order and type, see AHMaxesConfig
    bool        WindowType(int   type);  // filter window type,  see AHMwindowType enum
    bool     DisplayEQType(int   type);  // display EQ type,     see AHMdisplayEQType enum
    bool       FieldEQType(int   type);  // field EQ method,     see AHMfieldEQType enum
    bool       LevelEQType(int   type);  // level EQ method,     see AHMlevelEQType enum
    bool    EncryptionType(int   type);  // encryption type,     see AHMencryptType enum
    bool NormalizationType(int   type);  // normalization type (peak/RMS), see AHMnormalizationType enum
    bool    EnhanceMethods(int   type);  // enhancement method,  see AHMenhanceMethod OR bits
    bool    LeftRespSource(int   type) { return set_rsrc(bin_header.leftSrc,type); } // see AHMrespSource enum
    bool   RightRespSource(int   type) { return set_rsrc(bin_header.rghtSrc,type); } // see AHMrespSource enum
    bool    NorthCapSource(int   type) { return set_rsrc(bin_header.NcapSrc,type); } // see AHMrespSource enum
    bool    SouthCapSource(int   type) { return set_rsrc(bin_header.ScapSrc,type); } // see AHMrespSource enum
    bool    TrackingDevice(int   type);  // head tracking device see AHMrespSource enum
    bool       FlagComment(bool flag=true) { return bin_header.readCmt = flag; }

    bool        SampleRate(float rate);     // assume data resampled properly
    bool      SpeedOfSound(float spd, int dfmt=AHMdistSpecUnits, int tfmt=AHMtimeSpecUnits);   // converts to internal units
    float   FixedPointConv(float conv) { return bin_header.fixpCvt = conv; }
    float   BassExtendFreq(float freq) { return bin_header.bassFrq = freq; }  // bass extension low-pass cut-off frequency
    float TrebleExtendFreq(float freq) { return bin_header.trebFrq = freq; }  // treble extension high-pass cut-off frequency
    float  FreqCutoffSlope(float slope) { return bin_header.cutoSlp = slope; }  // treble extension high-pass cut-off frequency

    float         HeadSize(float size, int units=-1);
    float       AzimOrigin(float orig, int units=-1);
    float       ElevOrigin(float orig, int units=-1);
    float      RangeOrigin(float orig, int units=-1);
    float     AzimInterval(float intv, int units=-1);
    float     ElevInterval(float intv, int units=-1);
    float    RangeInterval(float intv, int units=-1);

    bool     DistanceUnits(int units);  // dist units,     see AHMscaleUnits enum
    bool        AngleUnits(int units);  // angular units,  see AHMscaleUnits enum
    bool         TimeUnits(int units);  // time units,     see AHMscaleUnits enum
    bool  NormalLevelUnits(int units);  // lin/db units,   see AHMscaleUnits enum
    bool   CoefficientData(int type, int bits) { return set_bits(bin_header.coefSiz,type,bits); }
    bool     TimeDelayData(int type, int bits) { return set_bits(bin_header.iatdSiz,type,bits); }
    bool        WeightData(int type, int bits) { return set_bits(bin_header.wghtSiz,type,bits); }
    bool   NormalLevelData(int type, int bits) { return set_bits(bin_header.nlvlSiz,type,bits); }

    int         AzimIncrOp(int type) { return set_op(bin_header.azimInc,type); }
    int         ElevIncrOp(int type) { return set_op(bin_header.elevInc,type); }
    int        RangeIncrOp(int type) { return set_op(bin_header.rangInc,type); }

    bool    VectorSequence(int idx, int fld) { return set_sqfld(bin_header.vectSeq,idx,fld); }
    bool    RegionSequence(int idx, int fld) { return set_sqfld(bin_header.regnSeq,idx,fld); }
    bool  ResponseSequence(int idx, int fld) { return set_sqfld(bin_header.respSeq,idx,fld); }
    bool   SectionSequence(int idx, int fld) { return set_sqfld(bin_header.coefSeq,idx,fld); }
    bool BonusDataSequence(int idx, int fld) { return set_sqfld(bin_header.bnsdSeq,idx,fld); }
    bool      CoefSequence(int idx, int fld) { return set_sqfld(bin_header.coefSeq,idx,fld); }
    bool    VectorSequence(const AuSeqField &sf) { bin_header.vectSeq=sf; return true; }
    bool    RegionSequence(const AuSeqField &sf) { bin_header.regnSeq=sf; return true; }
    bool  ResponseSequence(const AuSeqField &sf) { bin_header.respSeq=sf; return true; }
    bool   SectionSequence(const AuSeqField &sf) { bin_header.coefSeq=sf; return true; }
    bool BonusDataSequence(const AuSeqField &sf) { bin_header.bnsdSeq=sf; return true; }
    bool      CoefSequence(const AuSeqField &sf) { bin_header.coefSeq=sf; return true; }
    static bool SequenceRemoveField(AuSeqField &sf, int idx=-1);
    static bool    SequenceAddField(AuSeqField &sf, int fld, int idx=-1);

    bool      Conform2Spec(AHMErrorMgr &emgr, float ver_target=1.3f);
    bool ConformIA2PerSink(AHMErrorMgr &emgr);
    bool    SetASCIIHeader(const char *ascii);  // sets the ascii header and thisMap offset
    bool     ComputeTotals();  // sets totlSiz and totlRsp from current format settings
    void      ResetDefault(AFMfileType typ=AFMfileAHM) { set_txt_default(typ); set_bin_default(typ); }
    void ExtensionFileType(int type) { c_type_by_extension = char(type&0x007F); }

    static const char *AppendExtension(const char *filename, char *buf, int bufsiz, int type=0);
    static bool       Find(const char *filename, char *fullpathbuf, int pathbufsiz);
    static int FileTypeByExtension(const char *filename);
    static bool GetTypeVerSize(const char *filename, int &type, float &version, size_t &bytes);

    AuAHMF &operator=(const AuAHMF &ahmf);

    // low-level access
    char *        TextData() { return (char*)&txt_header; }
    char *      BinaryData() { return (char*)&bin_header; }
    const char * DataBlock() const { return p_data_buffer; }
    size_t  ActualFileSize() const { return l_actual_filesiz; }  // only good after a Read
    size_t   DataBlockSize() const { return l_data_bufsiz; }  // only valid after Read()

    // helpers - these probably should all be protected methods
    float AnglConversionFactor(int units, bool inv=false) const;
    float DistConversionFactor(int units, bool inv=false) const;
    float TimeConversionFactor(int units, bool inv=false) const;
    double MeterInDistanceUnits() const { return DistInAHMUnits(1); }
    double       PiInAngleUnits() const; // { return AnglInAHMUnits(M_PI); }
    double    SecondInTimeUnits() const { return TimeInAHMUnits(1); }
    double AnglInAHMUnits(double angl, int units=AHManglRadians) const;
    double DistInAHMUnits(double dist, int units=AHMdistMeters) const;
    double TimeInAHMUnits(double tval, int units=AHMtimeSeconds) const;
    double LevelChangeInAHMUnits(double lval, int units=AHMlevelDecibels) const;

    // Sizes
    size_t total_grid_locations() const;
    size_t respsize() const;
    size_t timesize() const;
    size_t levlsize() const;
    size_t compsize() const;
    size_t mapgsize() const;

  private:
    void  set_txt_default(AFMfileType typ/*=AFMfileAHM*/);
    void  set_bin_default(AFMfileType typ/*=AFMfileAHM*/);
    void  delete_asc_header();
    void  delete_data_buffer();
    bool  create_ascii_header();

    bool  set_ushrt(unsigned short &fld, int val);
    bool  set_op   (unsigned char  &fld, int op);
    bool  set_rsrc (unsigned char  &fld, int src);
    bool  set_bits (unsigned char  &fld, int typ, int bts);

    static bool  valid_seq_fld_idx(int idx);
    static int     seq_fld(int idx, const ah_sequence_field &sf);
    static bool  set_sqfld(ah_sequence_field &fld, int idx, int type);
    static int     get_fld_in_seq(const ah_sequence_field &sf, int type, int test=0);
    static bool  exist_fld_in_seq(const ah_sequence_field &sf, int type);
    static bool  exist_fld_range_in_seq(const ah_sequence_field &sf, int min, int max);
    static float decypher_version(const char *str);  // cannot set the version
    static int   decypher_filekey(const char *str);  // cannot set the filekey
};


// struct AuAHMFSizes is used in ComputeTotals
struct AuAHMFSizes
{
  size_t nlocations;    // grid plus caps
  size_t nresponses;    // sinks * nlocations
  size_t sz_header;     // text + binary + extra
  size_t sz_filter;     // taps * (zeros+poles)
  size_t ndelayploc;    // delays per loc
  size_t nlevelploc;    // levels per loc
  size_t nbases;        // total bases in file, each with ncomps
  size_t sz_resps;      // nlocs * nsnks * filt/comp
  size_t sz_delays;     // nlocs * delays per loc
  size_t sz_levels;     // nlocs * levels per loc
  size_t sz_basis;      // ncomp * filt * nbase
  size_t sz_vectors;    // nlocs * (trak+resp+sink)
  size_t sz_labels;     // nsnks * AFMF_SINKLABELSTR_SIZ
  size_t sz_this;       // size of the AuAHMFSizes structure for compatibility
  size_t sz_weighting;  // (zeros+poles) * optmTaps if weighting exists
  size_t sz_triangles;  // 12*triaCnt
  size_t sz_chnlmap;    // 4*nsnks
  // datasegment is sz_resp + sz_delays + sz_levels + sz_basis + sz_vectors
  // total file size adds sz_header
  AuAHMFSizes()
    : nlocations(0)
    , nresponses(0)
    , sz_header(0)
    , sz_filter(0)
    , ndelayploc(0)
    , nlevelploc(0)
    , nbases(0)
    , sz_resps(0)
    , sz_delays(0)
    , sz_levels(0)
    , sz_basis(0)
    , sz_vectors(0)
    , sz_this(sizeof(AuAHMFSizes))
    , sz_weighting(0)
    , sz_triangles(0)
    , sz_chnlmap(0)
  { ; }
};


// ==========================================================================
// ===================== INLINE PUBLIC METHOD IMPLEMENTATION: AuAHMF
// ==========================================================================

// Class: AuAHMF
// Method: constructor
// -------------------
// Creates a new AuAHMF object.  If dflt is true, fill the AHMF with default values.
//
inline
AuAHMF::AuAHMF(AFMfileType typ)
  : p_asc_header(0)
  , p_data_buffer(0)
  , l_data_bufsiz(0)
  , l_actual_filesiz(0)
  , c_type_by_extension(-1)
{
  set_txt_default(typ);
  set_bin_default(typ);
}

// Class: AuAHMF
// Method: constructor
// --------------------
// Copy constructor. Creates a new AuAHMF object from an existing ahmf
//
inline
AuAHMF::AuAHMF(const AuAHMF &ahmf)
  : p_asc_header(0)
  , p_data_buffer(0)
  , l_data_bufsiz(0)
  , l_actual_filesiz(0)
  , c_type_by_extension(-1)
{
  *this = ahmf;
}


// Class: AuAHMF
// Method: constructor
// -------------------
// Creates a new AuAHMF object.  If dflt is true, fill the AHMF with default values.
//
inline
AuAHMF::AuAHMF(const char *ahmfilename,AFMfileType typ)
  : p_asc_header(0)
  , p_data_buffer(0)
  , l_data_bufsiz(0)
  , l_actual_filesiz(0)
  , c_type_by_extension(-1)
{
  set_txt_default(typ);
  set_bin_default(typ);
  Read(ahmfilename);
}


// Class: AuAHMF
// Method: FilterSections()
// -----------------------------------
// Returns the number of filter sections per function/response
//
inline
size_t
AuAHMF::FilterSections() const
{
  // This first condition has no coefficients, despite what respTps may say
  if ( (ResponseTapPoles() == 0) && (ResponseTapZeros() == 0) )
    return 0;
  // This second condition is the legacy FIR case supporting very long FIR
  if ( (ResponseTapPoles() == 0) && (ResponseTapZeros() == 1) )
    return 1;  // number of filter sections per function/response
  // Final condition
  //   Any IIR
  //   Any FIR with rtapZrs > 1 which is cascaded (or maybe parallel but uncommon)
  return  bin_header.respTps;
}


// Class: AuAHMF
// Method: CoefsPerFunc()
// -----------------------------------
// Returns the total number of coefficients including all filter sections
// in the response.
//
inline
size_t
AuAHMF::CoefsPerFunc() const
{
  return FilterSections()*(ZeroCoefficients() + PoleCoefficients());
  // total number of coefs per response
}


// Class: AuAHMF
// Method: StagesPerFunc()
// -----------------------------------
// Returns the total number of stages of the filter
inline
int
AuAHMF::StagesPerFunc() const
{
  const AuSeqField &sseq = SectionSequence();
  int nstages = LastNonZeroFieldIndex(sseq) + 1;
  return ( (nstages < 1) ? 0 : (sseq.flg == 0) ? 1 : nstages);
}


// Class: AuAHMF
// Method: CoefsPerSection()
// -----------------------------------
// Returns the number of coefficients per filter section
// in the response filter.
//
inline
size_t
AuAHMF::CoefsPerSection() const
{
  return (ZeroCoefficients() + PoleCoefficients());
  // total number of coefs per filter section
}


inline
int
AuAHMF::ZeroCoefficients() const
{
  // legacy case
  if ( (bin_header.rtapZrs <= 1) && (bin_header.rtapPls == 0) )
    return bin_header.respTps;
  return bin_header.rtapZrs;
}


// Class: AuAHMF
// Method: SampleRate(sample_rate)
// -----------------------------------
// Sets the sampling rate (between 11025 and 100,000Hz)
//
inline
bool
AuAHMF::SampleRate(float rate)     // assume data resampled properly
{
  if ( (rate < 4000.0) || (rate > 192000.0) )
    return false;
  bin_header.smplRat = rate;
  return true;
}

// Class: AuAHMF
// Method: SpeedOfSound(spd,dfmt,tfmt)
// -----------------------------------
// Sets the speed of sound at time of sampling, converts to AHM units
//
inline
bool
AuAHMF::SpeedOfSound(float spd, int dfmt, int tfmt)
{
  bin_header.spdoSnd = spd * DistConversionFactor(dfmt) / TimeConversionFactor(tfmt);
  return true;
}


// Class: AuAHMF
// Method: ResponseTaps(num_taps)
// -----------------------------------
// Sets the number of taps (coefficients) in an FIR filter.
// Has no effect on IIR filters (returns false)
//
inline
bool
AuAHMF::ResponseTaps(size_t taps)
{
  // do nothing for IIR filters
  if (bin_header.rtapPls > 0)
    return false;

  // if there are already multiple sections of FIR,
  // then must use more specific methods of FilterSections() and ZeroCoefficients()
  if ( (bin_header.respTps > 1) && (bin_header.rtapZrs > 1) )
    return false;

  // make sure not a super long FIR filter (greater than 31 bits)
  // super long FIR must use multiple sections
  if ((unsigned __int64)taps > 0x7FFFFFFF)
    return false;

  // if already using legacy method, then continue
  if (bin_header.rtapZrs == 1) {
    bin_header.respTps = (unsigned long)taps;
    return true;
  }

  // if number of taps > 15-bits [32767 (2^15 - 1)],
  //   then must use legacy method
  if ((unsigned long)taps > 0x00007FFF) {
    bin_header.rtapZrs = 1;  // one coef per tap
    bin_header.respTps = (unsigned long)taps;
    return true;
  }

  // finally use single section, with taps stored in zero coefs
  bin_header.respTps = 1;   // one section
  bin_header.rtapZrs = (unsigned short)taps;
  return true;
}


// Class: AuAHMF
// Method: FilterSections(num_sections)
// -----------------------------------
// Sets the number of sections in the response filter.
//
inline
bool
AuAHMF::FilterSections(size_t  sections)  // RespTps field is now indicates filterSections for IIR
{
  // Cannot set FilterSections on legacy FIR format
  if ( (bin_header.rtapZrs == 1) && (bin_header.rtapPls == 0) )
    return false;

  // Must set PoleCoefficients and ZeroCoefficients appropriately prior to FilterSections
  if ( (sections >= 1) && (bin_header.rtapZrs == 0) && (bin_header.rtapPls == 0))
    return false;
  if ( (sections == 0) && ( (bin_header.rtapZrs >  0) || (bin_header.rtapPls >  0) ) )
    return false;

  // save filter sections value in the old response taps structure field
  bin_header.respTps = (unsigned long)sections;
  return true;
}

// numerator coefficients
inline
bool
AuAHMF::ZeroCoefficients(size_t zeros)
{
  if ((unsigned __int64)zeros > 0x7FFFFFFF)
    return false;

  // if number of taps > 15-bits [32767 (2^15 - 1)],
  //   then must use legacy method
  if ((unsigned long)zeros > 0x00007FFF)
    return ResponseTaps(zeros);

  bin_header.rtapZrs = (unsigned short)zeros;
  return true;
}


// denominator coefficients
inline
bool
AuAHMF::PoleCoefficients(int  poles)
{
  if ( (bin_header.rtapZrs == 1) && (bin_header.respTps > 0x07FFF) )
    return false;   // big FIR filter
  return set_ushrt(bin_header.rtapPls,poles);
}


// Class: AuAHMF
// Method: AxisConfiguration(type)
// ------------------------
inline
bool
AuAHMF::AxisConfiguration(int type)  // axis order and type, see AHMaxesConfig
{
  switch (type) {
    case AHMaxesDefault:    // corrects for an early default error, equates to Spherical_ZYR
    case AHMaxesSpherical_ZYR:    // CRE/UWMadison default
    case AHMaxesSpherical_YXR:    // UCDavis default
    case AHMaxesSpherical_XZR:
    // for cylindrical: first axis = azim, height = elev
    case AHMaxesCylindrical_ZHR:
    case AHMaxesCylindrical_YHR:
    case AHMaxesCylindrical_XHR:
    // for cartesian: azim = Y, elev = Z, rang = X
    case AHMaxesCartesian_YZX:
    case AHMaxesSpherical_RYZ:  // special format with range first, elevation second, azimuth last
      bin_header.axisCfg = (unsigned char)type;
      return true;
  }
  return false;
}


// Class: AuAHMF
// Method: WindowType(type)
// ------------------------
// Sets the window type.  "type" must be one of the WindowType enum.
// WindowType enum is defined in AuAHMhdr.h
//
inline
bool
AuAHMF::WindowType(int  type)
{
  if ( (type < AHMwndoNone) || (type >= AHMwndoLast) )
    return false;
  bin_header.wndoTyp = (unsigned char)type;
  return true;
}

// Class: AuAHMF
// Method: DisplayEQType
// ----------------------
// Sets the equalization type.  "type" must be one of the AHMdisplayEQType enum
// AHMdisplayEQType enum is defined in AuAHMhdr.h
//
inline
bool
AuAHMF::DisplayEQType(int  type)
{
  if ( (type < AHMdseqNone) || (type >= AHMdseqLast) )
    return false;
  bin_header.dseqTyp = (unsigned char)type;
  return true;
}

// Class: AuAHMF
// Method: FieldEQType(type)
// ------------------------
inline
bool
AuAHMF::FieldEQType(int type)  // field EQ method,     see AHMfieldEQType enum
{
  if ( (type < AHMfdeqNone) || (type >= AHMfdeqLast) )
    return false;
  bin_header.fdeqTyp = (unsigned char)type;
  return true;
}

// Class: AuAHMF
// Method: LevelEQType(type)
// ------------------------
inline
bool
AuAHMF::LevelEQType(int type)  // field EQ method,     see AHMfieldEQType enum
{
  if ( (type < AHMlveqNone) || (type >= AHMlveqLast) )
    return false;
  bin_header.lveqTyp = (unsigned char)type;
  return true;
}

// Class: AuAHMF
// Method: EncryptionType(type)
// ------------------------
inline
bool
AuAHMF::EncryptionType(int type)  // encryption type,     see AHMencryptType enum
{
  if ( (type < AHMcryptNone) || (type >= AHMcryptLast) )
    return false;
  bin_header.crptTyp = (unsigned char)type;
  return true;
}

// Class: AuAHMF
// Method: NormalizationType(type)
// ------------------------
inline
bool
AuAHMF::NormalizationType(int type)  // Normalization type,     see AHMnormalizationType enum
{
  if ( (type < AHMnlvlNone) || (type >= AHMnlvlLast) )
    return false;
  bin_header.nrmlTyp = (unsigned char)type;
  return true;
}


// Class: AuAHMF
// Method: EnhanceMethods(type)
// ------------------------
inline
bool
AuAHMF::EnhanceMethods(int type)  // enhancement method,  see AHMenhanceMethod OR bits
{
  if ( (type < 0) || (type > 0x0FF) )
    return false;
  bin_header.enhcMtd = (unsigned char)type;
  return true;
}

// Class: AuAHMF
// Method: TimeUnits(units)
// ------------------------
// Change the current units of time measurement.
inline
bool
AuAHMF::TimeUnits(int units)
{
  switch (units) {
    case AHMtimeSamples:
    case AHMtimeSeconds:
    case AHMtimeMSecs:
    case AHMtimeUSecs:
      bin_header.timeFmt = (unsigned char)units;
      return true;
  }
  return false;
}

// Class: AuAHMF
// Method: NormalLevelUnits(units)
// ------------------------
// Change the current units of normalization level.
inline
bool
AuAHMF::NormalLevelUnits(int units)
{
  switch (units) {
    case AHMlevelLinear:
    case AHMlevelDecibels:
      bin_header.nlvlFmt = (unsigned char)units;
      return true;
  }
  return false;
}

// Class: AuAHMF
// Method: TrackingDevice(type)
// ------------------------
inline
bool
AuAHMF::TrackingDevice(int type)  // tracking device,  see AHMtrackingDevice
{
  if ( (type < AHMtrakNone) || (type > AHMtrakLast) )
    return false;
  bin_header.trakDev = (unsigned char)type;
  return true;
}

inline
bool
AuAHMF::ExistRespData() const
{
  if (!bin_header.respTps || !bin_header.totlRsp) return false;
  if (!total_grid_locations()) return false;
  if (exist_fld_in_seq(bin_header.respSeq,AHMF_RESP))  return true;
  if (exist_fld_range_in_seq(bin_header.respSeq,AHMF_LEFT,AHMF_RGHT))  return true;
  if (exist_fld_range_in_seq(bin_header.respSeq,EQRS_SCMP,EQRS_RAWD))  return true;
  return false;
}

inline
bool
AuAHMF::ExistTimeData() const
{
  if (!total_grid_locations()) return false;
  if (exist_fld_in_seq(bin_header.respSeq,AHMF_TIME))  return true;
  if (exist_fld_range_in_seq(bin_header.respSeq,AHMF_LATD,AHMF_RATD))  return true;
  if (exist_fld_in_seq(bin_header.respSeq,AHMF_IATD))  return true;
  return false;
}

inline
bool
AuAHMF::ExistLevelData() const
{
  if (!total_grid_locations()) return false;
  if (exist_fld_in_seq(bin_header.respSeq,AHMF_LEVL))  return true;
  if (exist_fld_range_in_seq(bin_header.respSeq,AHMF_LALO,AHMF_RALO))  return true;
  if (exist_fld_in_seq(bin_header.respSeq,AHMF_IALD))  return true;
  return false;
}

inline
int
AuAHMF::ExistBasisFuncs() const
{
  int nbases = 0;
  if (exist_fld_in_seq(bin_header.regnSeq,AHMF_BASISLEFT))
    nbases++;
  if (exist_fld_in_seq(bin_header.regnSeq,AHMF_BASISRGHT))
    nbases++;
  if (exist_fld_in_seq(bin_header.regnSeq,AHMF_BASIS))
    nbases++;
  if (exist_fld_in_seq(bin_header.regnSeq,AHMF_BASISSINK))
    nbases += bin_header.sinkCnt;
  return nbases;
}


inline
bool
AuAHMF::ExistRespVectors() const
{
  return ( ( (bin_header.azimInc == AHMincrNoGrid)  // azim increment (add/multiply/log)
          || (bin_header.elevInc == AHMincrNoGrid)  // elev increment (add/multiply/log)
          || (bin_header.rangInc == AHMincrNoGrid)  // rang increment (add/multiply/log)
          ) &&
             (FieldIndexInSequence(RegionSequence(), AHMF_RESPVECT) > -1)
        );
}


inline
bool
AuAHMF::ExistTrackData() const
{
  return (FieldIndexInSequence(RegionSequence(), AHMF_TRAKDATA) > -1);
}


inline
bool
AuAHMF::ExistTrackErrData() const
{
  return (FieldIndexInSequence(RegionSequence(), AHMF_TRAKDERR) > -1);
}


inline
bool
AuAHMF::ExistSinkVectors() const
{
  return (bin_header.sinkCnt > 0)
      && ((FieldIndexInSequence(RegionSequence(), AHMF_SINKVECT) > -1)
       || (FieldIndexInSequence(BonusDataSequence(), AHMF_SINKVECT) > -1));
}

inline
bool
AuAHMF::ExistSinkBearings() const
{
  return (bin_header.sinkCnt > 0)
      && (FieldIndexInSequence(BonusDataSequence(), AHMF_SINKVECTBRG) > -1);
}

inline
bool
AuAHMF::ExistSinkLabels() const
{
  return (bin_header.sinkCnt > 0)
      && ((FieldIndexInSequence(RegionSequence(), AHMF_SINKLABEL) > -1)
       || (FieldIndexInSequence(BonusDataSequence(), AHMF_SINKLABEL) > -1));
}

// list of channel for each sink
inline
bool
AuAHMF::ExistSinkChannelMap() const
{
  return (bin_header.sinkCnt > 0)
      && ((FieldIndexInSequence(RegionSequence(), AHMF_SINKCHNLMAP) > -1)
       || (FieldIndexInSequence(BonusDataSequence(), AHMF_SINKCHNLMAP) > -1));
}

// table of triples connecting sinks/virtual nodes
inline
bool
AuAHMF::ExistSinkTriangles() const
{
  return (bin_header.triaCnt > 0)
      && ((FieldIndexInSequence(RegionSequence(), AHMF_SINKTRIANGLES) > -1)
       || (FieldIndexInSequence(BonusDataSequence(), AHMF_SINKTRIANGLES) > -1));
}

// an EQ/normalization weighting filter of WeightingTaps() tap length
inline
bool
AuAHMF::ExistWeighting() const
{
  return (bin_header.optmTps > 0)
      && ((FieldIndexInSequence(RegionSequence(), AHMF_WEIGHTING) > -1)
       || (FieldIndexInSequence(BonusDataSequence(), AHMF_WEIGHTING) > -1));
}

// Class: AuAHMF
// Method: ahm_1_43_type()   (formerly ahm_1_43_type())
// Used to check if it is an 1.43 type AHM file
// ---------------------------------
inline
bool
AuAHMF::IsAHM_1_43_RspSeq() const
{
  ah_sequence_field rseq = { { AHMF_RESP, AHMF_TIME, AHMF_LEVL, 0, 0, 0, 0 }, AHMF_INTERSECT };
  AuSeqField respSeq(rseq);
  if (ResponseSequence() == respSeq)
    return true;
  return false;
}

// Class: AuAHMF
// Method: IsSectionVersion()
// ---------------------------------
inline
bool
AuAHMF::IsSectionVersion() const
{
  // sectioned AHM's are v1.5+
  if (bin_header.fileTyp == AFMfileAHM) return (Version() >= 1.5f);
  // sectioned AFM's and EQF's are v1.2+
  return (Version() >= 1.2f);
}

// Class: AuAHMF
// Method: IsSectionVersion()
// ---------------------------------
inline
bool
AuAHMF::IsStrictSections() const
{
  // do not tolerate deprecated coefSeq
  if (!bin_header.coefSeq.fld[0] || !bin_header.coefSeq.flg) 
    return false;
  // do not tolerate FIR's specifed in respTps
  // even very long FIR's must be sectioned
  if ( (bin_header.respTps > 1) 
    && (bin_header.rtapPls == 0) 
    && ( (bin_header.rtapZrs == 0) 
      || (bin_header.rtapZrs == 1) ) ) 
    return false;

  return true;
}


// Class: AuAHMF
// Method: LatestVersion()
// ---------------------------------
// returns most current version, based on filetype
inline
float    
AuAHMF::LatestVersion() const  
{
  switch (bin_header.fileTyp) {
    case AFMfileAHM: return float(AHM_VERSION);
    case AFMfileAFM: return float(AFM_VERSION);
    case AFMfileEQF: return float(EQF_VERSION);
  }
  return float(AHM_VERSION);
}


inline
double
AuAHMF::AnglInAHMUnits(double angl, int units/*=AHManglRadians*/) const
{
  return angl*AnglConversionFactor(units);
}

inline
double
AuAHMF::DistInAHMUnits(double dist, int units/*=AHMdistMeters*/) const
{
  return dist*DistConversionFactor(units);
}

inline
double
AuAHMF::TimeInAHMUnits(double tval, int units/*=AHMtimeSeconds*/) const
{
  return tval*TimeConversionFactor(units);
}


// ==========================================================================
// ===================== INLINE PRIVATE METHOD IMPLEMENTATION: AuAHMF
// ==========================================================================

// Class: AuAHMF
// Method: delete_header()
// ------------------------
// Frees up the memory allocated to hold the ascii header.
//
inline
void
AuAHMF::delete_asc_header()
{
  if (p_asc_header) {
    delete p_asc_header;
    p_asc_header = 0;
  }
}


// Class: AuAHMF
// Method: delete_data_buffer()
// ------------------------
// Frees up the memory allocated to hold the data.
//
inline
void
AuAHMF::delete_data_buffer()
{
  if (p_data_buffer || l_data_bufsiz) {
    delete p_data_buffer;
    p_data_buffer = 0;
    l_data_bufsiz = 0;
  }
}


// Class: AuAHMF
// Method: valid_seq_fld_idx(index)
// -------------------------
// Returns TRUE if index is less than the maximum allowed seqfield.
//
inline
bool
AuAHMF::valid_seq_fld_idx(int idx)
{
  return ((unsigned)idx < AHMF_MAX_SEQFIELD);
}

// Class: AuAHMF
// Method: seq_fld(index)
// -------------------------
// Returns field as indexed from the given sequence
//
inline
int
AuAHMF::seq_fld(int idx, const ah_sequence_field &sf)
{
  if (valid_seq_fld_idx(idx))
    return sf.fld[idx];
  return ((idx==AHMF_MAX_SEQFIELD) ? sf.flg : 0);
}

// Class: AuAHMF
// Method: get_fld_in_seq(seq,type)
// ------------------------------
// Returns the existence of the field in sequence.
// related to higher level FieldIndexInSequence() method
//
inline
bool
AuAHMF::exist_fld_in_seq(const ah_sequence_field &sf, int type)
{
  return get_fld_in_seq(sf,type) >= 0;
}


// Class: AuAHMF
// Method: set_op(type)
// ------------------------
// Change the operation for interval increment.
inline
bool
AuAHMF::set_ushrt(unsigned short &fld, int val)
{
  if ((unsigned long)val > 0x0FFFF)
    return false;
  fld = (unsigned short)val;
  return true;
}

// Class: AuAHMF
// Method: set_op(type)
// ------------------------
// Change the operation for interval increment.
inline
bool
AuAHMF::set_op(unsigned char &fld, int op)
{
  switch (op) {
    case AHMincrAdd:
    case AHMincrMult:
    case AHMincrTangent:  // converts angular to linear coordinates
    case AHMincrLogN:
    case AHMincrLog2:
    case AHMincrLog10:
      fld = op;
      return true;
  }
  return false;
}


// Class: AuAHMF
// Method: set_rsrc(type)
// ------------------------
inline
bool
AuAHMF::set_rsrc(unsigned char &fld, int type)  // see AHMrespSource enum
{
  if ( (type < AHMrespNone) || (type >= AHMrespSourceLast) )
    return false;
  fld = (unsigned char)type;
  return true;
}


// Class: AuAHMF
// Method: set_bits(fld, typ, bts)
// ------------------------
// Change the operation for interval increment.
inline
bool
AuAHMF::set_bits(unsigned char &fld, int typ, int bts)
{
  if ( (bts < 8) || (bts > 64) )
    return false;

  if ( (typ != AHMdataInteger) && (typ != AHMdataFloat) )
    return false;

  if ( (typ == AHMdataFloat) && (bts != 32) && (bts != 64) )
    return false;

  unsigned char *data_type = &bin_header.coefDat;
  int index = int(&fld - &bin_header.coefSiz);

  if (index > 3)
    return false;

  fld = bts;
  data_type[index] = typ;

  return true;
}

// Class: AuAHMF
// Method: set_sqfld(seq, fld, typ)
// ------------------------
// Set field in a sequence.
inline
bool
AuAHMF::set_sqfld(ah_sequence_field &seq, int idx, int type)
{
  if ((unsigned)type >= 128)
    return false;

  if (valid_seq_fld_idx(idx)) {
    seq.fld[idx] = (unsigned char)type;
    return true;
  }

  if (idx==AHMF_MAX_SEQFIELD) {
    seq.flg = (unsigned char)type;
    return true;
  }

  return false;
}

// Methods that return array sizes.

// Class: AuAHMF
// total_grid_locations()
// ------------------------

inline
size_t
AuAHMF::total_grid_locations() const {
  return (bin_header.azimRsp * bin_header.elevRsp * bin_header.rangRsp);
}

// Class: AuAHMF
// respsize()
// ------------------------
inline
size_t
AuAHMF::respsize() const {
  size_t base_size = this->total_grid_locations() * bin_header.sinkCnt;
  return base_size * (bin_header.respCmp ? bin_header.respCmp : CoefsPerFunc() );
}

// Class: AuAHMF
// timesize()
// ------------------------
inline
size_t
AuAHMF::timesize() const {
  size_t timesize = ((this->FieldIndexInSequence(bin_header.respSeq,AHMF_IATD) > -1) ? this->total_grid_locations() : 0);
  timesize += ((this->FieldIndexInSequence(bin_header.respSeq,AHMF_LATD) > -1) ? this->total_grid_locations() : 0);
  timesize += ((this->FieldIndexInSequence(bin_header.respSeq,AHMF_RATD) > -1) ? this->total_grid_locations() : 0);
  timesize += ((this->FieldIndexInSequence(bin_header.respSeq,AHMF_TIME) > -1) ? this->total_grid_locations() * bin_header.sinkCnt : 0);
  return timesize;
}

// Class: AuAHMF
// levlsize()
// ------------------------
inline
size_t
AuAHMF::levlsize() const {
  size_t levlsize = ((this->FieldIndexInSequence(bin_header.respSeq,AHMF_IALD) > -1) ? this->total_grid_locations() : 0);
  levlsize += ((this->FieldIndexInSequence(bin_header.respSeq,AHMF_LALO) > -1) ? this->total_grid_locations() : 0);
  levlsize += ((this->FieldIndexInSequence(bin_header.respSeq,AHMF_RALO) > -1) ? this->total_grid_locations() : 0);
  levlsize += ((this->FieldIndexInSequence(bin_header.respSeq,AHMF_LEVL) > -1) ? this->total_grid_locations() * bin_header.sinkCnt : 0);
  return levlsize;
}

// Class: AuAHMF
// compsize()
// ------------------------
inline
size_t
AuAHMF::compsize() const {
  return bin_header.respCmp * ExistBasisFuncs() * CoefsPerFunc();
}

// Class: AuAHMF
// mapgsize()
// ------------------------
inline
size_t
AuAHMF::mapgsize() const {
  return ( this->respsize() + this->levlsize() + this->timesize() );
}


// ==========================================================================
// ===================== INLINE PUBLIC METHOD IMPLEMENTATION: AuSeqField
// ==========================================================================

inline
AuSeqField::AuSeqField()
{
  fld[0] = fld[1] = fld[2] = fld[3] = fld[4] = fld[5] = fld[6] = flg = 0;
}

inline
void
AuSeqField::Set(const char flag, const char f0, const char f1, const char f2,
                const char f3, const char f4, const char f5, const char f6)
{
  fld[0] =  f0;
  fld[1] =  f1;
  fld[2] =  f2;
  fld[3] =  f3;
  fld[4] =  f4;
  fld[5] =  f5;
  fld[6] =  f6;
  flg    = flag;
}

inline
AuSeqField::AuSeqField(const AuSeqField &sf)
{
  *this = sf;
}

inline
AuSeqField &
AuSeqField::operator=(const AuSeqField &sf)
{
  *this = static_cast<const ah_sequence_field &>(sf);
  return *this;
}

inline
AuSeqField::AuSeqField(const ah_sequence_field &sf)
{
  fld[0] = sf.fld[0];
  fld[1] = sf.fld[1];
  fld[2] = sf.fld[2];
  fld[3] = sf.fld[3];
  fld[4] = sf.fld[4];
  fld[5] = sf.fld[5];
  fld[6] = sf.fld[6];
  flg    = sf.flg;
}

inline
AuSeqField &
AuSeqField::operator=(const ah_sequence_field &sf)
{
  fld[0] = sf.fld[0];
  fld[1] = sf.fld[1];
  fld[2] = sf.fld[2];
  fld[3] = sf.fld[3];
  fld[4] = sf.fld[4];
  fld[5] = sf.fld[5];
  fld[6] = sf.fld[6];
  flg    = sf.flg;

  return *this;
}

inline
bool
AuSeqField::operator==(const AuSeqField &sf)
{
  return (
          (fld[0] == sf.fld[0]) &&
          (fld[1] == sf.fld[1]) &&
          (fld[2] == sf.fld[2]) &&
          (fld[3] == sf.fld[3]) &&
          (fld[4] == sf.fld[4]) &&
          (fld[5] == sf.fld[5]) &&
          (fld[6] == sf.fld[6]) &&
          (flg    == sf.flg)
    );
}

inline
bool
AuSeqField::operator==(const ah_sequence_field &sf)
{
  return (
          (fld[0] == sf.fld[0]) &&
          (fld[1] == sf.fld[1]) &&
          (fld[2] == sf.fld[2]) &&
          (fld[3] == sf.fld[3]) &&
          (fld[4] == sf.fld[4]) &&
          (fld[5] == sf.fld[5]) &&
          (fld[6] == sf.fld[6]) &&
          (flg    == sf.flg)
    );
}


// if either field is zero, than they may still be consistent
inline
bool
AuSeqField::IsConsistent(const AuSeqField &sf)
{
  return (
          (fld[0] == sf.fld[0])
       && ((fld[1] == sf.fld[1]) || !fld[1] || !sf.fld[1])
       && ((fld[2] == sf.fld[2]) || !fld[2] || !sf.fld[2])
       && ((fld[3] == sf.fld[3]) || !fld[3] || !sf.fld[3])
       && ((fld[4] == sf.fld[4]) || !fld[4] || !sf.fld[4])
       && ((fld[5] == sf.fld[5]) || !fld[5] || !sf.fld[5])
       && ((fld[6] == sf.fld[6]) || !fld[6] || !sf.fld[6])
       && ((flg    == sf.flg) || !fld[1] || !sf.fld[1])
    );
}

// if either field is zero, than they may still be consistent
inline
bool
AuSeqField::IsConsistent(const ah_sequence_field &sf)
{
  return (
          (fld[0] == sf.fld[0])
       && ((fld[1] == sf.fld[1]) || !fld[1] || !sf.fld[1])
       && ((fld[2] == sf.fld[2]) || !fld[2] || !sf.fld[2])
       && ((fld[3] == sf.fld[3]) || !fld[3] || !sf.fld[3])
       && ((fld[4] == sf.fld[4]) || !fld[4] || !sf.fld[4])
       && ((fld[5] == sf.fld[5]) || !fld[5] || !sf.fld[5])
       && ((fld[6] == sf.fld[6]) || !fld[6] || !sf.fld[6])
       && ((flg    == sf.flg) || !fld[1] || !sf.fld[1])
    );
}


#endif   // AuCORE_AHMF_H

