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

  CONTENTS:
    Point formats for LAS

  PROGRAMMERS:

    martin.isenburg@rapidlasso.com  -  http://rapidlasso.com
    uday.karan@gmail.com - Hobu, Inc.

  COPYRIGHT:

    (c) 2007-2014, martin isenburg, rapidlasso - tools to catch reality
    (c) 2014, Uday Verma, Hobu, Inc.

    This is free software; you can redistribute and/or modify it under the
    terms of the Apache Public License 2.0 published by the Apache Software
    Foundation. See the COPYING 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:

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

#pragma once

#include <functional>
#include <memory>
#include <vector>

#include "header.hpp"
#include "lazperf_base.hpp"

namespace lazperf
{

// Called when compressed output is to be written.
using OutputCb = std::function<void(const unsigned char *, size_t)>;

// Called when compressed input is to be read.
using InputCb = std::function<void(unsigned char *, size_t)>;

class LAZPERF_EXPORT las_compressor
{
public:
    typedef std::shared_ptr<las_compressor> ptr;

    virtual const char *compress(const char *in) = 0;
    virtual void done() = 0;
    virtual ~las_compressor();
};

class LAZPERF_EXPORT las_decompressor
{
public:
    typedef std::shared_ptr<las_decompressor> ptr;

    virtual char *decompress(char *in) = 0;
    virtual ~las_decompressor();
};

class point_compressor_base_1_2 : public las_compressor
{
    struct Private;

public:
    LAZPERF_EXPORT void done();

protected:
    point_compressor_base_1_2(OutputCb cb, size_t ebCount);
    virtual ~point_compressor_base_1_2();

    std::unique_ptr<Private> p_;
};

class point_compressor_0 final : public point_compressor_base_1_2
{
public:
    LAZPERF_EXPORT point_compressor_0(OutputCb cb, size_t ebCount = 0);
    LAZPERF_EXPORT ~point_compressor_0();

    LAZPERF_EXPORT virtual const char *compress(const char *in);
};

class point_compressor_1 final : public point_compressor_base_1_2
{
public:
    LAZPERF_EXPORT point_compressor_1(OutputCb cb, size_t ebCount = 0);
    LAZPERF_EXPORT ~point_compressor_1();

    LAZPERF_EXPORT virtual const char *compress(const char *in);
};

class point_compressor_2 final : public point_compressor_base_1_2
{
public:
    LAZPERF_EXPORT point_compressor_2(OutputCb cb, size_t ebCount = 0);
    LAZPERF_EXPORT ~point_compressor_2();

    LAZPERF_EXPORT virtual const char *compress(const char *in);
};

class point_compressor_3 final : public point_compressor_base_1_2
{
public:
    LAZPERF_EXPORT point_compressor_3(OutputCb cb, size_t ebCount = 0);
    LAZPERF_EXPORT ~point_compressor_3();

    LAZPERF_EXPORT virtual const char *compress(const char *in);
};

class point_compressor_base_1_4 : public las_compressor
{
    struct Private;

public:
    virtual const char *compress(const char *in) = 0;

protected:
    point_compressor_base_1_4(OutputCb cb, size_t ebCount);

    std::unique_ptr<Private> p_;
};


class point_compressor_6 final : public point_compressor_base_1_4
{
public:
    LAZPERF_EXPORT point_compressor_6(OutputCb cb, size_t ebCount = 0);
    LAZPERF_EXPORT ~point_compressor_6();

    LAZPERF_EXPORT virtual const char *compress(const char *in);
    LAZPERF_EXPORT virtual void done();
};

class point_compressor_7 final : public point_compressor_base_1_4
{
public:
    LAZPERF_EXPORT point_compressor_7(OutputCb cb, size_t ebCount = 0);
    LAZPERF_EXPORT ~point_compressor_7();

    LAZPERF_EXPORT virtual const char *compress(const char *in);
    LAZPERF_EXPORT virtual void done();
};

class point_compressor_8 final : public point_compressor_base_1_4
{
public:
    LAZPERF_EXPORT point_compressor_8(OutputCb cb, size_t ebCount = 0);
    LAZPERF_EXPORT ~point_compressor_8();

