import b from "btoa";
import { createElement as h } from "react";
import { styled } from "@patternplate/components";
import { DOMParser, XMLSerializer } from "xmldom";

const parser = new DOMParser();
const serializer = new XMLSerializer();

const TAG_NAMES = ["circle", "g", "path", "polygon", "rect", "svg"];

const ATTRIBUTES = {
  circle: ["cx", "cy", "r", "fill", "stroke", "style"],
  g: ["x", "y", "style"],
  path: ["d", "fill", "stroke", "style"],
  polygon: ["points", "fill", "stroke", "style"],
  rect: ["x", "y", "width", "height", "fill", "stroke", "style"],
  svg: ["width", "height", "viewBox", "x", "y", "style", "xmlns"]
};

function attributes(node, key) {
  return (ATTRIBUTES[node.tagName] || []).reduce(
    (props, name) => {
      const attribute = node.attributes.getNamedItem(name);
      if (attribute && attribute.specified) {
        props[name] = attribute.value;
      }
      return props;
    },
    { key }
  );
}

export function btoa(source) {
  return `data:image/svg+xml;base64,${b(source)}`;
}

export function parse(source) {
  const doc = parser.parseFromString(source, "image/svg+xml");
  const parsed = Array.prototype.slice.call(doc.childNodes).find(node => node.tagName === "svg");
  parsed.setAttribute("xmlns", "http://www.w3.org/2000/svg");
  return parsed;
}

export function png(source) {
  return new Promise((resolve, reject) => {
    const img = new global.Image();
    const canvas = global.document.createElement("canvas");
    const ratio = global.devicePixelRatio || 1;

    canvas.width = 16 * ratio;
    canvas.height = 16 * ratio;

    const context = canvas.getContext("2d");

    img.onload = () => {
      context.drawImage(img, 0, 0);
      resolve(canvas.toDataURL("image/png"));
    };

    img.onerror = err => reject(err);
    img.src = btoa(source);
  });
}

export function purge(parsed) {
  return Array.prototype.slice.call(parsed)
    .filter(node => TAG_NAMES.includes(node.tagName))
    .map(node => {
      node.childNodes = purge(node.childNodes);

      const attributes = ATTRIBUTES[node.tagName] || [];

      for (let i = 0; i < node.attributes.length; i++) {
        const attribute = node.attributes[i];
        if (!attributes.includes(attribute.name)) {
          node.removeAttribute(attribute.name);
        }
      }

      return node;
    });
}

export function render(element) {
  const [tagName, props, children = []] = element;
  const { style, ...rest } = props;
  const tag = styled(tagName)`
    ${style};
  `;
  return h(tag, rest, children.map(c => render(c)));
}

export function sanitize(parsed) {
  return [...parsed].map((node, i) => [
    node.tagName,
    attributes(node, i),
    sanitize(node.childNodes)
  ]);
}

export function stringify(tree) {
  return serializer.serializeToString(tree);
}

@@ -160,16 +160,52 @@
 xmldom%22;
+%0Aimport %7B camelCase %7D from %22lodash%22;
 %0A%0Aconst 
@@ -345,107 +345,469 @@
 %5D;%0A%0A
-const ATTRIBUTES = %7B%0A  circle: %5B%22cx%22, %22cy%22, %22r%22, %22fill%22, %22stroke%22, %22style%22%5D,%0A  g: %5B%22x%22, %22y
+/**%0A * These attributes are valid on all SVG elements and accepted by this%0A * renderer.%0A * All attributes will be converted to their camelCase version.%0A * This allows using valid SVG strings. %0A * Extend this list to allow additional default SVG attributes.%0A * %0A * @type %7BArray%7D%0A */%0Aconst SHARED_ATTRIBUTES = %5B%22fill%22, %22stroke%22, %22stroke-width%22%5D;%0A%0Aconst ATTRIBUTES = %7B%0A  circle: %5B...SHARED_ATTRIBUTES, %22cx%22, %22cy%22, %22r%22, %22style%22%5D,%0A  g: %5B...SHARED_ATTRIBUTES, %22x
 %22, %22
-style
+y
 %22%5D,%0A
@@ -819,28 +819,32 @@
 h: %5B
-%22d%22, %22fill%22, %22stroke
+...SHARED_ATTRIBUTES, %22d
 %22, %22
@@ -868,55 +868,72 @@
 n: %5B
-%22points%22, %22fill%22, %22stroke%22, %22style%22%5D,%0A  rect: %5B
+...SHARED_ATTRIBUTES, %22points%22%5D,%0A  rect: %5B...SHARED_ATTRIBUTES, 
 %22x%22,
@@ -960,26 +960,8 @@
 ht%22,
- %22fill%22, %22stroke%22,
  %22st
@@ -1199,24 +1199,65 @@
 Item(name);%0A
+      const reactProp = camelCase(name);%0A
       if (at
@@ -1304,20 +1304,25 @@
   props%5B
-name
+reactProp
 %5D = attr

allow stroke-width in config.logo (#182)