//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the 
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
//
#ifndef TRIANGLE_HPP
#define TRIANGLE_HPP

/**
 * \file Triangle.hpp
 *
 * Used to index triangles on a VV mesh
 */
#include <Config.hpp>
#include <Types.hpp>
#include <Information.hpp>

namespace  mgx
{
  /**
   * class Triangle Triangle.hpp <Triangle.hpp>
   *
   * A class to index triangles from a VV mesh. This class rearranges the order to 
   * put the lowest (by v.id()) vertex first, but preserves the orientation.
   */ 
  struct Triangle
  {
    // Default constructor
    Triangle() //: v{vertex(0),vertex(0),vertex(0)}
    {
      // Any way not to create them in the first place? need {0,0,0} initializer
      for(int i = 0; i < 3; ++i)
        v[i] = vertex(0);
    }

    // Constructor
    Triangle(vertex v0, vertex v1, vertex v2)
    {
      //if(v0.id() < v1.id() and v0.id() < v2.id()) {
      if(v0 < v1 and v0 < v2) {
        v[0] = v0; v[1] = v1; v[2] = v2;
      } else if(v1 < v0 and v1 < v2) {
      //} else if(v1.id() < v0.id() and v1.id() < v2.id()) {
        v[0] = v1; v[1] = v2; v[2] = v0;
      } else {
        v[0] = v2; v[1] = v0; v[2] = v1;
      }
    }

    // Template to load to reorder data to match triangle
    template<typename T>
    static bool setTriangleData(vertex v1, vertex v2, vertex v3, Triangle &t, T &V1, T &V2, T &V3)
    {
      if(v1 == t.v[1]) {
        std::swap(V2, V3);
        std::swap(V1, V2);
      } else if(v1 == t.v[2]) {
        std::swap(V1, V2);
        std::swap(V2, V3);
      }
      return true;
    }

    // Template to load to reorder data to match triangle
    template<typename T>
    static bool setTriangleData(vertex v1, vertex v2, vertex v3, Triangle &t, T V[])
    {
      return setTriangleData(v1, v2, v3, t, V[0], V[1], V[2]);
    }

    // == operator
    bool operator==(const Triangle &other) const 
    {
      return v[0] == other.v[0] and v[1] == other.v[1] and v[2] == other.v[2];
    }
 
    // < operator
    bool operator <(const Triangle &other) const
    {
      for(size_t i = 0; i < 3; ++i) {
        if(v[i] < other.v[i])
          return true;
        if(v[i] > other.v[i])
          return false;
      }
      return false;
    }

    // <= operator
    bool operator <=(const Triangle &other) const
    {
      for(size_t i = 0; i < 3; ++i) {
        if(v[i] < other.v[i])
          return true;
        if(v[i] > other.v[i])
          return false;
      }
      return true;
    }


    // Vertices of the triangle
    vertex v[3];
  };

  inline Triangle flipTri(const Triangle &t)
  {
    return Triangle(t.v[0], t.v[2], t.v[1]);
  };


  /// Map a triangle to a symmetric tensor
  typedef std::unordered_map<Triangle, SymmetricTensor> TriSymTensorMap;
  /// Element in TriSymTensorMap
  typedef std::pair<Triangle, SymmetricTensor> TriSymTensorPair;
}

// Define hash for Triangle for use in unordered containers
namespace std
{
  HASH_NS_ENTER
  // Hash Triangle
  template <> struct hash<mgx::Triangle>
  {
    size_t operator()(const mgx::Triangle &t) const
    {
      hash<mgx::vertex> h;
      return h(t.v[0]) ^ h(t.v[1]) ^ h(t.v[2]);
    }
  };
  HASH_NS_EXIT
}
#endif