    LAZPERF_EXPORT virtual const char *compress(const char *in);
    LAZPERF_EXPORT virtual void done();
};


// Decompressor

class point_decompressor_base_1_2 : public las_decompressor
{
    struct Private;

public:
    virtual char *decompress(char *in) = 0;
    virtual ~point_decompressor_base_1_2();

protected:
    point_decompressor_base_1_2(InputCb cb, size_t ebCount);
    void handleFirst();

    std::unique_ptr<Private> p_;
};

class point_decompressor_0 final : public point_decompressor_base_1_2
{
public:
    LAZPERF_EXPORT point_decompressor_0(InputCb cb, size_t ebCount = 0);
    LAZPERF_EXPORT ~point_decompressor_0();

    LAZPERF_EXPORT virtual char *decompress(char *in);
};

class point_decompressor_1 final : public point_decompressor_base_1_2
{
public:
    LAZPERF_EXPORT point_decompressor_1(InputCb cb, size_t ebCount = 0);
    LAZPERF_EXPORT ~point_decompressor_1();

    LAZPERF_EXPORT virtual char *decompress(char *out);
};

class point_decompressor_2 final : public point_decompressor_base_1_2
{
public:
    LAZPERF_EXPORT point_decompressor_2(InputCb cb, size_t ebCount = 0);
    LAZPERF_EXPORT ~point_decompressor_2();

    LAZPERF_EXPORT virtual char *decompress(char *out);
};

class point_decompressor_3 final : public point_decompressor_base_1_2
{
public:
    LAZPERF_EXPORT point_decompressor_3(InputCb cb, size_t ebCount = 0);
    LAZPERF_EXPORT ~point_decompressor_3();

    LAZPERF_EXPORT virtual char *decompress(char *out);
};

class point_decompressor_base_1_4 : public las_decompressor
{
    struct Private;

public:
    virtual char *decompress(char *out) = 0;

protected:
    point_decompressor_base_1_4(InputCb cb, size_t ebCount);

    std::unique_ptr<Private> p_;
};

class point_decompressor_6 final : public point_decompressor_base_1_4
{
public:
    LAZPERF_EXPORT point_decompressor_6(InputCb cb, size_t ebCount = 0);
    LAZPERF_EXPORT ~point_decompressor_6();

    LAZPERF_EXPORT virtual char *decompress(char *out);
};

class point_decompressor_7 final : public point_decompressor_base_1_4
{
public:
    LAZPERF_EXPORT point_decompressor_7(InputCb cb, size_t ebCount = 0);
    LAZPERF_EXPORT ~point_decompressor_7();

    LAZPERF_EXPORT virtual char *decompress(char *out);
};

struct point_decompressor_8 final : public point_decompressor_base_1_4
{
public:
    LAZPERF_EXPORT ~point_decompressor_8();
    LAZPERF_EXPORT point_decompressor_8(InputCb cb, size_t ebCount = 0);

    LAZPERF_EXPORT virtual char *decompress(char *out);
};

// FACTORY

LAZPERF_EXPORT las_compressor::ptr build_las_compressor(OutputCb, int format,
    size_t ebCount = 0);
LAZPERF_EXPORT las_decompressor::ptr build_las_decompressor(InputCb, int format,
    size_t ebCount = 0);

// CHUNK TABLE

// Note that the chunk values are sizes, rather than offsets.

// This functions is only for fixed-sized chunks.
LAZPERF_EXPORT void compress_chunk_table(OutputCb cb, const std::vector<uint32_t>& chunks);
LAZPERF_EXPORT void compress_chunk_table(OutputCb cb, const std::vector<chunk>& chunks,
    bool variableChunks);
LAZPERF_EXPORT std::vector<chunk> decompress_chunk_table(std::istream& s, size_t numChunks,
    bool variableChunks);
LAZPERF_EXPORT std::vector<chunk> decompress_chunk_table(InputCb cb, size_t numChunks,
    bool variableChunks);

// Deprecated
LAZPERF_EXPORT std::vector<uint32_t> decompress_chunk_table(InputCb cb, size_t numChunks);

} // namespace lazperf

