"use strict";

var _classProps = function (child, staticProps, instanceProps) {
  if (staticProps) Object.defineProperties(child, staticProps);
  if (instanceProps) Object.defineProperties(child.prototype, instanceProps);
};

// Recursivly clone objects
var clone = function (obj, parent) {
  if (typeof (obj) != "object") return obj;
  var cloned = new obj.constructor();

  for (var name in obj) {
    if (!obj.hasOwnProperty(name)) continue;
    var value = obj[name];

    if (name == "parent" && typeof (value) == "object") {
      if (parent) cloned[name] = parent;
    } else if (name == "source") {
      cloned[name] = value;
    } else if (value instanceof Array) {
      cloned[name] = value.map(function (i) {
        return clone(i, cloned);
      });
    } else {
      cloned[name] = clone(value, cloned);
    }
  }

  return cloned;
};

// Some common methods for all CSS nodes
var Node = (function () {
  var Node = function Node(defaults) {
    if (defaults === undefined) defaults = {};
    for (var name in defaults) {
      this[name] = defaults[name];
    }
  };

  _classProps(Node, null, {
    removeSelf: {
      writable: true,


      // Remove this node from parent
      //
      //   decl.removeSelf();
      //
      // Note, that removing by index is faster:
      //
      //   rule.each( (decl, i) => rule.remove(i) );
      value: function () {
        if (this.parent) {
          this.parent.remove(this);
        }
        return this;
      }
    },
    replace: {
      writable: true,


      // Shortcut to insert nodes before and remove self.
      //
      //   importNode.replace( loadedRoot );
      value: function (nodes) {
        this.parent.insertBefore(this, nodes);
        this.parent.remove(this);
        return this;
      }
    },
    toString: {
      writable: true,


      // Return CSS string of current node
      //
      //   decl.toString(); //=> "  color: black"
      value: function () {
        var result = "";
        var builder = function (str) {
          return result += str;
        };
        this.stringify(builder);
        return result;
      }
    },
    clone: {
      writable: true,


      // Clone current node
      //
      //   rule.append( decl.clone() );
      //
      // You can override properties while cloning:
      //
      //   rule.append( decl.clone({ value: '0' }) );
      value: function (overrides) {
        if (overrides === undefined) overrides = {};
        var cloned = clone(this);
        for (var name in overrides) {
          cloned[name] = overrides[name];
        }
        return cloned;
      }
    },
    toJSON: {
      writable: true,


      // Remove `parent` node on cloning to fix circular structures
      value: function () {
        var fixed = {};

        for (var name in this) {
          if (!this.hasOwnProperty(name)) continue;
          if (name == "parent") continue;
          var value = this[name];

          if (value instanceof Array) {
            fixed[name] = value.map(function (i) {
              return (typeof (i) == "object" && i.toJSON) ? i.toJSON() : i;
            });
          } else if (typeof (value) == "object" && value.toJSON) {
            fixed[name] = value.toJSON();
          } else {
            fixed[name] = value;
          }
        }

        return fixed;
      }
    },
    styleMap: {
      writable: true,
      value: function () {
        return {};
      }
    },
    style: {
      writable: true,


      // Copy code style from first node with same type
      value: function (name) {
        // Already had
        var value = this.styleMap()[name];
        if (typeof (value) != "undefined") return value;

        var parent = this.parent;

        // Hack for first rule in CSS
        if (!parent && (name == "beforeRule" || name == "beforeDecl")) {
          return "";
        }
        if (name == "beforeRule" && !parent.parent && parent.first == this) {
          return "";
        }

        // Floating child without parent
        if (!parent) return this.defaultStyle[name];

        // Detect style by other nodes
        var root = parent;
        while (root.parent) root = root.parent;

        if (!root.styleCache) root.styleCache = {};
        if (typeof (root.styleCache[name]) != "undefined") {
          return root.styleCache[name];
        }

        root.eachInside(function (other) {
          value = other.styleMap()[name];
          if (typeof (value) != "undefined") return false;
        });
        if (typeof (value) == "undefined") value = this.defaultStyle[name];

        root.styleCache[name] = value;
        return value;
      }
    },
    stringifyRaw: {
      writable: true,


      // Use raw value if origin was not changed
      value: function (prop) {
        var value = this[prop];
        var raw = this["_" + prop];
        if (raw && raw.value === value) {
          return raw.raw;
        } else {
          return value;
        }
      }
    }
  });

  return Node;
})();

// Default code style
Node.prototype.defaultStyle = {
  colon: ": ",
  beforeDecl: "\n    ",
  commentLeft: " ",
  commentRight: " ",
  beforeRule: "\n",
  beforeOpen: " ",
  beforeClose: "\n",
  emptyBody: ""
};

module.exports = Node;