import 'package:collection/collection.dart';

/// Shorthand function to generate a new [Node].
Node h(String tagName,
        [Map<String, dynamic> attributes = const {},
        Iterable<Node> children = const []]) =>
    Node(tagName, attributes, children);

/// Represents an HTML node.
class Node {
  final String tagName;
  final Map<String, dynamic> attributes = {};
  final List<Node> children = [];

  Node(this.tagName,
      [Map<String, dynamic> attributes = const {},
      Iterable<Node> children = const []]) {
    this
      ..attributes.addAll(attributes)
      ..children.addAll(children);
  }

  Node._selfClosing(this.tagName,
      [Map<String, dynamic> attributes = const {}]) {
    this.attributes.addAll(attributes);
  }

  @override
  bool operator ==(other) {
    return other is Node &&
        other.tagName == tagName &&
        const ListEquality<Node>().equals(other.children, children) &&
        const MapEquality<String, dynamic>()
            .equals(other.attributes, attributes);
  }

  @override
  int get hashCode {
    int hash = Object.hash(tagName, Object.hashAll(children));
    return Object.hash(hash, Object.hashAll(attributes.values));
  }
}

/// Represents a self-closing tag, i.e. `<br>`.
class SelfClosingNode extends Node {
  /*
  @override
  final String tagName;

  @override
  final Map<String, dynamic> attributes = {};
  */

  @override
  List<Node> get children => List<Node>.unmodifiable([]);

  SelfClosingNode(tagName, [Map<String, dynamic> attributes = const {}])
      : super._selfClosing(tagName, attributes);
}

/// Represents a text node.
class TextNode extends Node {
  final String text;

  TextNode(this.text) : super(':text');

  @override
  bool operator ==(other) => other is TextNode && other.text == text;

  @override
  int get hashCode => text.hashCode;
}