#include "DDImage/Attribute.h"
#include "DDImage/GeometryList.h"
#include "DDImage/GeoReader.h"
#include "DDImage/GeoReaderDescription.h"
#include "DDImage/Triangle.h"
#include "DDImage/Vector3.h"

#include <iostream>
#include <fstream>
#include <string>

using namespace DD::Image;

namespace my3d {

  class my3dReaderFormat : public GeoReaderFormat
  {
  public:

    void knobs(Knob_Callback c)
    {
      // define knobs here
    }

    void append(Hash& hash)
    {
      // update hash
    }
  };

  static GeoReaderFormat* buildformat(ReadGeo* iop)
  {
    return new my3dReaderFormat();
  }

  class my3dReader : public GeoReader
  {
  public:
    my3dReader(ReadGeo* readNode);

  protected:

    // DD::Image::GeoReader
    virtual void geometry_engine(DD::Image::Scene& scene, DD::Image::GeometryList& out);
    virtual void get_geometry_hash(DD::Image::Hash* geo_hash) {}
    virtual int  knob_changed(DD::Image::Knob* k) { return 0; }
    virtual void _validate(const bool for_real) {}
    virtual void append(Hash& newHash) {}
    //DD::Image::GeoReader

    static GeoReader* Build(ReadGeo* readNode, int fd, const unsigned char* buf, int bufSize);
    static bool Test(int fd, const unsigned char* buf, int bufSize);

    static GeoDescription description;
  };


  //
  // my3dReader methods
  //
  
  my3dReader::my3dReader(ReadGeo* readNode) :
    GeoReader(readNode)
  {
  }

  
  void my3dReader::geometry_engine(Scene& scene, GeometryList& out)
  {
    const unsigned int kMaxLineLen = 4096;

    // Open the file
    std::ifstream in(filename());
    
    // Skip over the header.
    in.ignore(kMaxLineLen, '\n');

    int obj = 0;
    out.add_object(obj);
    PointList* points = out.writable_points(obj);
    Attribute* normals = out.writable_attribute(obj, Group_Primitives, kNormalAttrName, NORMAL_ATTRIB);
    Attribute* texCoords = out.writable_attribute(obj, Group_Points, kUVAttrName, VECTOR4_ATTRIB);

    Vector3 pos, normal;
    Vector4 uv(0, 0, 0, 1);
    float size;
    int n = 0;
    while (in.good()) {
      in >> size >> pos.x >> pos.y >> pos.z >> normal.x >> normal.y >> normal.z >> uv.x >> uv.y;
      in.ignore(kMaxLineLen, '\n'); // skip any trailing characters.
      
      out.add_primitive(obj, new Triangle(n  * 3, n * 3 + 1, n * 3 + 2));

      Vector3 v1 = normal.cross(Vector3(1, 0, 0));
      Vector3 v2 = v1.cross(normal);
      v1.normalize();
      v2.normalize();
      points->push_back(pos);
      points->push_back(pos + (v1 * size));
      points->push_back(pos + (v2 * size));

      normals->add(1);
      normals->normal(n) = normal;

      texCoords->add(3);
      texCoords->vector4(n * 3) = uv;
      texCoords->vector4(n * 3 + 1) = uv + Vector4(size, 0, 0, 0);
      texCoords->vector4(n * 3 + 2) = uv + Vector4(0, size, 0, 0);

      ++n;
    }
  }


  //
  // my3dReader static methods
  //

  GeoReader* my3dReader::Build(ReadGeo* readNode, int fd, const unsigned char* buf, int bufSize)
  {
    return new my3dReader(readNode);
  }


  bool my3dReader::Test(int fd, const unsigned char* buf, int bufSize)
  {
    const char* kHeader = "{my3d}";
    const int kHeaderLen = strlen(kHeader);

    // Check that the buffer starts with our header.
    return (bufSize >= kHeaderLen && strncmp(kHeader, (const char*)buf, kHeaderLen) == 0);
  }


  //
  // my3dReader static variables
  //

  GeoDescription my3dReader::description("my3d\0", my3dReader::Build, buildformat, my3dReader::Test);

} // namespace my3d

