# This command should be called before doing a mentalray render.
# It creates the necessary geoshader shadow objects under the xgmr namespace.

import sys
import os.path
import maya.api.OpenMaya as om
import maya.OpenMayaAnim as oma
import maya.cmds as cmds
import maya.mel as mel
from xgenm import XgExternalAPI as xgapi
import xgenm as xg
from xgenm.ui.widgets import *
from xgenm.ui.util.xgUtil import *
from xgenm.ui.tabs.xgRendererTab import *
from xgenm.ui.util.xgProgressBar import setProgressInfo
import ctypes
import types

k_RenderAPIRenderer = "Renderman"
k_RenderAPIRendererObj = k_RenderAPIRenderer + "Renderer"

def castSelf(selfid):
    # Can't pass self as an object.
    # It's cast to id(self) by the caller
    # and we convert it back to a python object here
    if isinstance(selfid,str):
        return ctypes.cast( int(selfid), ctypes.py_object ).value
    else:
        return selfid

def addMethod( self, method ):
    self.__dict__[method.__name__] = types.MethodType( method, self, xg.ui.tabs.RendermanRendererTabUI )
#
# RenderAPI RendererTab UI callbacks

# RenderAPIRendererTabUIInit callback
# Called at the end of RenderAPIRendererTab.__init__()
def xgmrUI(selfid):
    self = castSelf(selfid)

    # Extend the RenderAPIRendererTab instance with some of our methods
    addMethod( self, xgmrRefresh )
    addMethod( self, xgmrSplineDegreeChanged )
    addMethod( self, xgmrRenderModeChanged )
    addMethod( self, xgmrApproxTypeChanged )
    addMethod( self, xgmrAutoGeoshadersChanged )
    addMethod( self, xgmrMotionBlurModeChanged )
    addMethod( self, xgmrMotionBlurChanged )

    expand = ExpandUI(_L10N( kMentalRaySettings, "mental ray Settings" ))
    self.mr_expand_settings = expand
    self.layout().addWidget( expand )

    # Horizontal layout
    row = QtGui.QWidget()
    hbox = QtGui.QHBoxLayout()
    hbox.setSpacing(3)
    hbox.setContentsMargins(1,1,1,1)
    label = QtGui.QLabel(_L10N( kMentalrayGeoshaders, "Setup GeoShaders" ))
    label.setFixedWidth(labelWidth())
    label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
    label.setIndent(10)
    label.setToolTip(_L10N( kMentalrayGeoshadersAnn, "Setup a mental ray geometry shader used to generate the XGen render time geometry.\nBy default, the setup is automatically done when a new description is created.\nYou can perform the setup manually for the current Description by clicking the Setup button." ))
    hbox.addWidget(label)
    self.mr_autocreateGeoshaders = QtGui.QComboBox()
    self.mr_autocreateGeoshaders.setFixedWidth(120)
    self.mr_autocreateGeoshaders.addItem(_L10N( kMentalrayGeoshadersAuto, 'Automatically' ) )
    self.mr_autocreateGeoshaders.addItem(_L10N( kMentalrayGeoshadersManual, 'Manually' ) )
    self.mr_autocreateGeoshaders.setToolTip( label.toolTip() )
    self.connect(self.mr_autocreateGeoshaders, QtCore.SIGNAL("activated(int)"), self.xgmrAutoGeoshadersChanged )
    hbox.addWidget(self.mr_autocreateGeoshaders)

    self.mr_setup_geoshaders = QtGui.QPushButton()
    self.mr_setup_geoshaders.setText(_L10N( kSetupGeometryShaders, 'Setup' ))
    self.mr_setup_geoshaders.setFixedWidth(80)
    self.mr_setup_geoshaders.setToolTip( label.toolTip() )
    self.connect(self.mr_setup_geoshaders, QtCore.SIGNAL("clicked()"),
                 lambda: mel.eval("xgmr -setupGeometryShader -description \"%s\" -palette \"%s\"" % (xgg.DescriptionEditor.currentDescription(), xgg.DescriptionEditor.currentPalette() )))
    hbox.addWidget(self.mr_setup_geoshaders)

    hbox.addStretch()
    row.setLayout(hbox)
    expand.addWidget(row)

    # Horizontal layout
    row = QtGui.QWidget()
    hbox = QtGui.QHBoxLayout()
    hbox.setSpacing(3)
    hbox.setContentsMargins(1,1,1,1)
    label = QtGui.QLabel(_L10N( kMentalRayRenderMode, "Render Mode" ))
    label.setFixedWidth(labelWidth())
    label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
    label.setIndent(10)
    label.setToolTip(_L10N( kMentalRayRenderModeAnn, "'Live' allows XGen to query patches and guide animation directly from Maya.\nThis mode doesn't support Motion Blur and will try to reuse information the preview generated.\n\n'Batch Render' mode will always read geometry and animation from files.\n'Batch Render' mode is always on when doing a Batch Render.\nYou must first export the patches and guide animation to caches.\nUse this mode to make sure your caches are properly exported before doing a Batch Render." ))
    hbox.addWidget(label)
    self.mr_rendermode = QtGui.QComboBox()
    self.mr_rendermode.setFixedWidth( 120 )
    self.mr_rendermode.addItem(_L10N( kRenderModeLive, 'Live' ), "1" )
    self.mr_rendermode.addItem(_L10N( kRenderModeBatch, 'Batch Render' ), "3" )
    self.mr_rendermode.setToolTip(label.toolTip())
    self.connect(self.mr_rendermode , QtCore.SIGNAL("activated(int)"), self.xgmrRenderModeChanged )
    hbox.addWidget(self.mr_rendermode)
    filler = QtGui.QWidget()
    hbox.addWidget(filler)
    row.setLayout(hbox)
    expand.addWidget(row)

    # Horizontal layout
    row = QtGui.QWidget()
    hbox = QtGui.QHBoxLayout()
    hbox.setSpacing(3)
    hbox.setContentsMargins(1,1,1,1)
    label = QtGui.QLabel(_L10N( kTools, "Tools" ))
    label.setFixedWidth(labelWidth())
    label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
    label.setIndent(10)
    hbox.addWidget(label)
    
    self.mr_apply_hair_shader = QtGui.QPushButton()
    self.mr_apply_hair_shader.setText(_L10N( kMentalRayApplyHair, 'Apply Hair Shader' ))
    self.mr_apply_hair_shader.setToolTip(_L10N( kMentalRayApplyHairAnn, "Apply default mental ray XGen Hair Shader to the current Description.\nThe shader reacts to 'root_color', 'tip_color' and 'back_color' XGen custom shader parameters." ))
    self.connect(self.mr_apply_hair_shader, QtCore.SIGNAL("activated(int)"),
                 lambda: mel.eval("xgmr -applyShader \"hair\" -description \"%s\" -palette \"%s\"" % (xgg.DescriptionEditor.currentDescription(), xgg.DescriptionEditor.currentPalette )))
    hbox.addWidget(self.mr_apply_hair_shader)

    filler = QtGui.QWidget()
    hbox.addWidget(filler)
    row.setLayout(hbox)
    expand.addWidget(row)

    filler = QtGui.QWidget()
    hbox.addWidget(filler)
    row.setLayout(hbox)
    expand.addWidget(row)

    expand = ExpandUI(_L10N( kMentalRayApproxSetttings, "mental ray Approximation Settings" ))
    self.layout().addWidget( expand )
    self.mr_expand_approx_settings = expand

    # Horizontal layout
    row = QtGui.QWidget()
    hbox = QtGui.QHBoxLayout()
    hbox.setSpacing(3)
    hbox.setContentsMargins(1,1,1,1)
    label = QtGui.QLabel(_L10N( kSplineDegree, "Degree" ))
    label.setFixedWidth(labelWidth())
    label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
    label.setIndent(10)
    label.setToolTip(_L10N( kSplineDegreeAnn, "Spline Primitives can be rendered as linear segments or as cubic bezier curves.\nSet to Cubic for Card Primiives.\nHas no effect on Archives and Sphere Primitves." ))
    hbox.addWidget(label)
    self.mr_splinedegree = QtGui.QComboBox()
    self.mr_splinedegree.addItem(_L10N( kSplineDegreeLinear, 'Linear' ), "1" )
    self.mr_splinedegree.addItem(_L10N( kSplineDegreeCubic, 'Cubic' ), "3" )
    self.mr_splinedegree.setToolTip(label.toolTip())
    self.connect(self.mr_splinedegree, QtCore.SIGNAL("activated(int)"), self.xgmrSplineDegreeChanged )
    hbox.addWidget(self.mr_splinedegree)
    filler = QtGui.QWidget()
    hbox.addWidget(filler)
    row.setLayout(hbox)
    expand.addWidget(row)

    # Horizontal layout
    row = QtGui.QWidget()
    hbox = QtGui.QHBoxLayout()
    hbox.setSpacing(3)
    hbox.setContentsMargins(1,1,1,1)
    label = QtGui.QLabel(_L10N( kApproximationMethod, "Type" ))
    label.setFixedWidth(labelWidth())
    label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
    label.setIndent(10)
    label.setToolTip(_L10N( kApproximationMethodAnn, "Type of mental ray approximation to use when subdividing render time hair bezier curves or card NURBS patches.\n'No Approximation' won't subdivide the render time geometry.\n'Parametric Approximation' subdivides render time geometry a number of times between control points.\n'Fine Approximation' subdivides until edges hit a given length in pixel size. It's view dependent." ))
    hbox.addWidget(label)
    self.mr_approx_type = QtGui.QComboBox()
    self.mr_approx_type.addItem(_L10N( kApproxNo, 'No Approximation' ), "0")
    self.mr_approx_type.addItem(_L10N( kApproxParametric, 'Parametric Approximation' ), "1")
    self.mr_approx_type.addItem(_L10N( kApproxFine, 'Fine Approximation' ), "2")
    self.mr_approx_type.setToolTip(label.toolTip())
    self.connect(self.mr_approx_type, QtCore.SIGNAL("activated(int)"), self.xgmrApproxTypeChanged )
    hbox.addWidget(self.mr_approx_type)
    filler = QtGui.QWidget()
    hbox.addWidget(filler)
    row.setLayout(hbox)
    expand.addWidget(row)
    self.mr_approx_type_row = row

    self.mr_approx_parametric = IntegerUI( "custom__mr_approx_parametric",
                                _L10N( kParametricSubdivisionsAnn, "For Linear, defines the number of segments to use between control points.\nFor Cubic, defines a recursive number of subdivisions between control points." ),
                                k_RenderAPIRendererObj,0,99,_L10N( kParametricSubdivisions, "Subdivisions" ), autoPlayblast=False)
    expand.addWidget(self.mr_approx_parametric)
    self.mr_approx_parametric.xgAttrChanged.connect( self.xgmrRefresh )

    self.mr_approx_fine = FloatUI( "custom__mr_approx_fine",
                                _L10N( kSubPixelSizeAnn, "Subdivides geometry until edges hit a maximal length in pixel size.\nIt is view and resolution dependent.\nValues below 1.0 will split the geometry within the same pixel.\nThis is usefull to render very thin hair with tube normals and a high number of samples.\nValues above 1.0 will produce geometry that don't look perfectly smooth.\nIt's usefull to optimize render time if the geometry is far away from the camera." ),
                                k_RenderAPIRendererObj, 0.0, 1000.0 , 0.0, 1.0, _L10N( kSubPixelSize, "Sub Pixel Size" ), autoPlayblast=False)
    expand.addWidget(self.mr_approx_fine)
    self.mr_approx_fine.xgAttrChanged.connect( self.xgmrRefresh )


    expand = ExpandUI(_L10N( kMentalRayMotionBlurSetttings, "mental ray Motion Blur Settings" ))
    self.mr_expand_motion_blur_settings = expand
    self.layout().addWidget( expand )

    self.mr_motion_blur = CheckBoxUI(_L10N( kMentalRayEnableMotionBlur, "Enable Motion Blur" ),"custom__mr_motion_blur",
                                _L10N( kMentalRayEnableMotionBlurAnn, "Enable Motion Blur.\nTo compute the motion vectors, XGen needs to sample animated geometry and guide animations at multiple times.\nMake sure you export everything like for a Batch render." ),
                                k_RenderAPIRendererObj)
    self.connect(self.mr_motion_blur.boxValue[0], QtCore.SIGNAL("clicked(bool)"), self.xgmrMotionBlurChanged )
    expand.addWidget(self.mr_motion_blur)

    self.mr_motion_blur_steps = IntegerUI( "custom__mr_motion_blur_steps",
                                _L10N( kMentalRayMotionBlurStepsAnn, "Defines the number of times the XGen geometry is evaluated to produced non-linear motion blur.\nA lower value makes the render go faster but the motion blur effect might not capture the rotation of teh primitives." ),
                                k_RenderAPIRendererObj,1,15,_L10N( kMentalRayMotionBlurSteps, "Motion Steps" ), autoPlayblast=False)
    expand.addWidget(self.mr_motion_blur_steps)
    self.mr_motion_blur_steps.xgAttrChanged.connect( self.xgmrRefresh )

    self.mr_motion_blur_factor = FloatUI( "custom__mr_motion_blur_factor",
                                _L10N( kMentalRayMotionBlurFactorAnn, "Motion Blur time interval." ),
                                k_RenderAPIRendererObj, 0.0, 100.0 , 0.0, 2.0, _L10N( kMentalRayMotionBlurFactor, "Motion Blur by" ), autoPlayblast=False)
    expand.addWidget(self.mr_motion_blur_factor)
    self.mr_motion_blur_factor.xgAttrChanged.connect( self.xgmrRefresh )

    self.mr_motion_blur_multiplier = FloatUI( "custom__mr_motion_blur_multiplier",
                                _L10N( kMentalRayMotionBlurMultiplierAnn, "Motion Factor multiplies the computed motion vectors.\nUse it to attenuate or accentuate the motion blur effect." ),
                                k_RenderAPIRendererObj, 0.0, 100.0 , 0.0, 2.0, _L10N( kMentalRayMotionBlurMultiplier, "Motion Factor" ), autoPlayblast=False)
    expand.addWidget(self.mr_motion_blur_multiplier)
    self.mr_motion_blur_multiplier.xgAttrChanged.connect( self.xgmrRefresh )

    # Horizontal layout
    row = QtGui.QWidget()
    hbox = QtGui.QHBoxLayout()
    hbox.setSpacing(3)
    hbox.setContentsMargins(1,1,1,1)
    label = QtGui.QLabel(_L10N( kMotionBlur, "Keyframe Location" ))
    label.setFixedWidth(labelWidth())
    label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
    label.setIndent(10)
    label.setToolTip(_L10N( kMentalRayKeyframeLocationAnn, "The motion offset when calculating motion blur." ))
    hbox.addWidget(label)
    self.mr_motion_blur_mode = QtGui.QComboBox()
    self.mr_motion_blur_mode.addItem(_L10N( kMentalRayKeyframeLocationStart, 'Start of Frame' ), "0")
    self.mr_motion_blur_mode.addItem(_L10N( kMentalRayKeyframeLocationMiddle, 'Middle of Frame' ), "1")
    self.mr_motion_blur_mode.addItem(_L10N( kMentalRayKeyframeLocationEnd, 'End of Frame' ), "2")
    self.mr_motion_blur_mode.setToolTip(label.toolTip())
    self.connect(self.mr_motion_blur_mode, QtCore.SIGNAL("activated(int)"), self.xgmrMotionBlurModeChanged )
    hbox.addWidget(self.mr_motion_blur_mode)
    filler = QtGui.QWidget()
    hbox.addWidget(filler)
    row.setLayout(hbox)
    expand.addWidget(row)

    # Register the mental ray renderer in the method combo box
    self.addRenderer("mental ray")

