// Copyright (c) 2021 The Foundry Visionmongers Ltd. All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//     * Neither the name of Foundry nor the names of its
//       contributors may be used to endorse or promote products derived from this
//       software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// This work is a C++ implementation compatible with version 1.2.8 of the Python-based Nuke gizmo
// authored at Psyop by Jonah Friedman and Andy Jones (see https://github.com/Psyop/Cryptomatte).

#ifndef ENCRYPTOMATTE_PLUGIN_H
#define ENCRYPTOMATTE_PLUGIN_H

#include "DDImage/MetaData.h"
#include "DDImage/Hash.h"
#include "DDImage/Iop.h"

namespace DD {
  namespace Image {
    class LayerI;
    class Row;
  }
}

namespace Foundry {
  namespace NukePlugins {

    class EncryptomattePlugin : public DD::Image::Iop
    {
    public:
      EncryptomattePlugin(Node* node);

    private:
      static const Description kDescription;
      const char* Class() const override;
      const char* node_help() const override;

      void in_channels(int input, DD::Image::ChannelSet& mask) const override;
      void knobs(DD::Image::Knob_Callback callback) override;
      const char* input_label(int input, char* buffer) const override;

      int knob_changed(DD::Image::Knob* k) override;
      void append(DD::Image::Hash& hash) override;
      bool updateUI(const DD::Image::OutputContext& context) override;
      void _validate(bool forReal) override;
      const DD::Image::MetaData::Bundle& _fetchMetaData(const char* searchKey) override;
      // TODO: Remove this once we figured out how to avoid the copies with the
      // Iop base class
      void pixel_engine(const DD::Image::Row& in, int y, int x, int r, DD::Image::ChannelMask, DD::Image::Row& out);
      void engine(int y, int x, int r, DD::Image::ChannelMask, DD::Image::Row &) override;

      // Get the standard Nuke cryptomatte sublayers from the selected
      // cryptomatte layer name.
      void setCryptomatteSublayers();
      bool isMatteNameManual() const;
      void checkLayerInitialised();

      // Returns the node name of the matte input.
      std::string getMatteName() const;

      // This method returns a data structure for the input row indexed by
      // the channels starting from zero.
      std::vector<const float*> createInputRowData(const DD::Image::Row& in, int x, int r);

      // This method returns a data structure for the output row indexed by
      // the channels starting from zero.
      std::vector<float*> createOutputRowData(DD::Image::Row& out, int x, int r);

      // Baked value for the matte name knob where the user inputs the desired
      // matte name.
      std::string _matteName;

      // Baked value for the hidden previous matte name. This is to help
      // deciding whether the matte name has changed.
      std::string _previousMatteName;

      // This is used for the auto-labeling feature whether it is auto or
      // manual.
      int _matteNameTypeIndex = 0;
      // This is used for the merge operation feature. This could be over or
      // under.
      int _mergeOperationIndex = 0;

      // Holds the metadata for the current plugin instance passed downstream.
      DD::Image::MetaData::Bundle _meta;

      // Helper member variable to decide whether the layer name has changed
      // since the last rendering.
      std::string _previousCryptoLayerName;
      // Check whether the Encryptomatte instance owns the currently selected
      // layer.
      bool _isOwnLayer = true;
      // When the layer selection does not change, it means the layer has been
      // initialised
      bool _isOwnLayerInitialised = false;

      // Auxilliary variable, set once per rendering in validate rather than
      // for each row, whether the merge operation is over or under
      bool _isOver;
      // Auxilliary variable, set once per rendering in validate rather than
      // for each row, holding the value of the calculated hash for the
      // currently input matte name in the corresponding knob.
      float _matteId;

      // Baked value for the currently selected cryptomatte layer name
      std::string _cryptoLayerName;
      // The underlying cryptomatte sublayers as standard Nuke layers for the
      // selected main cryptomatte layer.
      std::vector<DD::Image::LayerI*> _cryptomatteSublayers;

      // The depth of the cryptomatte layer. This means the number of
      // cryptomatte sublayers. Each sublayer can contain two objects, so the
      // maximum number of objects is this variable * 2 therefore.
      size_t _cryptoLayerDepth;
    };

  }
}

#endif