"""
by K. Urner, 4D Solutions
Modified May 8, 2000:  added __repr__ (tested for Qvector compatibility)
"""

from coords import Vector
import math

class Quaternion:

    def __init__(self,s1,v1):
       # initialize a Quaternian as [s1,v1]
       self.s = float(s1)  # a scalar
       self.v = v1         # a Vector

    def __repr__(self):
       return "Quaternion " + str((self.s,self.v))

    def __add__(self,q1):
        return Quaternion(self.s + q1.s, self.v + q1.v)

    def __sub__(self,q1):
        return Quaternion(self.s - q1.s, self.v - q1.v)

    def __neg__(self):
        return Quaternion(-self.s, -self.v)
        
    def __mul__(self,q1):
        # multiply (self x quaternion q1) --> new quaternion or...
        # multiply (self x scalar) --> new quaternion
        
        typearg = type(q1).__name__ # to check argument type

        # check for Quaternion type
        if (typearg == 'instance'   
           and q1.__class__.__name__ == 'Quaternion'):
           # s1*s2 - v1.v2 where s1 is self.s, v1 is self.v
           new_s = self.s * q1.s - self.v.dot(q1.v)
           # (v1 x v2) + (s1*v2) + (s2*v1)
           new_v = self.v.cross(q1.v) + self.s*q1.v + q1.s*self.v
           return Quaternion(new_s, new_v)

        # check for scalar type
        if typearg in ['int','float','long int']:
           return Quaternion(q1*self.s,q1*self.v)

    __rmul__ = __mul__

    def __div__(self,s1):
        # divide self by scalar
        return Quaternion(self.s/s1,self.v/s1)
    
    def inv(self):
        # inverse of self
        return self.conj()/self.norm()

    def norm(self):
        # norm of self
        return self.s**2 + self.v.dot(self.v)

    def conj(self):
        # conjugate of self 
        return Quaternion(self.s,-self.v)

    
def rotate(v1,axis,alpha):
     # rotate vector v1 around axis 'X', 'Y' or 'Z', by alpha degrees
     qr = rotator(axis,alpha)
     # sandwich v1 as a Quaternion's vector part
     # between a rotator and its inverse
     new_q = qr * Quaternion(0,v1) * qr.inv()
     return new_q.v  # return just the vector part
        
def rotator(axis,alpha):
     # return quaternion for rotating around axis
     # by alpha degrees
     alpha = alpha * math.pi/180.0 # convert to radians
     ntuple = (axis=="X",axis=="Y",axis=="Z")
     v1 = math.sin(alpha/2.0) * Vector(ntuple)
     s1 = math.cos(alpha/2.0)
     return Quaternion(s1,v1)