/// <reference path="./utils/element-types.d.ts" />
/// <reference path="./utils/events.d.ts" />
/// <reference path="./utils/intrinsic-elements.d.ts" />

export interface Children {
  children?: AttributeValue;
}

export interface CustomElementHandler {
  (attributes: Attributes, contents: (string | HTMLElement)[]): HTMLElement;
}

export interface Attributes {
  [key: string]: AttributeValue;
}


export function createElement(
  tag: string | CustomElementHandler,
  attrs: Attributes & Children | undefined = {},
  ...children: (string | HTMLElement)[]
): HTMLElement {

  if (typeof tag === "function") {
    if (attrs == null) {
      attrs = { num: 0 };
    }
    if (children == null) {
      children = [""];
    }
    return tag(attrs, children);
  }

  const retElement = document.createElement(tag);

  for (let name in attrs) {
    if (name && attrs.hasOwnProperty(name)) {

      let value = attrs[name];
      if (typeof value === "number") {
        retElement.setAttribute(name, value.toString());
      } else if (typeof value === "function") {
        retElement.addEventListener(name.slice(2), value);
      }
      else {
        retElement.setAttribute(name, value);
      }
    }
  }

  for (let i = 2; i < arguments.length; i++) {
    let child = arguments[i];

    // check if child is a HTMLElement
    if (child.nodeType != undefined) {
      retElement.appendChild(child);
      continue;
    }

    if (child instanceof Array) {
      for (let j = 0; j < child.length; j++) {
        if (child[j].nodeType != undefined) retElement.appendChild(child[j]);
        else retElement.appendChild(document.createTextNode(child[j].toString()));
      }
      continue;
    }
    // child is a string
    retElement.appendChild(document.createTextNode(child.toString()));
  }
  return retElement;
}