#!/usr/bin/env python

#   cr2_parser, Poser file pseudo-parser; python module.
#   Copyright (C) 2006  Frederick Lee <phaethon@linux.ucla.edu>
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA


PACKAGE="cr2_parser"
VERSION="0.01"


#Dumb parser version
#Relies on the tendancy of cr2 files to put "{" and "}" on lines of their own.
#Relies on newline appearing to be significant.

#Collects entire contents of cr2 into "blocks", which are just classes for now.

#Primary target: create armature skeleton

"""
Information from http://home.carolina.rr.com/kattman/Tutorials.htm
"""




debug=0

def READING (msg):
  if debug:
    print "READING", msg

def ENDREAD (msg):
  if debug:
    print "ENDREAD", msg




class cr2:
#  class version:
#  class figure:
#    class material:
#    class inkyChain:
#  class actor:
#    class channels:  #aka dials.
#      class keys:
  class version:
    def __init__ (self):  #version
      self.number = None
    def load (self, fileobj):
      s = fileobj.readline().strip()
      w = s.split()
      depth = 0
      while s==s and (depth > 0 or s != "}"):
        if len(w) < 1:
          w = ("",)
        if s == "{":
          depth += 1
        elif w[0] == "number":  #The only valid key known.
          self.number = w[1]
        s = fileobj.readline().strip()
        w = s.split()
        if s == "}":
          depth -= 1
      return

  class figure:
    class material:
      def __init__ (self):  #material
        pass
      def load (self, fileobj):
        #Expect { first.
        READING("material")
        s = fileobj.readline().strip()
        w = s.split()
        depth = 0
        while s==s and (depth > 0 or s != "}"):
          if len(w) < 1:
            w = ("",)
          if s == "{":
            depth += 1
          #TODO:
          """
        KdColor <float> <float> <float> <float>
        KaColor <float> <float> <float> <float>
        KsColor <float> <float> <float> <float>
        TextureColor <float> <float> <float> <float>
        NsExponent <int>
        tMin <float>
        tMax <float>
        tExpo <float>
        bumpStrength <float>
        ksIgnoreTexture <int>
        reflectThruLights <int>
        reflectThruKd <int>
        textureMap NO_MAP
        bumpMap NO_MAP
        reflectionMap NO_MAP
        transparencyMap NO_MAP
        ReflectionColor <float> <float> <float> <float>
        reflectionStrength <float>
"""
          s = fileobj.readline().strip()
          w = s.split()
          if s == "}":
            depth -= 1
        ENDREAD("material")
        return
      pass

    class inkyChain:
      def __init__ (self):  #inkyChain
        pass
      def load (self, fileobj):
        #Expect { first.
        s = fileobj.readline().strip()
        w = s.split()
        depth = 0
        while s==s and (depth > 0 or s != "}"):
          if len(w) < 1:
            w = ("",)
          if s == "{":
            depth += 1
          #TODO:
          """
        {
        on
        name <string>
        addLink <string:bone_name>
        addLink <string:bone_name>
        goal <string:bone_name>
        linkWeight <int> <float>
        linkWeight <int> <float>
        }
"""
          s = fileobj.readline().strip()
          w = s.split()
          if s == "}":
            depth -= 1
        return
      pass

    def __init__ (self):  #figure
      self.name = ""
      self.root = None
      self.parenting = {}  #parenting[child] = parent
      self.materials = {}
      self.inkyChains = {}
      self.defaultPick = None
      self.displayOn = 1
      self.welding = {}  #welding[child] = parent
      self.allowBending = 1
      self.figureType = 0
      self.canonType = 0
      self.conforming = 1
      self.displayMode = None
      self.locked = 0

    def load (self, fileobj):
      #Expect { first.
      READING("figure")
      s = fileobj.readline().strip()
      w = s.split()
      depth = 0
      while s==s and (depth > 0 or s != "}"):
        if len(w) < 1:
          w = ("",)
        if s == "{":
          depth += 1
        elif w[0] == "name":
          w = s.split(' ',1)
          self.name = w[1]
        elif w[0] == "addChild":
          child = w[1]
          s = fileobj.readline().strip()
          par = s
          self.parenting[child] = par
        elif w[0] == "weld":
          child = w[1]
          s = fileobj.readline().strip()
          par = s
          self.welding[child] = par
        elif w[0] == "inkyChain":
          ic = self.inkyChain()
          ic.name = w[1]
          ic.load(fileobj)
          self.inkyChains[ic.name] = ic
        elif (w[0] == "material"):
          m = self.material()
          m.name = w[1]
          m.load(fileobj)
          self.inkyChains[m.name] = m
        elif w[0] in ["root", "defaultPick", "displayMode"]:
          #Misc. string crap.
          self.__dict__[w[0]] = w[1]
        elif w[0] in ["displayOn", "allowBending", "figureType", "canonType", "conforming", "locked"]:
          #Misc integer crap.
          self.__dict__[w[0]] = int(w[1])
        s = fileobj.readline().strip()
        w = s.split()
        if s == "}":
          depth -= 1
      ENDREAD("figure")
      return self.name

  class actor:
    class channelset:  #aka dials.
      class dial:
        class key:
          def __init__ (self):  #key
            self.k = (0, 0)
            self.static = 0

          def load (self, fileobj):
            #Expect { first.
            READING("keys")
            s = fileobj.readline().strip()
            w = s.split()
            depth = 0
            while s==s and (depth > 0 or s != "}"):
              if len(w) < 1:
                w = ("",)
              if s == "{":
                depth += 1
              elif w[0] == "k":
                self.k = (float(w[1]), float(w[2]))
              elif w[0] == "static":
                self.static = int(w[1])
              s = fileobj.readline().strip()
              w = s.split()
              if s == "}":
                depth -= 1
            ENDREAD("keys")
            return

        class deltaset:
          def __init__ (self, size):  #deltaset
            self.d = [None] * size
            pass

          def load (self, fileobj):
            #Expect { first.
            READING("deltas")
            s = fileobj.readline().strip()
            w = s.split()
            depth = 0
            while s==s and (depth > 0 or s != "}"):
              if len(w) < 1:
                w = ("",)
              if s == "{":
                depth += 1
              elif w[0] == 'd':
                n = int(w[1])
                (x, y, z) = (w[2], w[3], w[4])
                self.d[n] = (x,y,z)
              s = fileobj.readline().strip()
              w = s.split()
              if s == "}":
                depth -= 1
            ENDREAD("deltas")
            return

        def __init__ (self):  #dial
          self.name = ""
          self.initvalue = 0
          self.hidden = 0
          self.forceLimits = 0
          self.min = 0
          self.max = 0
          self.trackingScale = 1
          self.keys = []
          self.deltaAddDelta = 1.0  #Some kind of scaling factor?
          self.indexes = 0  #Number of vertices that get changed in this delta
          self.numbDeltas = 0  #Total number of vertices available.
          self.angles = ()
          self.otherActor = None
          self.matrixActor = None
          self.center = ()
          self.flipped = 0
          self.sphereMatsRaw = []
          self.doBulge = 0
          self.posBulgeLeft = 0
          self.posBulgeRight = 0
          self.negBulgeLeft = 0
          self.negBulgeRight = 0
          self.jointMult = 0
          self.calcWeights = 0

        def load (self, fileobj):
          #Expect { first.
          READING("dial")
          s = fileobj.readline().strip()
          w = s.split()
          depth = 0
          while s==s and (depth > 0 or s != "}"):
            if len(w) < 1:
              w = ("",)
            if s == "{":
              depth += 1
            elif w[0] == "name":
              w = s.split(' ', 1)
              self.name = w[1]
            elif w[0] == "keys":
              k = self.key()
              k.load(fileobj)
              self.keys.append(k)
            elif w[0] == "deltaAddDelta":
              self.deltaAddDelta = float(w[1])
            elif w[0] == "indexes":
              self.indexes = int(w[1])
            elif w[0] == "numbDeltas":
              self.numbDeltas = int(w[1])
            elif w[0] == "deltas":
              deltaforce = self.deltaset(self.numbDeltas)
              deltaforce.load(fileobj)
              self.deltas = deltaforce
            elif w[0] == "sphereMatsRow":
              for i in xrange(0, 8):
                t = fileobj.readline().strip()
                v = map(float, t.split())
                self.sphereMatsRow.append(v)
            elif w[0] in ("flipped", "calcWeights"):
              #Misc. flag crap.
              self.__dict__[w[0]] = 1
            elif w[0] in ("otherActor", "MatrixActor"):
              #Misc. string crap.
              self.__dict__[w[0]] = w[1]
            elif w[0] in ("hidden", "forceLimits", "interpStyleLocked", "doBulge", "jointMult"):
              #Misc. integer crap.
              self.__dict__[w[0]] = int(w[1])
            elif w[0] in ("initValue", "min", "max", "trackingScale", "staticValue", "posBulgeLeft", "posBulgeRight", "negBulgeLeft", "negBulgeRight"):
              #Misc. float crap.
              self.__dict__[w[0]] = float(w[1])
            s = fileobj.readline().strip()
            w = s.split()
            if s == "}":
              depth -= 1
          ENDREAD("dial")
          return

      def __init__ (self):  #channelset
        self.dials = {}
        self.deltas = None
        pass

      def load (self, fileobj):
        #Expect { first.
        READING("channels")
        s = fileobj.readline().strip()
        w = s.split()
        depth = 0
        while s==s and (depth > 0 or s != "}"):
          if len(w) < 1:
            w = ("",)
          if s == "{":
            depth += 1
          elif len(w) > 1:  #Only "<type> <name>" statements allowed.
            dial = self.dial()
            dial.name = w[1]
            dial.load(fileobj)
            self.dials[dial.name] = dial
          s = fileobj.readline().strip()
          w = s.split()
          if s == "}":
            depth -= 1
        ENDREAD("channels")
        return

    def __init__ (self):  #actor
      self.name = ""
      self.bend = 1
      self.dynamicsLock = 0
      self.hidden = 0
      self.addToMenu = 0
      self.castsShadow = 1
      self.includeDepthCue = 1
      self.parent = None
      self.channels = self.channelset()
      self.displayOrigin = 0
      self.displayMode = None
      self.customMaterial = 0
      self.locked = 0
      pass

    def load (self, fileobj):
      #Expect { first.
      READING("actor %s" % self.name)
      s = fileobj.readline().strip()
      w = s.split()
      depth = 0
      while s==s and (depth > 0 or s != "}"):
        if len(w) < 1:
          w = ("",)
        if s == "{":
          depth += 1
        elif w[0] == "channels":
          self.channels.load(fileobj)
        elif w[0] == "on":
          pass
        elif w[0] == "off":
          pass
        elif w[0] == "parent":
          self.parent = w[1]
        elif w[0] == "displayMode":
          self.displayMode = w[1]
        elif w[0] in ("bend", "dynamicsLock", "hidden", "addToMenu", "castsShadow", "includeInDepthCue", "displayOrigin", "customMaterial", "locked"):
          #Misc. integer crap.
          self.__dict__[w[0]] = int(w[1])
        elif w[0] in ("endPoint", "origin", "orientation"):
          #Misc. coordinate crap.
          (x, y, z) = (float(w[1]), float(w[2]), float(w[3]))
          self.__dict__[w[0]] = (x, y, z)
        s = fileobj.readline().strip()
        w = s.split()
        if s == "}":
          depth -= 1
      ENDREAD("actor %s" % self.name)
      return self.name

  def __init__ (self): #cr2
    self.name = ""
    self.ver = self.version()
    self.figures = {}
    self.actors = {}
    self.figureResFile = None

  def load (self, fileobj):
    READING("cr2")
    s = fileobj.readline().strip()
    w = s.split()
    depth = 0
    while s==s and (depth > 0 or s != "}"):
