// Copyright 2017 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef DRACO_SRC_DRACO_MESH_MESH_STRIPIFIER_H_
#define DRACO_SRC_DRACO_MESH_MESH_STRIPIFIER_H_

#include "draco/mesh/mesh_misc_functions.h"

namespace draco {

// Class that generates triangle strips from a provided draco::Mesh data
// structure. The strips represent a more memory efficient storage of triangle
// connectivity that can be used directly on the GPU (see
// https://en.wikipedia.org/wiki/Triangle_strip ). In general, a mesh needs to
// be represented by several triangle strips and it has been proven that finding
// the optimal set of triangle strips is an NP-complete problem. The algorithm
// implemented by this class finds this set of triangle strips based on a greedy
// heuristic that always selects the longest available strip that covers the
// next unprocessed face. The longest strip is found by analyzing all strips
// that can cover the given face (three strips corresponding to three
// directions).
class MeshStripifier {
 public:
  MeshStripifier()
      : mesh_(nullptr),
        num_strips_(0),
        num_encoded_faces_(0),
        last_encoded_point_(kInvalidPointIndex) {}

  // Generate triangle strips for a given mesh and output them to the output
  // iterator |out_it|. In most cases |out_it| stores the values in a buffer
  // that can be used directly on the GPU. Note that the algorithm can generate
  // multiple strips to represent the whole mesh. In such cases multiple strips
  // are separated using a so called primitive restart index that is specified
  // by the |primitive_restart_index| (usually defined as the maximum allowed
  // value for the given type).
  // https://www.khronos.org/opengl/wiki/Vertex_Rendering#Primitive_Restart
  template <typename OutputIteratorT, typename IndexTypeT>
  bool GenerateTriangleStripsWithPrimitiveRestart(
      const Mesh &mesh, IndexTypeT primitive_restart_index,
      OutputIteratorT out_it);

  // The same as above but disjoint triangle strips are separated by degenerate
  // triangles instead of the primitive restart index. Degenerate triangles are
  // zero area triangles that are automatically discarded by the GPU. Using
  // degenerate triangles usually results in a slightly longer output indices
  // array compared to the similar triangle strips that use primitive restart
  // index. The advantage of this method is that it is supported by all hardware
  // and all relevant APIs (including WebGL 1.0).
  template <typename OutputIteratorT>
  bool GenerateTriangleStripsWithDegenerateTriangles(const Mesh &mesh,
                                                     OutputIteratorT out_it);

  // Returns the number of strips generated by the last call of the
  // GenerateTriangleStrips() method.
  int num_strips() const { return num_strips_; }

 private:
  bool Prepare(const Mesh &mesh) {
    mesh_ = &mesh;
    num_strips_ = 0;
    num_encoded_faces_ = 0;
    // TODO(ostava): We may be able to avoid computing the corner table if we
    // already have it stored somewhere.
    corner_table_ = CreateCornerTableFromPositionAttribute(mesh_);
    if (corner_table_ == nullptr) {
      return false;
    }

    // Mark all faces as unvisited.
    is_face_visited_.assign(mesh.num_faces(), false);
    return true;
  }

  // Returns local id of the longest strip that can be created from the given
  // face |fi|.
  int FindLongestStripFromFace(FaceIndex fi) {
    // There are three possible strip directions that can contain the provided
    // input face. We try all of them and select the direction that result in
    // the longest strip.
    const CornerIndex first_ci = corner_table_->FirstCorner(fi);
    int longest_strip_id = -1;
    int longest_strip_length = 0;
    for (int i = 0; i < 3; ++i) {
      GenerateStripsFromCorner(i, first_ci + i);
      if (strip_faces_[i].size() > longest_strip_length) {
        longest_strip_length = static_cast<int>(strip_faces_[i].size());
        longest_strip_id = i;
      }
    }
    return longest_strip_id;
  }

  // Generates strip from the data stored in |strip_faces_| and
  // |strip_start_start_corners_| and stores it to |out_it|.
  template <typename OutputIteratorT>
  void StoreStrip(int local_strip_id, OutputIteratorT out_it) {
    ++num_strips_;

    const int num_strip_faces = strip_faces_[local_strip_id].size();
    CornerIndex ci = strip_start_corners_[local_strip_id];
    for (int i = 0; i < num_strip_faces; ++i) {
      const FaceIndex fi = corner_table_->Face(ci);
      is_face_visited_[fi] = true;
      ++num_encoded_faces_;

      if (i == 0) {
        // Add the start face (three indices).
        *out_it++ = CornerToPointIndex(ci).value();
        *out_it++ = CornerToPointIndex(corner_table_->Next(ci)).value();
        last_encoded_point_ = CornerToPointIndex(corner_table_->Previous(ci));
        *out_it++ = last_encoded_point_.value();
      } else {
        // Store the point on the newly reached corner.
        last_encoded_point_ = CornerToPointIndex(ci);
        *out_it++ = last_encoded_point_.value();

        // Go to the correct source corner to proceed to the next face.
        if (i & 1) {
          ci = corner_table_->Previous(ci);
        } else {
          ci = corner_table_->Next(ci);
        }
      }
      ci = corner_table_->Opposite(ci);
    }
  }

