/* Copyright (c) 2008-2021 the MRtrix3 contributors.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Covered Software is provided under this License on an "as is"
 * basis, without warranty of any kind, either expressed, implied, or
 * statutory, including, without limitation, warranties that the
 * Covered Software is free of defects, merchantable, fit for a
 * particular purpose or non-infringing.
 * See the Mozilla Public License v. 2.0 for more details.
 *
 * For more details, see http://www.mrtrix.org/.
 */

#include "gui/mrview/window.h"
#include "gui/mrview/tool/connectome/node.h"

#include "exception.h"
#include "types.h"
#include "gui/mrview/window.h"

namespace MR
{
  namespace GUI
  {
    namespace MRView
    {
      namespace Tool
      {




        Node::Node (const Eigen::Vector3f& com, const size_t vol, const size_t pixheight, const MR::Image<bool>& image) :
            centre_of_mass (com),
            volume (vol),
            mask (image),
            name (image.name()),
            size (1.0f),
            colour { 0.5f, 0.5f, 0.5f },
            alpha (1.0f),
            visible (true),
            pixmap (pixheight, pixheight)
        {
          pixmap.fill (QColor (128, 128, 128));
        }

        Node::Node () :
            centre_of_mass (),
            volume (0),
            size (0.0f),
            colour { 0.0f, 0.0f, 0.0f },
            alpha (0.0f),
            visible (false),
            pixmap (12, 12)
        {
          pixmap.fill (QColor (0, 0, 0));
        }











        Node::Mesh::Mesh (MR::Surface::Mesh& in) :
            count (3 * in.num_triangles())
        {
          GL::Context::Grab context;
          GL::assert_context_is_current();

          vector<float> vertices;
          vertices.reserve (3 * in.num_vertices());
          for (size_t v = 0; v != in.num_vertices(); ++v) {
            for (size_t axis = 0; axis != 3; ++axis)
              vertices.push_back (in.vert(v)[axis]);
          }
          vertex_buffer.gen();
          vertex_buffer.bind (gl::ARRAY_BUFFER);
          if (vertices.size())
            gl::BufferData (gl::ARRAY_BUFFER, vertices.size() * sizeof (float), &vertices[0], gl::STATIC_DRAW);

          if (!in.have_normals())
            in.calculate_normals();
          vector<float> normals;
          normals.reserve (3 * in.num_vertices());
          for (size_t n = 0; n != in.num_vertices(); ++n) {
            for (size_t axis = 0; axis != 3; ++axis)
              normals.push_back (in.norm(n)[axis]);
          }
          normal_buffer.gen();
          normal_buffer.bind (gl::ARRAY_BUFFER);
          if (normals.size())
            gl::BufferData (gl::ARRAY_BUFFER, normals.size() * sizeof (float), &normals[0], gl::STATIC_DRAW);

          vertex_array_object.gen();
          vertex_array_object.bind();
          vertex_buffer.bind (gl::ARRAY_BUFFER);
          gl::EnableVertexAttribArray (0);
          gl::VertexAttribPointer (0, 3, gl::FLOAT, gl::FALSE_, 0, (void*)(0));
          normal_buffer.bind (gl::ARRAY_BUFFER);
          gl::EnableVertexAttribArray (1);
          gl::VertexAttribPointer (1, 3, gl::FLOAT, gl::FALSE_, 0, (void*)(0));

          vector<unsigned int> indices;
          indices.reserve (3 * in.num_triangles());
          for (size_t i = 0; i != in.num_triangles(); ++i) {
            for (size_t v = 0; v != 3; ++v)
              indices.push_back (in.tri(i)[v]);
          }
          index_buffer.gen();
          index_buffer.bind();
          if (indices.size())
            gl::BufferData (gl::ELEMENT_ARRAY_BUFFER, indices.size() * sizeof (unsigned int), &indices[0], gl::STATIC_DRAW);
          GL::assert_context_is_current();
        }

        Node::Mesh::Mesh (Mesh&& that) :
            count (that.count),
            vertex_buffer (std::move (that.vertex_buffer)),
            normal_buffer (std::move (that.normal_buffer)),
            vertex_array_object (std::move (that.vertex_array_object)),
            index_buffer (std::move (that.index_buffer))
        {
          that.count = 0;
        }

        Node::Mesh::~Mesh()
        {
          GL::Context::Grab context;
          vertex_buffer.clear();
          normal_buffer.clear();
          vertex_array_object.clear();
          index_buffer.clear();
        }

        Node::Mesh& Node::Mesh::operator= (Node::Mesh&& that)
        {
          count = that.count; that.count = 0;
          vertex_buffer = std::move (that.vertex_buffer);
          normal_buffer = std::move (that.normal_buffer);
          vertex_array_object = std::move (that.vertex_array_object);
          index_buffer = std::move (that.index_buffer);
          return *this;
        }

        void Node::Mesh::render() const
        {
          assert (count);
          GL::assert_context_is_current();
          vertex_buffer.bind (gl::ARRAY_BUFFER);
          normal_buffer.bind (gl::ARRAY_BUFFER);
          vertex_array_object.bind();
          index_buffer.bind();
          gl::DrawElements (gl::TRIANGLES, count, gl::UNSIGNED_INT, (void*)0);
          GL::assert_context_is_current();
        }






      }
    }
  }
}





