# $Id: edge.py,v 1.3 2002/09/20 03:52:16 culler Exp $
#   t3m - software for studying triangulated 3-manifolds
#   Copyright (C) 2002 Marc Culler, Nathan Dunfield and others
#
#   This program is distributed under the terms of the
#   GNU General Public License, version 2 or later, as published by
#   the Free Software Foundation.  See the file GPL.txt for details.

from .simplex import *
from .corner import Corner
from .arrow import Arrow
from .perm4 import Perm4
import sys

# A table used for _add_corner, equivalent to:
# other_arrow = arrow.copy().opposite()
# tail, head = other_arrow.tail(), other_arrow.head()

_edge_add_corner_dict = {}
for edge, face in EdgeFacePairs:
    other_arrow = Arrow(edge, face, None).opposite()
    _edge_add_corner_dict[edge, face] = other_arrow.tail(), other_arrow.head()


# An edge has an initial and terminal vertex, but these are determined
# arbitrarily when the 1-skeleton is constructed.

class Edge:

    def __init__(self):
        self.Index = -1
        self.Name = ''
        self.IntOrBdry = ''         # value: '', 'int' or 'bdry'
        self.Corners = []           # Corners of type "1-simplex in Tetrahedron"
        self.Vertices = []          # pairs: (initial Vertex, terminal Vertex)
        self.LeftBdryArrow = None   # Arrows representing the two boundary faces,
        self.RightBdryArrow = None  # if this is a boundary edge.
        self._edge_orient_cache = {}

    def __repr__(self):
        if self.Index > -1:
            return ('e' + str(self.Index) + self.Name +
                    ' (' + self.IntOrBdry + ')')
        else:
            return '< floating edge' + str(id(self)) + ' >'

    # below added by NMD for more detailed printing

    # returns an arrow rotating around self

    def get_arrow(self):
        e = self.Corners[0].Subsimplex
        return Arrow(e, RightFace[e], self.Corners[0].Tetrahedron)

    def info(self, out=sys.stdout):
        out.write(repr(self) + "\t Edge of valence %d\tEndpoints %s\n"
                  % (self.valence(), self.Vertices))
        if self.IntOrBdry == 'bdry':
            a = self.LeftBdryArrow.copy()
            a.reverse()
        else:
            a = self.get_arrow()
        s = "\t"
        for i in range(self.valence()):
            s = s + repr(a) + "  "
            a.next()
            if i > 0 and (i + 1) % 3 == 0 and i != (self.valence()-1):
                s = s + "\n\t"
        out.write(s + '\n')

    def valence(self):
        return len(self.Corners)

# Return 1 if all corners belong to distinct tetrahedra.
    def distinct(self):
        for corner in self.Corners:
            corner.Tetrahedron.Checked = 0
        for corner in self.Corners:
            if corner.Tetrahedron.Checked == 1:
                return 0
            else:
                corner.Tetrahedron.Checked = 1
        return 1

# Return 1 if two sides of a 2-simplex are identified to the edge.
    def self_adjacent(self):
        for corner in self.Corners:
            for one_subsimplex in AdjacentEdges[corner.Subsimplex]:
                if corner.Tetrahedron.Class[one_subsimplex] is self:
                    return 1
        return 0

# Return 1 if two opposite edges of a 3-simplex are identified to
# the edge.
    def self_opposite(self):
        count = 0
        for corner in self.Corners:
            if corner.Tetrahedron.Class[comp(corner.Subsimplex)] == self:
                count = count + 1
        return count/2

# Remove all references to self from adjoining Tetrahedra and Vertices
    def erase(self):
        for corner in self.Corners:
            corner.Tetrahedron.Class[corner.Subsimplex] = None
        for vertex in self.Vertices:
            try:
                vertex.Edges.remove(self)
            except:
                pass
        self.Index = -1

    # Below added 7/6/99 by NMD.  Given a tetrahedra and a pair of vertices
    # (a, b) returns the orientation of the edge self with respect to the arrow
    # (a -> b).  Returns 1 if the orientations agree and -1 if they differ.
    # raises an exception if the arrow is not on this edge.

    def orientation_with_respect_to(self, tet, a, b):
        try:
            return self._edge_orient_cache[(tet, a, b)]
        except IndexError:
            raise ValueError("Given corner of tet not on this edge")

    def index(self):
        return self.Index

    def _first_embedding(self):
        """
        For this edge, return an edge embedding similar
        to regina, that is a pair (tetrahedron, permutation) such that
        vertex 0 and 1 of the tetrahedron span the edge.
        """

        corner = self.Corners[0]
        tet = corner.Tetrahedron

        for perm in Perm4.A4():
            if corner.Subsimplex == perm.image(E01):
                # Match orientation of edge given by Vertices
                if tet.Class[perm.image(V0)] == self.Vertices[0]:
                    if tet.Class[perm.image(V1)] == self.Vertices[1]:
                        return (tet, perm)

    def embeddings(self):
        """
        Iterator through the embeddings of this edge.
        An edge embedding is a pair (tetrahedron, permutation) such that
        vertices of the tetrahedron that are labeled by the images of 0 and 1
        under the permutation span the edge. The images of 2 and 3 of the edge
        embeddings are in an orientation compatible way.
        This is similar to the NEdgeEmbeddings of regina.
        """

        order = len(self.Corners)
        tet, perm = self._first_embedding()
        for i in range(order):
            yield tet, perm
            face = perm.image(F2)
            tet, perm = (
                tet.Neighbor[face], tet.Gluing[face] * perm * Perm4( (0,1,3,2) ))

    def _add_corner(self, arrow):
        """
        Used by Mcomplex.build_edge_classes
        """
        self.Corners.append(Corner(arrow.Tetrahedron, arrow.Edge))
        # Next line equivalent to:
        # other_arrow = arrow.copy().opposite()
        # tail, head = other_arrow.tail(), other_arrow.head()
        tail, head = _edge_add_corner_dict[arrow.Edge, arrow.Face]
        self._edge_orient_cache[arrow.Tetrahedron, tail, head] = 1
        self._edge_orient_cache[arrow.Tetrahedron, head, tail] = -1