  PointIndex CornerToPointIndex(CornerIndex ci) const {
    return mesh_->CornerToPointId(ci);
  }

  // Returns the opposite corner in case the opposite triangle does not lie
  // across an attribute seam. Otherwise return kInvalidCornerIndex.
  CornerIndex GetOppositeCorner(CornerIndex ci) const {
    const CornerIndex oci = corner_table_->Opposite(ci);
    if (oci < 0) {
      return kInvalidCornerIndex;
    }
    // Ensure the point ids are same on both sides of the shared edge between
    // the triangles.
    if (CornerToPointIndex(corner_table_->Next(ci)) !=
        CornerToPointIndex(corner_table_->Previous(oci))) {
      return kInvalidCornerIndex;
    }
    if (CornerToPointIndex(corner_table_->Previous(ci)) !=
        CornerToPointIndex(corner_table_->Next(oci))) {
      return kInvalidCornerIndex;
    }
    return oci;
  }

  void GenerateStripsFromCorner(int local_strip_id, CornerIndex ci);

  const Mesh *mesh_;
  std::unique_ptr<CornerTable> corner_table_;

  // Store strip faces for each of three possible directions from a given face.
  std::vector<FaceIndex> strip_faces_[3];
  // Start corner for each direction of the strip containing the processed face.
  CornerIndex strip_start_corners_[3];
  IndexTypeVector<FaceIndex, bool> is_face_visited_;
  // The number of strips generated by this method.
  int num_strips_;
  // The number of encoded triangles.
  int num_encoded_faces_;
  // Last encoded point.
  PointIndex last_encoded_point_;
};

template <typename OutputIteratorT, typename IndexTypeT>
bool MeshStripifier::GenerateTriangleStripsWithPrimitiveRestart(
    const Mesh &mesh, IndexTypeT primitive_restart_index,
    OutputIteratorT out_it) {
  if (!Prepare(mesh)) {
    return false;
  }

  // Go over all faces and generate strips from the first unvisited one.
  for (FaceIndex fi(0); fi < mesh.num_faces(); ++fi) {
    if (is_face_visited_[fi]) {
      continue;
    }

    const int longest_strip_id = FindLongestStripFromFace(fi);

    // Separate triangle strips with the primitive restart index.
    if (num_strips_ > 0) {
      *out_it++ = primitive_restart_index;
    }

    StoreStrip(longest_strip_id, out_it);
  }

  return true;
}

template <typename OutputIteratorT>
bool MeshStripifier::GenerateTriangleStripsWithDegenerateTriangles(
    const Mesh &mesh, OutputIteratorT out_it) {
  if (!Prepare(mesh)) {
    return false;
  }

  // Go over all faces and generate strips from the first unvisited one.
  for (FaceIndex fi(0); fi < mesh.num_faces(); ++fi) {
    if (is_face_visited_[fi]) {
      continue;
    }

    const int longest_strip_id = FindLongestStripFromFace(fi);

    // Separate triangle strips by degenerate triangles. There will be either
    // three or four degenerate triangles inserted based on the number of
    // triangles that are already encoded in the output strip (three degenerate
    // triangles for even number of existing triangles, four degenerate
    // triangles for odd number of triangles).
    if (num_strips_ > 0) {
      // Duplicate last encoded index (first degenerate face).
      *out_it++ = last_encoded_point_.value();

      // Connect it to the start point of the new triangle strip (second
      // degenerate face).
      const CornerIndex new_start_corner =
          strip_start_corners_[longest_strip_id];
      const PointIndex new_start_point = CornerToPointIndex(new_start_corner);
      *out_it++ = new_start_point.value();
      num_encoded_faces_ += 2;
      // If we have previously encoded number of faces we need to duplicate the
      // point one more time to preserve the correct orientation of the next
      // strip.
      if (num_encoded_faces_ & 1) {
        *out_it++ = new_start_point.value();
        num_encoded_faces_ += 1;
      }
      // The last degenerate face will be added implicitly in the StoreStrip()
      // function below as the first point index is going to be encoded there
      // again.
    }

    StoreStrip(longest_strip_id, out_it);
  }

  return true;
}

}  // namespace draco

#endif  // DRACO_SRC_DRACO_MESH_MESH_STRIPIFIER_H_
