/*
* v3dfile.h
* Header file for v3d export and types
*
* Supakorn "Jamie" Rassameemasmuang <[email protected]> and
* John C. Bowman
*/

#ifndef V3DFILE_H
#define V3DFILE_H

#include <prc/oPRCFile.h>
#include <zlib.h>

#include "common.h"

#ifdef HAVE_LIBTIRPC

#include "abs3doutfile.h"
#include "xstream.h"
#include "triple.h"
#include "material.h"
#define transform transform_
#include "v3dtypes.h"
#undef transform
#include "v3dheadertypes.h"

namespace camp
{

class AHeader
{
public:
 v3dheadertypes ty;

 AHeader(v3dheadertypes const& ty) : ty(ty) {}
 virtual ~AHeader() = default;
 virtual uint32_t getWordSize(bool singleprecision) const = 0;
 virtual void writeContent(xdr::oxstream& ox) const = 0;
};

template<typename T, uint32_t realWords, uint32_t floatWords=0>
class SingleObjectHeader : public AHeader
{
public:
 SingleObjectHeader(v3dheadertypes const& ty, T const& ob) : AHeader(ty), obj(ob)
 {
 }
 ~SingleObjectHeader() override = default;

protected:
 uint32_t getWordSize(bool singleprecision) const override
 {
   return (singleprecision ? 1 : 2)*realWords+floatWords;
 }

 void writeContent(xdr::oxstream &ox) const override
 {
   ox << obj;
 }

private:
 T obj;
};

using open_mode=xdr::xios::open_mode;
using TripleHeader=SingleObjectHeader<triple,3>;
using PairHeader=SingleObjectHeader<pair,2>;
using DoubleHeader=SingleObjectHeader<double,1>;
using Uint32Header=SingleObjectHeader<uint32_t,0,1>;
using RGBAHeader=SingleObjectHeader<prc::RGBAColour,0,4>;

const unsigned int v3dVersion=1;

class LightHeader : public AHeader
{
public:
 LightHeader(triple const& direction, prc::RGBAColour const& color);
 ~LightHeader() override=default;

protected:
 [[nodiscard]]
 uint32_t getWordSize(bool singleprecision) const override;
 void writeContent(xdr::oxstream &ox) const override;

private:
 triple direction;
 prc::RGBAColour color;
};

class v3dfile : public abs3Doutfile {
private:
 bool finalized;
public:
 v3dfile(bool singleprecision=false) : abs3Doutfile(singleprecision),
                                       finalized(false) {}
 void writeInit();
 void finalize();

 void addPatch(triple const* controls, prc::RGBAColour const* c) override;
 void addStraightPatch(
         triple const* controls, prc::RGBAColour const* c) override;
 void addBezierTriangle(
         triple const* control, prc::RGBAColour const* c) override;
 void addStraightBezierTriangle(
         triple const* controls, prc::RGBAColour const* c) override;

#ifdef HAVE_LIBGLM
 void addMaterial(Material const& mat) override;
#endif

 void addSphere(triple const& center, double radius) override;
 void addHemisphere(triple const& center, double radius, double const& polar, double const& azimuth) override;

 void addCylinder(triple const& center, double radius, double height,
                  double const& polar, const double& azimuth,
                  bool core) override;
 void addDisk(triple const& center, double radius,
              double const& polar, const double& azimuth) override;
 void addTube(const triple *g, double width, bool core) override;

 void addTriangles(size_t nP, const triple* P, size_t nN,
                   const triple* N, size_t nC, const prc::RGBAColour* C,
                   size_t nI, const uint32_t (*PI)[3],
                   const uint32_t (*NI)[3],
                   const uint32_t (*CI)[3]) override;

 void addCurve(triple const& z0, triple const& c0, triple const& c1,
               triple const& z1) override;

 void addCurve(triple const& z0, triple const& z1) override;

 void addPixel(triple const& z0, double width) override;

 void precision(int digits) override {}


protected:
#ifdef HAVE_LIBGLM
 void addvec4(glm::vec4 const& vec);
#endif

 void addCenterIndexMat();
 void addIndices(uint32_t const* trip);
 void addTriples(triple const* triples, size_t n);
 void addColors(prc::RGBAColour const* col, size_t nc);

 void addHeaders();
 void addCenters();

 virtual xdr::oxstream& getXDRFile() = 0;
};

class gzv3dfile : public v3dfile {
public:
 gzv3dfile(string const& name, bool singleprecision=false);
 ~gzv3dfile() override;

protected:
 xdr::oxstream& getXDRFile() override;

private:
 xdr::memoxstream memxdrfile;
 string name;
 bool destroyed;
 void close() override;
};

} //namespace camp
#endif

#endif