(function (global, factory) {
  if (typeof define === "function" && define.amd) {
    define(["exports", "three"], factory);
  } else if (typeof exports !== "undefined") {
    factory(exports, require("three"));
  } else {
    var mod = {
      exports: {}
    };
    factory(mod.exports, global.three);
    global.Node = mod.exports;
  }
})(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _three) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.Node = void 0;

  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

  function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

  function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }

  var Node = /*#__PURE__*/function () {
    function Node(type) {
      _classCallCheck(this, Node);

      this.uuid = _three.MathUtils.generateUUID();
      this.name = '';
      this.type = type;
      this.userData = {};
    }

    _createClass(Node, [{
      key: "analyze",
      value: function analyze(builder) {
        var settings = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
        builder.analyzing = true;
        this.build(builder.addFlow(settings.slot, settings.cache, settings.context), 'v4');
        builder.clearVertexNodeCode();
        builder.clearFragmentNodeCode();
        builder.removeFlow();
        builder.analyzing = false;
      }
    }, {
      key: "analyzeAndFlow",
      value: function analyzeAndFlow(builder, output) {
        var settings = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
        this.analyze(builder, settings);
        return this.flow(builder, output, settings);
      }
    }, {
      key: "flow",
      value: function flow(builder, output) {
        var settings = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
        builder.addFlow(settings.slot, settings.cache, settings.context);
        var flow = {};
        flow.result = this.build(builder, output);
        flow.code = builder.clearNodeCode();
        flow.extra = builder.context.extra;
        builder.removeFlow();
        return flow;
      }
    }, {
      key: "build",
      value: function build(builder, output, uuid) {
        output = output || this.getType(builder, output);
        var data = builder.getNodeData(uuid || this);

        if (builder.analyzing) {
          this.appendDepsNode(builder, data, output);
        }

        if (builder.nodes.indexOf(this) === -1) {
          builder.nodes.push(this);
        }

        if (this.updateFrame !== undefined && builder.updaters.indexOf(this) === -1) {
          builder.updaters.push(this);
        }

        return this.generate(builder, output, uuid);
      }
    }, {
      key: "generate",
      value: function
        /* builder, output, uuid, type, ns */
      generate() {// This method needs to be implemented in subclasses
      }
    }, {
      key: "getHash",
      value: function getHash() {
        var hash = '{';
        var prop, obj;

        for (prop in this) {
          obj = this[prop];

          if (obj instanceof Node) {
            hash += '"' + prop + '":' + obj.getHash() + ',';
          }
        }

        if (this.hashProperties) {
          for (var i = 0; i < this.hashProperties.length; i++) {
            prop = this.hashProperties[i];
            obj = this[prop];
            hash += '"' + prop + '":"' + String(obj) + '",';
          }
        }

        hash += '"id":"' + this.uuid + '"}';
        return hash;
      }
    }, {
      key: "appendDepsNode",
      value: function appendDepsNode(builder, data, output) {
        data.deps = (data.deps || 0) + 1;
        var outputLen = builder.getTypeLength(output);

        if (outputLen > (data.outputMax || 0) || this.getType(builder, output)) {
          data.outputMax = outputLen;
          data.output = output;
        }
      }
    }, {
      key: "setName",
      value: function setName(name) {
        this.name = name;
        return this;
      }
    }, {
      key: "getName",
      value: function
        /* builder */
      getName() {
        return this.name;
      }
    }, {
      key: "getType",
      value: function getType(builder, output) {
        return output === 'sampler2D' || output === 'samplerCube' ? output : this.type;
      }
    }, {
      key: "getJSONNode",
      value: function getJSONNode(meta) {
        var isRootObject = meta === undefined || typeof meta === 'string';

        if (!isRootObject && meta.nodes[this.uuid] !== undefined) {
          return meta.nodes[this.uuid];
        }
      }
    }, {
      key: "copy",
      value: function copy(source) {
        if (source.name !== undefined) this.name = source.name;
        if (source.userData !== undefined) this.userData = JSON.parse(JSON.stringify(source.userData));
        return this;
      }
    }, {
      key: "createJSONNode",
      value: function createJSONNode(meta) {
        var isRootObject = meta === undefined || typeof meta === 'string';
        var data = {};
        if (typeof this.nodeType !== 'string') throw new Error('Node does not allow serialization.');
        data.uuid = this.uuid;
        data.nodeType = this.nodeType;
        if (this.name !== '') data.name = this.name;
        if (JSON.stringify(this.userData) !== '{}') data.userData = this.userData;

        if (!isRootObject) {
          meta.nodes[this.uuid] = data;
        }

        return data;
      }
    }, {
      key: "toJSON",
      value: function toJSON(meta) {
        return this.getJSONNode(meta) || this.createJSONNode(meta);
      }
    }]);

    return Node;
  }();

  _exports.Node = Node;
  Node.prototype.isNode = true;
  Node.prototype.hashProperties = undefined;
});