/*
===============================================================================

  FILE:  laswaveform13reader.cpp

  CONTENTS:

    see corresponding header file

  PROGRAMMERS:

    martin.isenburg@rapidlasso.com  -  http://rapidlasso.com

  COPYRIGHT:

    (c) 2007-2012, martin isenburg, rapidlasso - fast tools to catch reality

    This is free software; you can redistribute and/or modify it under the
    terms of the GNU Lesser General Licence as published by the Free Software
    Foundation. See the LICENSE.txt file for more information.

    This software is distributed WITHOUT ANY WARRANTY and without even the
    implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

  CHANGE HISTORY:

    see corresponding header file

===============================================================================
*/
#include "laswaveform13reader.hpp"

#include "bytestreamin_file.hpp"
#include "arithmeticdecoder.hpp"
#include "integercompressor.hpp"

LASwaveform13reader::LASwaveform13reader()
{
  nbits = 0;
  nsamples = 0;
  temporal = 0;
  location = 0;
  XYZt[0] = XYZt[1] = XYZt[2] = 0;
  XYZreturn[0] = XYZreturn[1] = XYZreturn[2] = 0;

  s_count = 0;
  XYZsample[0] = XYZsample[1] = XYZsample[2] = 0;
  sample = 0;

  sampleMin = 0;
  sampleMax = 0;

  samples = 0;

  size = 0;
  compressed = FALSE;
  wave_packet_descr = 0;
  file = 0;
  stream = 0;
  last_position = 0;
  start_of_waveform_data_packet_record = 0;
  dec = 0;
  ic8 = 0;
  ic16 = 0;
}

LASwaveform13reader::~LASwaveform13reader()
{
  if (samples) delete [] samples;
  if (ic8) delete ic8;
  if (ic16) delete ic16;
  if (dec) delete dec;
}

BOOL LASwaveform13reader::is_compressed() const
{
  return compressed;
}