#      print "s='%s'" % s
      if len(w) < 1:
         w = ("",)
      if s == "{":
        depth += 1
      elif w[0] == "version":
        self.ver.load(fileobj)
      elif w[0] == "figure":
        figure = self.figure()
        name = figure.load(fileobj)
        self.figures[name] = figure
      elif w[0] == "actor":
        actor = self.actor()
        actor.name = w[1]
        name = actor.load(fileobj)
        self.actors[name] = actor
      elif w[0] == "figureResFile":
        self.figureResFile = w[1]
      elif w[0] == "setGeomHandlerOffset":
        (x, y, z) = (w[1], w[2], w[3])
        (x, y, z) = (float(x), float(y), float(z))
        self.setGeomHandlerOffset = (x, y, z)
      else:
#        print "Unknown keyword", w[0], "at", fileobj.tell()
#        print s
        pass
      s = fileobj.readline().strip()
      w = s.split()
      if s == "}":
        depth -= 1
#      print "depth=%d" % depth, "s='%s'" % s
    ENDREAD("cr2")
    return self.name

  def test (self):
    f = open("test.cr2", "rt")
    self.load(file2(f))
    f.close()
    print self.__dict__

  def trace_import (self):
    print self.figures
    figure = self.figures[self.figures.keys()[0]]
    print "Expected number of bones:", len(self.actors.keys())
    keylist = self.actors.keys()
    for i in xrange(0, len(keylist)):
      bonename = keylist[i]
      actor = self.actors[bonename]
      print "Bone %d: '%s'" % (i, bonename)
      print " base is at", actor.origin, "extends to", actor.endPoint
      if figure.welding.has_key(bonename):
        print " parent to '%s'" % (figure.welding[bonename],)
    pass


class file2:
  def __init__ (self, origfile):
    self.origfile = origfile
    self.linecount = 0

  def readline (self):
    self.linecount += 1
    return self.origfile.readline()
  def tell (self):
    return self.linecount


if __name__ == "__main__":
  x = cr2()
  x.test()
  x.trace_import()

