#ifndef DECODER_H
#define DECODER_H

/*
 * Copyright 2009 - 2010 Kevin Ackley (kackley@gwi.net)
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "Common.h"

namespace e57 {

   class Decoder
   {
      public:
         static std::shared_ptr<Decoder>  DecoderFactory(unsigned bytestreamNumber,
                                                         std::shared_ptr<CompressedVectorNodeImpl> cVector,
                                                         std::vector<SourceDestBuffer>& dbufs,
                                                         const ustring& codecPath);
         virtual             ~Decoder() {}

         virtual void        destBufferSetNew(std::vector<SourceDestBuffer>& dbufs) = 0;
         virtual uint64_t    totalRecordsCompleted() = 0;
         virtual size_t      inputProcess(const char* source, const size_t count) = 0;
         virtual void        stateReset() = 0;
         unsigned            bytestreamNumber() {return(bytestreamNumber_);}
#ifdef E57_DEBUG
         virtual void        dump(int indent = 0, std::ostream& os = std::cout) = 0;
#endif

      private:
         Decoder() = delete;

      protected:
         Decoder(unsigned bytestreamNumber);

         unsigned            bytestreamNumber_;
   };


   class BitpackDecoder : public Decoder
   {
      public:
         virtual void        destBufferSetNew(std::vector<SourceDestBuffer>& dbufs);

         virtual uint64_t    totalRecordsCompleted() {return(currentRecordIndex_);}

         virtual size_t      inputProcess(const char* source, const size_t byteCount);
         virtual size_t      inputProcessAligned(const char* inbuf, const size_t firstBit, const size_t endBit) = 0;

         virtual void        stateReset();

#ifdef E57_DEBUG
         virtual void        dump(int indent = 0, std::ostream& os = std::cout);
#endif
      protected:
         BitpackDecoder(unsigned bytestreamNumber, SourceDestBuffer& dbuf, unsigned alignmentSize, uint64_t maxRecordCount);

         void                inBufferShiftDown();

         uint64_t            currentRecordIndex_;
         uint64_t            maxRecordCount_;

         std::shared_ptr<SourceDestBufferImpl> destBuffer_;

         std::vector<char>   inBuffer_;
         size_t              inBufferFirstBit_;
         size_t              inBufferEndByte_;
         unsigned            inBufferAlignmentSize_;
         unsigned            bitsPerWord_;
         unsigned            bytesPerWord_;
   };


   class BitpackFloatDecoder : public BitpackDecoder
   {
      public:
         BitpackFloatDecoder(unsigned bytestreamNumber, SourceDestBuffer& dbuf, FloatPrecision precision, uint64_t maxRecordCount);

         virtual size_t      inputProcessAligned(const char* inbuf, const size_t firstBit, const size_t endBit);

#ifdef E57_DEBUG
         virtual void        dump(int indent = 0, std::ostream& os = std::cout);
#endif
      protected:
         FloatPrecision      precision_;
   };


   class BitpackStringDecoder : public BitpackDecoder
   {
      public:
         BitpackStringDecoder(unsigned bytestreamNumber, SourceDestBuffer& dbuf, uint64_t maxRecordCount);

         virtual size_t      inputProcessAligned(const char* inbuf, const size_t firstBit, const size_t endBit);

#ifdef E57_DEBUG
         virtual void        dump(int indent = 0, std::ostream& os = std::cout);
#endif
      protected:
         bool        readingPrefix_;
         int         prefixLength_;
         uint8_t     prefixBytes_[8];
         int         nBytesPrefixRead_;
         uint64_t    stringLength_;
         ustring     currentString_;
         uint64_t    nBytesStringRead_;
   };


   template <typename RegisterT>
   class BitpackIntegerDecoder : public BitpackDecoder
   {
      public:
         BitpackIntegerDecoder(bool isScaledInteger, unsigned bytestreamNumber, SourceDestBuffer& dbuf,
                               int64_t minimum, int64_t maximum, double scale, double offset, uint64_t maxRecordCount);

         virtual size_t      inputProcessAligned(const char* inbuf, const size_t firstBit, const size_t endBit);

#ifdef E57_DEBUG
         virtual void        dump(int indent = 0, std::ostream& os = std::cout);
#endif
      protected:
         bool        isScaledInteger_;
         int64_t     minimum_;
         int64_t     maximum_;
         double      scale_;
         double      offset_;
         unsigned    bitsPerRecord_;
         RegisterT   destBitMask_;
   };


   class ConstantIntegerDecoder : public Decoder
   {
      public:
         ConstantIntegerDecoder(bool isScaledInteger, unsigned bytestreamNumber, SourceDestBuffer& dbuf,
                                int64_t minimum, double scale, double offset, uint64_t maxRecordCount);
         virtual void        destBufferSetNew(std::vector<SourceDestBuffer>& dbufs);
         virtual uint64_t    totalRecordsCompleted() {return(currentRecordIndex_);}
         virtual size_t      inputProcess(const char* source, const size_t byteCount);
         virtual void        stateReset();
#ifdef E57_DEBUG
         virtual void        dump(int indent = 0, std::ostream& os = std::cout);
#endif
      protected:
         uint64_t            currentRecordIndex_;
         uint64_t            maxRecordCount_;

         std::shared_ptr<SourceDestBufferImpl> destBuffer_;

         bool                isScaledInteger_;
         int64_t             minimum_;
         double              scale_;
         double              offset_;
   };
}

#endif
