#!/usr/bin/env -S LD_PRELOAD=/usr/lib/libjemalloc.so /usr/bin/python3.13
#
# Copyright 2016 Pixar
#
# Licensed under the terms set forth in the LICENSE.txt file available at
# https://openusd.org/license.
#

from __future__ import print_function

import argparse, os, sys
from pxr import UsdUtils, Sdf, Tf 

parser = argparse.ArgumentParser( \
            prog=os.path.basename(sys.argv[0]),
            description='Stitch multiple usd file(s) together '
                        'into one using value clips.\n'
                        'An example command is:\n\n'
                        'usdstitchclips --out result.usd --clipPath '
                        '/World/fx/Particles_Splash clip1.usd clip2.usd '
                        '\n\n'
                        'This will produce two files, a result.topology.usd '
                        'and a result.usd.',
            formatter_class=argparse.RawDescriptionHelpFormatter)

parser.add_argument('usdFiles', nargs='+')
parser.add_argument('-o', '--out', action='store',
                    help='specify the filename for the top-level result file, which also serves as base-name for the topology and manifest files.')
parser.add_argument('-c', '--clipPath', action='store',
                    help='specify a prim path at which to begin stitching clip data.')
parser.add_argument('-s', '--startTimeCode', action='store',
                    help='specify the time at which the clips will become active')
parser.add_argument('-r', '--stride', action='store',
                    help='specify a stride for the numeric component of filenames for template metadata')
parser.add_argument('-e', '--endTimeCode', action='store',
                    help='specify the time at which the clips will cease being active')
parser.add_argument('-t', '--templateMetadata', action='store_true',
                    help='author template clip metadata in the root layer.')
parser.add_argument('-p', '--templatePath', action='store',
                    help='specify a template asset path to author') 
parser.add_argument('--clipSet', action='store',
                    help='specify a named clipSet in which to author clip metadata, so that multiple sets of clips can be applied on the same prim.')
parser.add_argument('--activeOffset', action='store', required=False,
                    help='specify an offset for template-based clips, offsetting the frame number of each clip file.')
parser.add_argument('--interpolateMissingClipValues', action='store_true',
                    help=('specify whether values for clips without authored '
                          'samples are interpolated from surrounding clips '
                          'if no default value is authored in any clip.'))
# useful for debugging with diffs
parser.add_argument('-n', '--noComment', action='store_true',
                    help='do not write a comment specifying how the usd file was generated')
results = parser.parse_args()

# verify the integrity of the inputs
assert results.out is not None, "must specify output file(--out)"
assert results.clipPath is not None, "must specify a clip path(--clipPath)"
assert results.usdFiles is not None, "must specify clip files"

if os.path.isfile(results.out):
    print("Warning: merging with current result layer")

outLayerGenerated = False
topologyLayerGenerated = False
topologyLayerName = ""

try:
    outLayer = Sdf.Layer.FindOrOpen(results.out)
    if not outLayer:
        outLayerGenerated = True
        outLayer = Sdf.Layer.CreateNew(results.out)

    topologyLayerName = UsdUtils.GenerateClipTopologyName(results.out)
    topologyLayer = Sdf.Layer.FindOrOpen(topologyLayerName)
    if not topologyLayer:
        topologyLayerGenerated = True
        topologyLayer = Sdf.Layer.CreateNew(topologyLayerName)

    manifestLayerName = UsdUtils.GenerateClipManifestName(results.out)
    manifestLayer = Sdf.Layer.FindOrOpen(manifestLayerName)
    if not manifestLayer:
        manifestLayerGenerated = True
        manifestLayer = Sdf.Layer.CreateNew(manifestLayerName)

    if results.startTimeCode:
        results.startTimeCode = float(results.startTimeCode)

    if results.endTimeCode:
        results.endTimeCode = float(results.endTimeCode)

    if results.stride:
        results.stride = float(results.stride)

    if results.activeOffset:
        results.activeOffset = float(results.activeOffset)

    if results.templateMetadata:
        def _checkMissingTemplateArg(argName, argValue):
            if not argValue:
                raise Tf.ErrorException('Error: %s must be specified '
                                        'when --templateMetadata is' % argName)

        _checkMissingTemplateArg('templatePath', results.templatePath)
        _checkMissingTemplateArg('endTimeCode', results.endTimeCode)
        _checkMissingTemplateArg('startTimeCode', results.startTimeCode)
        _checkMissingTemplateArg('stride', results.stride)

        UsdUtils.StitchClipsTopology(topologyLayer, results.usdFiles)
        UsdUtils.StitchClipsManifest(manifestLayer, topologyLayer, 
                                     results.usdFiles, results.clipPath)
        UsdUtils.StitchClipsTemplate(outLayer, 
                                     topologyLayer,
                                     manifestLayer,
                                     results.clipPath,
                                     results.templatePath,
                                     results.startTimeCode,
                                     results.endTimeCode,
                                     results.stride,
                                     results.activeOffset,
                                     results.interpolateMissingClipValues,
                                     results.clipSet)
    else:
        if results.templatePath:
            raise Tf.ErrorException('Error: templatePath cannot be specified '
                                    'without --templateMetadata')
        if results.activeOffset:
            raise Tf.ErrorException('Error: activeOffset cannot be specified '
                                    'without --templateMetadata')
        if results.stride:
            raise Tf.ErrorException('Error: stride cannot be specified '
                                    'without --templateMetadata')

        UsdUtils.StitchClips(outLayer, results.usdFiles, results.clipPath, 
                             results.startTimeCode, results.endTimeCode,
                             results.interpolateMissingClipValues,
                             results.clipSet)


    if not results.noComment:
        outLayer.comment = 'Generated with ' + ' '.join(sys.argv)
        outLayer.Save()

except Tf.ErrorException as e:
    # if something in the authoring fails, remove the output file 
    if outLayerGenerated and os.path.isfile(results.out):
        os.remove(results.out)
    if topologyLayerGenerated and os.path.isfile(topologyLayerName):
        os.remove(topologyLayerName)
    if manifestLayerGenerated and os.path.isfile(manifestLayerName):
        os.remove(manifestLayerName)
    sys.exit(e)