BOOL LASwaveform13reader::open(const char* file_name, I64 start_of_waveform_data_packet_record, const LASvlr_wave_packet_descr * const * wave_packet_descr)
{
  if (file_name == 0)
  {
    REprintf("ERROR: file name pointer is zero\n");
    return FALSE;
  }

  // we need wave packet descriptors

  if (wave_packet_descr == 0)
  {
    REprintf("ERROR: wave packet descriptor pointer is zero\n");
    return FALSE;
  }

  // only array positions 1 through 255 should have a wave packet descriptor

  if (wave_packet_descr[0] != 0)
  {
    REprintf("ERROR: wave_packet_descr[0] with index 0 must be zero\n");
    return FALSE;
  }

  // check if compressed or not

  I32 i;
  compressed = FALSE;

  for (i = 1; i < 256; i++)
  {
    if (wave_packet_descr[i])
    {
      compressed = compressed || (wave_packet_descr[i]->getCompressionType() > 0);
    }
  }

  // create file name and open file

  if (start_of_waveform_data_packet_record == 0)
  {
    if (!compressed && (strstr(".wdp", file_name) || strstr(".WDP", file_name)))
    {
      file = fopen(file_name, "rb");
    }
    else if (compressed && (strstr(".wdz", file_name) || strstr(".WDZ", file_name)))
    {
      file = fopen(file_name, "rb");
    }
    else
    {
      char* file_name_temp = LASCopyString(file_name);
      I32 len = (I32)strlen(file_name_temp);
      if ((file_name_temp[len-3] == 'L') || (file_name_temp[len-3] == 'W'))
      {
        file_name_temp[len-3] = 'W';
        file_name_temp[len-2] = 'D';
        file_name_temp[len-1] = (compressed ? 'Z' : 'P');
      }
      else
      {
        file_name_temp[len-3] = 'w';
        file_name_temp[len-2] = 'd';
        file_name_temp[len-1] = (compressed ? 'z' : 'p');
      }
      file = fopen(file_name_temp, "rb");
      free(file_name_temp);
    }
  }
  else
  {
    file = fopen(file_name, "rb");
  }

  if (file == 0)
  {
    REprintf( "ERROR: cannot open waveform file '%s'\n", file_name);
    return FALSE;
  }

  if (IS_LITTLE_ENDIAN())
  {
    stream = new ByteStreamInFileLE(file);
  }
  else
  {
    stream = new ByteStreamInFileBE(file);
  }

  this->start_of_waveform_data_packet_record = start_of_waveform_data_packet_record;
  this->wave_packet_descr = wave_packet_descr;

  // attempt waveform descriptor cross-check

  I64 position = start_of_waveform_data_packet_record + 60;
  stream->seek(position);

  char magic[25];
  try { stream->getBytes((U8*)magic, 24); } catch(...)
  {
    REprintf("ERROR: reading waveform descriptor cross-check\n");
    return FALSE;
  }

  if (strncmp(magic, "LAStools waveform ", 18) == 0)
  {
    // do waveform descriptor cross-check

    U16 i, number;
    try { stream->get16bitsLE((U8*)&number); } catch(...)
    {
      REprintf("ERROR: reading number of waveform descriptors\n");
      return FALSE;
    }
    for (i = 0; i < number; i++)
    {
      U16 index;
      try { stream->get16bitsLE((U8*)&index); } catch(...)
      {
        REprintf("ERROR: reading index of waveform descriptor %d\n", i);
        return FALSE;
      }
      if ((index == 0) || (index > 255))
      {
        REprintf("ERROR: cross-check - index %d of waveform descriptor %d out-of-range\n", index, i);
        return FALSE;
      }
      if (wave_packet_descr[index] == 0)
      {
        REprintf("WARNING: cross-check - waveform descriptor %d with index %d unknown\n", i, index);
        I32 dummy;
        try { stream->get32bitsLE((U8*)&dummy); } catch(...)
        {
          REprintf("ERROR: cross-check - reading rest of waveform descriptor %d\n", i);
          return FALSE;
        }
        continue;
      }
      U8 compression;
      try { stream->getBytes(&compression, 1); } catch(...)
      {
        REprintf("ERROR: reading compression of waveform descriptor %d\n", i);
        return FALSE;
      }
      if (compression != wave_packet_descr[index]->getCompressionType())
      {
        REprintf("ERROR: cross-check - compression %d %d of waveform descriptor %d with index %d is different\n", compression, wave_packet_descr[index]->getCompressionType(), i, index);
        return FALSE;
      }
      U8 nbits;
      try { stream->getBytes(&nbits, 1); } catch(...)
      {
        REprintf("ERROR: reading nbits of waveform descriptor %d\n", i);
        return FALSE;
      }
      if (nbits != wave_packet_descr[index]->getBitsPerSample())
      {
        REprintf("ERROR: cross-check - nbits %d %d of waveform descriptor %d with index %d is different\n", nbits, wave_packet_descr[index]->getBitsPerSample(), i, index);
        return FALSE;
      }
      U16 nsamples;
      try { stream->get16bitsLE((U8*)&nsamples); } catch(...)
      {
        REprintf("ERROR: reading nsamples of waveform descriptor %d\n", i);
        return FALSE;
      }
      if (nsamples != wave_packet_descr[index]->getNumberOfSamples())
      {
        REprintf("ERROR: cross-check - nsamples %d %d of waveform descriptor %d with index %d is different\n", nsamples, wave_packet_descr[index]->getNumberOfSamples(), i, index);
        return FALSE;
      }
    }
  }

  last_position = stream->tell();

  // create decompressor

  if (compressed)
  {
    if (dec == 0) dec = new ArithmeticDecoder();
    if (ic8 == 0) ic8 = new IntegerCompressor(dec, 8);
    if (ic16 == 0) ic16 = new IntegerCompressor(dec, 16);
  }
  return TRUE;
}

