// Copyright 2017 The Draco Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
'use_strict';

const assert = require('assert');
const draco = require('./draco-animation');
const decoderModule = draco.createDecoderModule({});
const encoderModule = draco.createEncoderModule({});

function generateAnimationData(numKeyframes, numAnimations, numComponents) {
  const timestamps = new Float32Array(numKeyframes);

  const keyframes = new Array(numAnimations);
  for (let i = 0; i < numAnimations; ++i) {
    keyframes[i] = new Float32Array(numKeyframes * numComponents[i]);
  }

  for (let frameId = 0; frameId < numKeyframes; ++frameId) {
    timestamps[frameId] = frameId;
    for (let keyframeId = 0; keyframeId < numAnimations; ++keyframeId) {
      for (let j = 0; j < numComponents[keyframeId]; ++j) {
        // Set an arbitrary deterministic value.
        keyframes[keyframeId][frameId * numComponents[keyframeId] + j] =
            (frameId * numComponents[keyframeId] + j) * (keyframeId + 1);
      }
    }
  }

  const animation = {
    timestamps : timestamps,
    keyframes : keyframes
  };

  return animation;
}

function encodeAnimation(animation) {
  const encoder = new encoderModule.AnimationEncoder();
  const animationBuilder = new encoderModule.AnimationBuilder();
  const dracoAnimation = new encoderModule.KeyframeAnimation();

  // Add timestamps to animation.
  const numKeyframes = animation.timestamps.length;
  assert(animationBuilder.SetTimestamps(dracoAnimation, numKeyframes,
        animation.timestamps));

  // Add keyframes.
  for (let keyframeId = 0; keyframeId < animation.keyframes.length;
      ++keyframeId) {
    const numComponents =
        animation.keyframes[keyframeId].length / animation.timestamps.length;
    assert(animationBuilder.AddKeyframes(dracoAnimation, numKeyframes,
          numComponents, animation.keyframes[keyframeId]) == keyframeId + 1);
  }

  const encodedData = new encoderModule.DracoInt8Array();
  encoder.SetTimestampsQuantization(16);
  encoder.SetKeyframesQuantization(16);
  console.log("Quantized timestamps to 16 bits.");
  console.log("Quantized keyframes to 16 bits.");
  const encodedLen = encoder.EncodeAnimationToDracoBuffer(dracoAnimation,
                                                          encodedData);

  encoderModule.destroy(dracoAnimation);
  encoderModule.destroy(animationBuilder);
  encoderModule.destroy(encoder);

  assert(encodedLen > 0, "Failed encoding.");
  console.log("Encoded size: " + encodedLen);

  const outputBuffer = new ArrayBuffer(encodedLen);
  const outputData = new Int8Array(outputBuffer);
  for (let i = 0; i < encodedLen; ++i) {
    const data0 = encodedData.GetValue(i);
    outputData[i] = encodedData.GetValue(i);
  }
  encoderModule.destroy(encodedData);

  return outputBuffer;
}

function decodeAnimation(encodedData) {
  const buffer = new decoderModule.DecoderBuffer();
  buffer.Init(new Int8Array(encodedData), encodedData.byteLength);
  const decoder = new decoderModule.AnimationDecoder();

  const dracoAnimation = new decoderModule.KeyframeAnimation();
  decoder.DecodeBufferToKeyframeAnimation(buffer, dracoAnimation);
  assert(dracoAnimation.ptr != 0, "Invalid animation.");


  const numKeyframes = dracoAnimation.num_frames();

  const timestampAttData = new decoderModule.DracoFloat32Array();
  assert(decoder.GetTimestamps(dracoAnimation, timestampAttData));
  const timestamps = new Float32Array(numKeyframes);
  for (let i = 0; i < numKeyframes; ++i) {
      timestamps[i] = timestampAttData.GetValue(i);
  }

  const numAnimations = dracoAnimation.num_animations();
  const keyframes = new Array(numAnimations);
  for (let keyframeId = 0; keyframeId < numAnimations; ++keyframeId) {
    const animationAttData = new decoderModule.DracoFloat32Array();
    // The id of keyframe attribute starts at 1.
    assert(decoder.GetKeyframes(dracoAnimation, keyframeId + 1,
          animationAttData));
    keyframes[keyframeId] = new Float32Array(animationAttData.size());
    for (let i = 0; i < animationAttData.size(); ++i) {
      keyframes[keyframeId][i] = animationAttData.GetValue(i);
    }
  }

  const animation = {
    timestamps : timestamps,
    keyframes : keyframes
  }

  console.log("Decoding successful.");

  return animation;
}

function compareAnimation(animation, decodedAnimation) {
  console.log("Comparing decoded animation data...");
  assert(animation.timestamps.length == decodedAnimation.timestamps.length);
  assert(animation.keyframes.length == decodedAnimation.keyframes.length);

  console.log("Done.");
}

// Create animation with 50 frames and two animations.
// The first animation has 3 components and the second has 4 components.
const animation = generateAnimationData(100, 2, [3, 4]);
const encodedData = encodeAnimation(animation);

const decodedAnimation = decodeAnimation(encodedData);
compareAnimation(animation, decodedAnimation);