# RenderAPIRendererTabUIRefresh callback
# Called at the end of RenderAPIRendererTab.refresh()
def xgmrRefresh(selfid):

    self = castSelf(selfid)

    vis = self.renderer.currentText()=="mental ray"
    self.mr_expand_settings.setVisible(vis)
    self.mr_expand_motion_blur_settings.setVisible(vis)
    self.mr_expand_approx_settings.setVisible(vis)

    # Declare the mental ray custom parameters
    self.declareCustomAttr( 'mr_rendermode', "0" )
    self.declareCustomAttr( 'mr_splinedegree', "1" )
    self.declareCustomAttr( 'mr_approx_type', "1" )
    self.declareCustomAttr( 'mr_approx_fine', "0.1" )
    self.declareCustomAttr( 'mr_approx_parametric', "3" )
    self.declareCustomAttr( 'mr_motion_blur', "0" )
    self.declareCustomAttr( 'mr_motion_blur_mode', "1" )
    self.declareCustomAttr( 'mr_motion_blur_steps', "5" )
    self.declareCustomAttr( 'mr_motion_blur_factor', "1.0" )
    self.declareCustomAttr( 'mr_motion_blur_multiplier', "1.0" )
    
    # Get all the values
    rendermode = int(self.getCustomAttr( "mr_rendermode" ))
    deg = int(self.getCustomAttr( "mr_splinedegree" ))
    typ = int(self.getCustomAttr( "mr_approx_type" ))
    fine = float(self.getCustomAttr( "mr_approx_fine" ))
    subdi = int(self.getCustomAttr( "mr_approx_parametric" ))
    mb = self.getCustomAttr( "mr_motion_blur" ) != "0"
    mb_mode = int(self.getCustomAttr( "mr_motion_blur_mode" ))
    mb_steps = int(self.getCustomAttr( "mr_motion_blur_steps" ))
    mb_factor = float(self.getCustomAttr( "mr_motion_blur_factor" ))
    mb_multiplier = float(self.getCustomAttr( "mr_motion_blur_multiplier" ))

    # Update the UI
    de = xgg.DescriptionEditor

    if de.autoCreateMR:
        self.mr_autocreateGeoshaders.setCurrentIndex( 0 )
    else:
        self.mr_autocreateGeoshaders.setCurrentIndex( 1 )

    if deg==3:
        self.mr_splinedegree.setCurrentIndex( 1 )
    else:
        self.mr_splinedegree.setCurrentIndex( 0 )

    self.mr_rendermode.setCurrentIndex( rendermode )
    self.mr_approx_type.setCurrentIndex( typ )
    self.mr_approx_parametric.refresh()
    self.mr_approx_fine.refresh()
    self.mr_motion_blur.refresh()
    self.mr_motion_blur_mode.setCurrentIndex( mb_mode )
    self.mr_motion_blur_steps.refresh()
    self.mr_motion_blur_factor.refresh()
    self.mr_motion_blur_multiplier.refresh()

    if deg>1:
        self.mr_approx_parametric.setVisible( typ==1 )
        self.mr_approx_fine.setVisible( typ==2 )
        self.mr_approx_type_row.setVisible( True )
    else:
        self.mr_approx_parametric.setVisible( True )
        self.mr_approx_fine.setVisible( False )
        self.mr_approx_type_row.setVisible( False )
        typ = 0

    self.mr_motion_blur_mode.setEnabled( mb )
    self.mr_motion_blur_steps.setEnabled( mb )
    self.mr_motion_blur_factor.setEnabled( mb )
    self.mr_motion_blur_multiplier.setEnabled( mb )
    
    pal = de.currentPalette()
    desc = de.currentDescription()
    
    
    # Update the exposed geoshader parameters
    if xgg.Maya:
        import maya.cmds as cmds
        relativeNodes = cmds.listRelatives(desc, children=True, type="transform")
        nExists = False
        for relativeNode in relativeNodes:
            if xg.stripNameSpace(str(relativeNode)) == "geoshader":
                nExistsName = pal + "|" + desc + "|" + relativeNode
                nExists = cmds.objExists( nExistsName )
        if nExists:
            cnx = cmds.listConnections(nExistsName + ".miGeoShader")
            if len(cnx)==1:
                cmds.setAttr( cnx[0] + ".approx_degree", deg )
                cmds.setAttr( cnx[0] + ".approx_mode", typ )
                cmds.setAttr( cnx[0] + ".approx_parametric_subdivisions", subdi )
                cmds.setAttr( cnx[0] + ".approx_fine_sub_pixel_size", fine )
                cmds.setAttr( cnx[0] + ".motion_blur", mb )
                cmds.setAttr( cnx[0] + ".motion_blur_mode", mb_mode )
                cmds.setAttr( cnx[0] + ".motion_blur_steps", mb_steps )
                cmds.setAttr( cnx[0] + ".motion_blur_factor", mb_factor )
                cmds.setAttr( cnx[0] + ".motion_blur_multiplier", mb_multiplier )
                #cmds.setAttr( cnx[0] + ".render_mode", rendermode )
				
                # update xg node name in xgen_geo.data
                dataStrings = cmds.getAttr( cnx[0] + ".data" ).split(" ")
                index = [i for i in range(0, len(dataStrings)) if dataStrings[i] == "-palette"]
                dataStrings[index[0] + 1] = pal
                index = [i for i in range(0, len(dataStrings)) if dataStrings[i] == "-description"]
                dataStrings[index[0] + 1] = desc
                xgenData = dataStrings[0]
                for i in range(1, len(dataStrings)):
                    xgenData += " " + dataStrings[i]
                cmds.setAttr( cnx[0] + ".data", xgenData, type="string" )
				
                xgenPatches = xg.objNameSpace(str(pal)) + xg.stripNameSpace(str(cmds.getAttr( cnx[0] + ".patches" )))
                cmds.setAttr( cnx[0] + ".patches", xgenPatches, type="string" )
				