BOOL LASwaveform13reader::read_waveform(const LASpoint* point)
{
  U32 index = point->wavepacket.getIndex();
  if (index == 0)
  {
    return FALSE;
  }

  if (wave_packet_descr[index] == 0)
  {
    REprintf( "ERROR: wavepacket is indexing non-existant descriptor %u\n", index);
    return FALSE;
  }

  nbits = wave_packet_descr[index]->getBitsPerSample();
  if ((nbits != 8) && (nbits != 16))
  {
    REprintf( "ERROR: waveform with %d bits per samples not supported yet\n", nbits);
    return FALSE;
  }

  nsamples = wave_packet_descr[index]->getNumberOfSamples();

//  temporary Optech Fix
//  nsamples = point->wavepacket.getSize();
//  if (nbits == 16) nsamples / 2;

  if (nsamples == 0)
  {
    REprintf( "ERROR: waveform has no samples\n");
    return FALSE;
  }

  temporal = wave_packet_descr[index]->getTemporalSpacing();
  location = point->wavepacket.getLocation();

  XYZt[0] = point->wavepacket.getXt();
  XYZt[1] = point->wavepacket.getYt();
  XYZt[2] = point->wavepacket.getZt();

  XYZreturn[0] = point->get_x();
  XYZreturn[1] = point->get_y();
  XYZreturn[2] = point->get_z();

  // alloc data

  if (size < ((nbits/8) * nsamples))
  {
    if (samples) delete [] samples;
    samples = new U8[((nbits/8) * nsamples)];
  }

  size = ((nbits/8) * nsamples);

  // read waveform

  I64 position = start_of_waveform_data_packet_record + point->wavepacket.getOffset();
  stream->seek(position);

  if (wave_packet_descr[index]->getCompressionType() == 0)
  {
    try { stream->getBytes(samples, size); } catch(...)
    {
      REprintf( "ERROR: cannot read %u bytes for waveform with %u samples of %u bits\n", size, nsamples, nbits);
      return FALSE;
    }
  }
  else
  {
    if (nbits == 8)
    {
      stream->getBytes(samples, 1);
      dec->init(stream);
      ic8->initDecompressor();
      for (s_count = 1; s_count < nsamples; s_count++)
      {
        samples[s_count] = ic8->decompress(samples[s_count-1]);
      }
    }
    else
    {
      stream->getBytes(samples, 2);
      dec->init(stream);
      ic16->initDecompressor();
      for (s_count = 1; s_count < nsamples; s_count++)
      {
        ((U16*)samples)[s_count] = ic16->decompress(((U16*)samples)[s_count-1]);
      }
    }
    dec->done();
  }

  s_count = 0;
  return TRUE;
}

BOOL LASwaveform13reader::get_samples()
{
  if (nbits == 8)
  {
    sampleMin = samples[0];
    sampleMax = samples[0];
    for (s_count = 1; s_count < nsamples; s_count++)
    {
      if (samples[s_count] < sampleMin) sampleMin = samples[s_count];
      else if (samples[s_count] > sampleMax) sampleMax = samples[s_count];
    }
  }
  else
  {
    sampleMin = ((U16*)samples)[0];
    sampleMax = ((U16*)samples)[0];
    for (s_count = 1; s_count < nsamples; s_count++)
    {
      if (((U16*)samples)[s_count] < sampleMin) sampleMin = ((U16*)samples)[s_count];
      else if (((U16*)samples)[s_count] > sampleMax) sampleMax = ((U16*)samples)[s_count];
    }
  }
  s_count = 0;
  return (s_count < nsamples);
}

BOOL LASwaveform13reader::has_samples()
{
  if (s_count < nsamples)
  {
    if (nbits == 8)
    {
      sample = samples[s_count];
    }
    else
    {
      sample = ((U16*)samples)[s_count];
    }
    s_count++;
    return TRUE;
  }
  return FALSE;
}

BOOL LASwaveform13reader::get_samples_xyz()
{
  if (nbits == 8)
  {
    sampleMin = samples[0];
    sampleMax = samples[0];
    for (s_count = 1; s_count < nsamples; s_count++)
    {
      if (samples[s_count] < sampleMin) sampleMin = samples[s_count];
      else if (samples[s_count] > sampleMax) sampleMax = samples[s_count];
    }
  }
  else
  {
    sampleMin = ((U16*)samples)[0];
    sampleMax = ((U16*)samples)[0];
    for (s_count = 1; s_count < nsamples; s_count++)
    {
      if (((U16*)samples)[s_count] < sampleMin) sampleMin = ((U16*)samples)[s_count];
      else if (((U16*)samples)[s_count] > sampleMax) sampleMax = ((U16*)samples)[s_count];
    }
  }
  s_count = 0;
  return (s_count < nsamples);
}

BOOL LASwaveform13reader::has_samples_xyz()
{
  if (s_count < nsamples)
  {
    F32 dist = location - s_count*temporal;
    XYZsample[0] = XYZreturn[0] + dist*XYZt[0];
    XYZsample[1] = XYZreturn[1] + dist*XYZt[1];
    XYZsample[2] = XYZreturn[2] + dist*XYZt[2];
    if (nbits == 8)
    {
      sample = samples[s_count];
    }
    else
    {
      sample = ((U16*)samples)[s_count];
    }
    s_count++;
    return TRUE;
  }
  return FALSE;
}

void LASwaveform13reader::close()
{
  if (stream)
  {
    delete stream;
    stream = 0;
  }
  if (file)
  {
    fclose(file);
    file = 0;
  }
}
