/***************************************************************************
**
**  This file is part of GeopsyCore.
**
**  GeopsyCore is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  GeopsyCore is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2010-03-20
**  Copyright: 2010-2019
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "PasscalSegYHeader.h"

namespace GeopsyCore {

  /*!
    \class PasscalSegYHeader PasscalSegYHeader.h
    \brief Brief description of class still missing

    (from src/include/segy.h in passcal software)
    The PASSCAL SEGY trace format is a modified form of the SEG-Y
    trace format.  The modification comes because we use some of
    the unspecified header words to store information pertinent to
    the PASSCAL data.  The data values for each trace are preceded
    by a 240 byte header. This format is given below.  All integer
    values are stored with the most significant byte first (big endian).  Data
    values are either 16 0r 32 bit integers depending on byte 206
    of the header, the field named "data_form".

    PASSCAL SEGY format is also described in many reports that can be
    downloaded from IRIS: http://dmc.iris.washington.edu/data
    One example is http://dmc.iris.washington.edu/data/reports/1999/99-002B.pdf


  */

  /*!
    Description of constructor still missing
  */
  PasscalSegYHeader::PasscalSegYHeader()
  {
    TRACE;
    ASSERT(sizeof( PasscalSegYHeader)==240);
    memset(this, 0, 240);
  }

  void PasscalSegYHeader::read(QDataStream& s)
  {
    TRACE;
    for(int i=0;i<7;i++) s >> raw.raw0[i];
    for(int i=0;i<4;i++) s >> raw.raw1[i];
    for(int i=0;i<8;i++) s >> raw.raw2[i];
    for(int i=0;i<2;i++) s >> raw.raw3[i];
    for(int i=0;i<4;i++) s >> raw.raw4[i];
    for(int i=0;i<46;i++) s >> raw.raw5[i];
    s.readRawData(raw.raw6, 18);
    s >> raw.raw7;
    s >> raw.raw8;
    for(int i=0;i<8;i++) s >> raw.raw9[i];
    s >> raw.raw10;
    for(int i=0;i<2;i++) s >> raw.raw11[i];
    for(int i=0;i<3;i++) s >> raw.raw12[i];
  }

  void PasscalSegYHeader::write(QDataStream& s) const
  {
    TRACE;
    for(int i=0;i<7;i++) s << raw.raw0[i];
    for(int i=0;i<4;i++) s << raw.raw1[i];
    for(int i=0;i<8;i++) s << raw.raw2[i];
    for(int i=0;i<2;i++) s << raw.raw3[i];
    for(int i=0;i<4;i++) s << raw.raw4[i];
    for(int i=0;i<46;i++) s << raw.raw5[i];
    s.writeRawData(raw.raw6, 18);
    s << raw.raw7;
    s << raw.raw8;
    for(int i=0;i<8;i++) s << raw.raw9[i];
    s << raw.raw10;
    for(int i=0;i<2;i++) s << raw.raw11[i];
    for(int i=0;i<3;i++) s << raw.raw12[i];
  }

  SignalFileFormat PasscalSegYHeader::determineByteOrder(QString fileName, int verbosity)
  {
    TRACE;
    QFile f(fileName);
    if( !f.open(QIODevice::ReadOnly) ) {
      return SignalFileFormat::Unknown;
    }
    QDataStream s(&f);
  #if(QT_VERSION >= QT_VERSION_CHECK(4, 6, 0))
    s.setFloatingPointPrecision(QDataStream::SinglePrecision);
  #endif
    s.setByteOrder(QDataStream::LittleEndian);
    bool littleEndian=canReadAll(s, f.size());
    f.seek(0);
    s.setByteOrder(QDataStream::BigEndian);
    bool bigEndian=canReadAll(s, f.size());
    if((littleEndian && bigEndian) || (!littleEndian && !bigEndian)) {
      App::log(verbosity, tr("Cannot determine byte order for PASSCAL SEGY format.\n") );
      return SignalFileFormat::Unknown;
    } else if(littleEndian) {
      return SignalFileFormat::PasscalSegYLittleEndian;
    } else {
      return SignalFileFormat::PasscalSegYBigEndian;
    }
  }

  bool PasscalSegYHeader::canReadAll(QDataStream& s, qint64 fileSize)
  {
    TRACE;
    PasscalSegYHeader h;
    qint64 offset=0;
    while(offset<fileSize) {
      s.device()->seek(offset);
      h.read(s);
      if(s.status()!=QDataStream::Ok) {
        return false;
      }
      offset+=240 + h.sampleSize() * (qint64) h.nSamples();
      if(offset>fileSize) {
        return false;
      }
    }
    return true;
  }

  DateTime PasscalSegYHeader::startTime() const
  {
    TRACE;
    DateTime t(QDateTime(QDate(field.year , 1, 1), QTime(field.hour, field.minute, field.second)));
    t.addDays(field.day-1);
    t.addSeconds(0.001*field.milliseconds);
    return t;
  }

  DateTime PasscalSegYHeader::timePick() const
  {
    TRACE;
    DateTime t(QDateTime(QDate(field.triggerYear , 1, 1), QTime(field.triggerHour, field.triggerMinute, field.triggerSecond)));
    t.addDays(field.triggerDay-1);
    t.addSeconds(0.001*field.triggerMilliseconds);
    return t;
  }

  Signal::Components PasscalSegYHeader::component() const
  {
    TRACE;
    switch(field.channelNumber) {
    case 1:
    case 4:
      return Signal::Vertical;
    case 2:
    case 5:
      return Signal::North;
    case 3:
    case 6:
      return Signal::East;
    default:
      return Signal::UndefinedComponent;
    }
  }

} // namespace GeopsyCore
