#!/usr/bin/env python
# -*- encoding: utf-8 -*-

# Copyright (c) 2012, tamanegi (tamanegi@users.sourceforge.jp)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

import sys
import copy

from vector3d import *

# Voxel data
# Each grid can have three types of values, namely, -1, 0, 1.
# -1 : grid has large negative value
#  0 : small absolute value
#  1 : grid has large positive value
# Internally, these values are stored as 1, 0, 2, in an array, respectively.

# a Voxel
class Voxel:
  # class-static variables
  # something lattice-vector-like object
  lattice = [ [0,0,0], [1,0,0], [0,0,1], [1,0,1], [0,1,0], [1,1,0], [0,1,1], [1,1,1] ]
  # list of tetrahedrons
  #tetrahedrons = [ [0,1,3,4], [0,2,3,4], [2,3,4,6], [1,3,4,5], [3,4,5,7], [3,4,6,7] ]
  tetrahedrons = [ [3,4,0,1], [3,4,0,2], [3,4,2,6], [3,4,1,5], [3,4,5,7], [3,4,6,7] ]
  # list of midpoints
  midpoints = [ [0,1], [0,2], [0,3], [1,2], [1,3], [2,3] ]
  def __init__( self ):
    # first 4 vertexes locates in the same square, last 4 vertexes do so
    #  z   2___3  6___7    y
    #  ^   |   |  |   |   /
    #  |   |___|  |___|  /
    #  |   0   1  4   5  -> x
    # 0-4, 1-5, 2-6, and 3-7 are also connected
    self.mydata = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
    # origin of the voxel; position of grid point 0
    self.origin = Vector3D( 0.0, 0.0, 0.0 )
    # grid size for each direction
    # note that a voxel is always cubic, the bond angles are always 90 degrees.
    self.grid = Vector3D( 1.0, 1.0, 1.0 )

  def __getitem__( self, index ):
    return self.mydata[index]

  def __setitem__( self, index, value ):
    self.mydata[index] = value

  # pos must be Vector3D type
  def setOrigin( self, pos ):
    self.origin = Vector3D( pos.x, pos.y, pos.z )

  def setGridLength( self, vec ):
    self.grid = Vector3D( vec.x, vec.y, vec.z )

  def setValueFromList( self, values ):
    # check length of the list
    if len( values ) != 8:
      print >> sys.stderr, "Error(Voxel): voxel data length must be 8."
      sys.exit(1)
    self.mydata = copy.copy( values )

  # generate XML data by marching tetrahedron method
  def generateXml( self, toWhat = sys.stdout, positive = True ):
    if positive:
      self.__generateXml( toWhat, 2 )
    else:
      self.__generateXml( toWhat, 1 )

  def __generateXml( self, toWhat, value ):
    for hedron in Voxel.tetrahedrons:
      self.__gen( toWhat, hedron, value )

  def __gen( self, toWhat, hedron, value ):
    dat = [ 0, 0, 0, 0 ]
    num = 0 # number of found points
    counter = 0
    for vertex in hedron:
      if self.mydata[vertex] == value:
        dat[counter] = 1
        num += 1
      counter += 1

    if num == 0 or num == 4:
      return

    pos = []
    #mplist = []
    for mp in Voxel.midpoints:
      if dat[mp[0]] != dat[mp[1]]:
        #mplist.append( mp )
        pos.append( self.__getMidpointPos( Voxel.lattice[hedron[mp[0]]], Voxel.lattice[hedron[mp[1]]] ) )

    if len(pos) == 3:
      # draw 0-1-2 trangles
      print "      <TRI p0=\"" + str(pos[0]) + "\" p1=\"" + str(pos[1]) + "\" p2=\"" + str(pos[2]) + "\" />"
    elif len(pos) == 4:
      print "      <TRI p0=\"" + str(pos[0]) + "\" p1=\"" + str(pos[1]) + "\" p2=\"" + str(pos[3]) + "\" />"
      print "      <TRI p0=\"" + str(pos[0]) + "\" p1=\"" + str(pos[3]) + "\" p2=\"" + str(pos[2]) + "\" />"
      #if self.__diagonal( mplist[0], mplist[1] ):
      #  print >> sys.stderr, "1"
      #  print "      <TRI p0=\"" + str(pos[0]) + "\" p1=\"" + str(pos[1]) + "\" p2=\"" + str(pos[2]) + "\" />"
      #  print "      <TRI p0=\"" + str(pos[0]) + "\" p1=\"" + str(pos[1]) + "\" p2=\"" + str(pos[3]) + "\" />"
      #elif self.__diagonal( mplist[0], mplist[2] ):
      #  print >> sys.stderr, "2"
      #  print "      <TRI p0=\"" + str(pos[0]) + "\" p1=\"" + str(pos[2]) + "\" p2=\"" + str(pos[1]) + "\" />"
      #  print "      <TRI p0=\"" + str(pos[0]) + "\" p1=\"" + str(pos[2]) + "\" p2=\"" + str(pos[3]) + "\" />"
      #elif self.__diagonal( mplist[0], mplist[3] ):
      #  print "      <TRI p0=\"" + str(pos[0]) + "\" p1=\"" + str(pos[1]) + "\" p2=\"" + str(pos[3]) + "\" />"
      #  print "      <TRI p0=\"" + str(pos[0]) + "\" p1=\"" + str(pos[3]) + "\" p2=\"" + str(pos[2]) + "\" />"
      #else:
      #  print >> sys.stderr, "Error: unexpected error."
      #  sys.exit(9)
    else:
      print >> sys.stderr, "Error: unexpected error in creating polygons."
      sys.exit(1)

  def __diagonal( self, m0, m1 ):
    if m0[0] == m1[0] or m0[0] == m1[1]:
      return False
    if m0[1] == m1[0] or m0[1] == m1[1]:
      return False
    return True

  def __getMidpointPos( self, vertex0, vertex1 ):
    x = 0.5 * self.grid.x * float( vertex0[0] + vertex1[0] )
    y = 0.5 * self.grid.y * float( vertex0[1] + vertex1[1] )
    z = 0.5 * self.grid.z * float( vertex0[2] + vertex1[2] )
    return self.origin + Vector3D( x, y, z )