# Callback after description creation to switch to mental ray render
def xgmrOnCreateDescription( param ):
    params = str(param).split(',')
    if len(params)==2:
        xg.setAttr( "renderer", "mental ray", params[1], params[0], "RendermanRenderer" )

def xgmrSplineDegreeChanged(self,index):
    if index==1:
        self.setCustomAttr( "mr_splinedegree", "3" )
    else:
        self.setCustomAttr( "mr_splinedegree", "1" )

    self.xgmrRefresh()

def xgmrRenderModeChanged(self,index):
    self.setCustomAttr( "mr_rendermode", str(index) )
    self.xgmrRefresh()

def xgmrApproxTypeChanged(self,index):
    self.setCustomAttr( "mr_approx_type", str(index) )
    self.xgmrRefresh()

def xgmrAutoGeoshadersChanged(self,index):
    xgg.DescriptionEditor.autoCreateMR = index==0
    xgg.DescriptionEditor.updateMentalrayControls()
    self.xgmrRefresh()

def xgmrMotionBlurModeChanged(self,index):
    self.setCustomAttr( "mr_motion_blur_mode", str(index) )
    self.xgmrRefresh()

def xgmrMotionBlurChanged(self,state):
    self.setCustomAttr( "mr_motion_blur", str(int(state)) )
    self.xgmrRefresh()


#-
# ==========================================================================
# Copyright (C) 2012 Autodesk, Inc. and/or its licensors.  All
# rights reserved.
#
# The coded instructions, statements, computer programs, and/or related
# material (collectively the "Data") in these files contain unpublished
# information proprietary to Autodesk, Inc. ("Autodesk") and/or its
# licensors, which is protected by U.S. and Canadian federal copyright
# law and by international treaties.
#
# The Data is provided for use exclusively by You. You have the right
# to use, modify, and incorporate this Data into other products for
# purposes authorized by the Autodesk software license agreement,
# without fee.
#
# The copyright notices in the Software and this entire statement,
# including the above license grant, this restriction and the
# following disclaimer, must be included in all copies of the
# Software, in whole or in part, and all derivative works of
# the Software, unless such copies or derivative works are solely
# in the form of machine-executable object code generated by a
# source language processor.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
# AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED
# WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF
# NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
# PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR
# TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS
# BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
# DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK
# AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY
# OR PROBABILITY OF SUCH DAMAGES.
#
# ==========================================================================






