import { IPlatform } from '../platform';
import { objectAssign } from '../utilities';
import { aliasRegistration, createInterface, singletonRegistration } from '../utilities-di';

import { createLookup, isString, resolve, type IContainer } from '@aurelia/kernel';
import type { INode } from '../dom';

export interface ISVGAnalyzer extends NoopSVGAnalyzer {}
export const ISVGAnalyzer = /*@__PURE__*/createInterface<ISVGAnalyzer>('ISVGAnalyzer', x => x.singleton(NoopSVGAnalyzer));

const o = (keys: string | string[]): Record<string, true | undefined> => {
  const lookup = createLookup<true | undefined>();
  keys = isString(keys) ? keys.split(' ') : keys;
  let key: string;
  for (key of keys) {
    lookup[key] = true;
  }
  return lookup;
};
export class NoopSVGAnalyzer {
  public isStandardSvgAttribute(_node: INode, _attributeName: string): boolean {
    return false;
  }
}

export class SVGAnalyzer {
  public static register(container: IContainer): void {
    container.register(
      singletonRegistration(this, this),
      aliasRegistration(this, ISVGAnalyzer)
    );
  }

  /** @internal */
  private readonly _svgElements: Record<string, Record<string, true | undefined> | undefined> = objectAssign(createLookup<Record<string, true | undefined> | undefined>(), {
    'a':                    o('class externalResourcesRequired id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures style systemLanguage target transform xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space'),
    'altGlyph':             o('class dx dy externalResourcesRequired format glyphRef id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures rotate style systemLanguage x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y'),
    'altglyph':             createLookup<true | undefined>(),
    'altGlyphDef':          o('id xml:base xml:lang xml:space'),
    'altglyphdef':          createLookup<true | undefined>(),
    'altGlyphItem':         o('id xml:base xml:lang xml:space'),
    'altglyphitem':         createLookup<true | undefined>(),
    'animate':              o('accumulate additive attributeName attributeType begin by calcMode dur end externalResourcesRequired fill from id keySplines keyTimes max min onbegin onend onload onrepeat repeatCount repeatDur requiredExtensions requiredFeatures restart systemLanguage to values xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space'),
    'animateColor':         o('accumulate additive attributeName attributeType begin by calcMode dur end externalResourcesRequired fill from id keySplines keyTimes max min onbegin onend onload onrepeat repeatCount repeatDur requiredExtensions requiredFeatures restart systemLanguage to values xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space'),
    'animateMotion':        o('accumulate additive begin by calcMode dur end externalResourcesRequired fill from id keyPoints keySplines keyTimes max min onbegin onend onload onrepeat origin path repeatCount repeatDur requiredExtensions requiredFeatures restart rotate systemLanguage to values xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space'),
    'animateTransform':     o('accumulate additive attributeName attributeType begin by calcMode dur end externalResourcesRequired fill from id keySplines keyTimes max min onbegin onend onload onrepeat repeatCount repeatDur requiredExtensions requiredFeatures restart systemLanguage to type values xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space'),
    'circle':               o('class cx cy externalResourcesRequired id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup r requiredExtensions requiredFeatures style systemLanguage transform xml:base xml:lang xml:space'),
    'clipPath':             o('class clipPathUnits externalResourcesRequired id requiredExtensions requiredFeatures style systemLanguage transform xml:base xml:lang xml:space'),
    'color-profile':        o('id local name rendering-intent xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space'),
    'cursor':               o('externalResourcesRequired id requiredExtensions requiredFeatures systemLanguage x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y'),
    'defs':                 o('class externalResourcesRequired id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures style systemLanguage transform xml:base xml:lang xml:space'),
    'desc':                 o('class id style xml:base xml:lang xml:space'),
    'ellipse':              o('class cx cy externalResourcesRequired id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures rx ry style systemLanguage transform xml:base xml:lang xml:space'),
    'feBlend':              o('class height id in in2 mode result style width x xml:base xml:lang xml:space y'),
    'feColorMatrix':        o('class height id in result style type values width x xml:base xml:lang xml:space y'),
    'feComponentTransfer':  o('class height id in result style width x xml:base xml:lang xml:space y'),
    'feComposite':          o('class height id in in2 k1 k2 k3 k4 operator result style width x xml:base xml:lang xml:space y'),
    'feConvolveMatrix':     o('bias class divisor edgeMode height id in kernelMatrix kernelUnitLength order preserveAlpha result style targetX targetY width x xml:base xml:lang xml:space y'),
    'feDiffuseLighting':    o('class diffuseConstant height id in kernelUnitLength result style surfaceScale width x xml:base xml:lang xml:space y'),
    'feDisplacementMap':    o('class height id in in2 result scale style width x xChannelSelector xml:base xml:lang xml:space y yChannelSelector'),
    'feDistantLight':       o('azimuth elevation id xml:base xml:lang xml:space'),
    'feFlood':              o('class height id result style width x xml:base xml:lang xml:space y'),
    'feFuncA':              o('amplitude exponent id intercept offset slope tableValues type xml:base xml:lang xml:space'),
    'feFuncB':              o('amplitude exponent id intercept offset slope tableValues type xml:base xml:lang xml:space'),
    'feFuncG':              o('amplitude exponent id intercept offset slope tableValues type xml:base xml:lang xml:space'),
    'feFuncR':              o('amplitude exponent id intercept offset slope tableValues type xml:base xml:lang xml:space'),
    'feGaussianBlur':       o('class height id in result stdDeviation style width x xml:base xml:lang xml:space y'),
    'feImage':              o('class externalResourcesRequired height id preserveAspectRatio result style width x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y'),
    'feMerge':              o('class height id result style width x xml:base xml:lang xml:space y'),
    'feMergeNode':          o('id xml:base xml:lang xml:space'),
    'feMorphology':         o('class height id in operator radius result style width x xml:base xml:lang xml:space y'),
    'feOffset':             o('class dx dy height id in result style width x xml:base xml:lang xml:space y'),
    'fePointLight':         o('id x xml:base xml:lang xml:space y z'),
    'feSpecularLighting':   o('class height id in kernelUnitLength result specularConstant specularExponent style surfaceScale width x xml:base xml:lang xml:space y'),
    'feSpotLight':          o('id limitingConeAngle pointsAtX pointsAtY pointsAtZ specularExponent x xml:base xml:lang xml:space y z'),
    'feTile':               o('class height id in result style width x xml:base xml:lang xml:space y'),
    'feTurbulence':         o('baseFrequency class height id numOctaves result seed stitchTiles style type width x xml:base xml:lang xml:space y'),
    'filter':               o('class externalResourcesRequired filterRes filterUnits height id primitiveUnits style width x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y'),
    'font':                 o('class externalResourcesRequired horiz-adv-x horiz-origin-x horiz-origin-y id style vert-adv-y vert-origin-x vert-origin-y xml:base xml:lang xml:space'),
    'font-face':            o('accent-height alphabetic ascent bbox cap-height descent font-family font-size font-stretch font-style font-variant font-weight hanging id ideographic mathematical overline-position overline-thickness panose-1 slope stemh stemv strikethrough-position strikethrough-thickness underline-position underline-thickness unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical widths x-height xml:base xml:lang xml:space'),
    'font-face-format':     o('id string xml:base xml:lang xml:space'),
    'font-face-name':       o('id name xml:base xml:lang xml:space'),
    'font-face-src':        o('id xml:base xml:lang xml:space'),
    'font-face-uri':        o('id xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space'),
    'foreignObject':        o('class externalResourcesRequired height id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures style systemLanguage transform width x xml:base xml:lang xml:space y'),
    'g':                    o('class externalResourcesRequired id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures style systemLanguage transform xml:base xml:lang xml:space'),
    'glyph':                o('arabic-form class d glyph-name horiz-adv-x id lang orientation style unicode vert-adv-y vert-origin-x vert-origin-y xml:base xml:lang xml:space'),
    'glyphRef':             o('class dx dy format glyphRef id style x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y'),
    'glyphref':             createLookup<true | undefined>(),
    'hkern':                o('g1 g2 id k u1 u2 xml:base xml:lang xml:space'),
    'image':                o('class externalResourcesRequired height id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup preserveAspectRatio requiredExtensions requiredFeatures style systemLanguage transform width x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y'),
    'line':                 o('class externalResourcesRequired id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures style systemLanguage transform x1 x2 xml:base xml:lang xml:space y1 y2'),
    'linearGradient':       o('class externalResourcesRequired gradientTransform gradientUnits id spreadMethod style x1 x2 xlink:arcrole xlink:href xlink:role xlink:title xlink:type xml:base xml:lang xml:space y1 y2'),
    'marker':               o('class externalResourcesRequired id markerHeight markerUnits markerWidth orient preserveAspectRatio refX refY style viewBox xml:base xml:lang xml:space'),
    'mask':                 o('class externalResourcesRequired height id maskContentUnits maskUnits requiredExtensions requiredFeatures style systemLanguage width x xml:base xml:lang xml:space y'),
    'metadata':             o('id xml:base xml:lang xml:space'),
    'missing-glyph':        o('class d horiz-adv-x id style vert-adv-y vert-origin-x vert-origin-y xml:base xml:lang xml:space'),
    'mpath':                o('externalResourcesRequired id xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space'),
    'path':                 o('class d externalResourcesRequired id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup pathLength requiredExtensions requiredFeatures style systemLanguage transform xml:base xml:lang xml:space'),
    'pattern':              o('class externalResourcesRequired height id patternContentUnits patternTransform patternUnits preserveAspectRatio requiredExtensions requiredFeatures style systemLanguage viewBox width x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y'),
    'polygon':              o('class externalResourcesRequired id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup points requiredExtensions requiredFeatures style systemLanguage transform xml:base xml:lang xml:space'),
    'polyline':             o('class externalResourcesRequired id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup points requiredExtensions requiredFeatures style systemLanguage transform xml:base xml:lang xml:space'),
    'radialGradient':       o('class cx cy externalResourcesRequired fx fy gradientTransform gradientUnits id r spreadMethod style xlink:arcrole xlink:href xlink:role xlink:title xlink:type xml:base xml:lang xml:space'),
    'rect':                 o('class externalResourcesRequired height id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures rx ry style systemLanguage transform width x xml:base xml:lang xml:space y'),
    'script':               o('externalResourcesRequired id type xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space'),
    'set':                  o('attributeName attributeType begin dur end externalResourcesRequired fill id max min onbegin onend onload onrepeat repeatCount repeatDur requiredExtensions requiredFeatures restart systemLanguage to xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space'),
    'stop':                 o('class id offset style xml:base xml:lang xml:space'),
    'style':                o('id media title type xml:base xml:lang xml:space'),
    'svg':                  o('baseProfile class contentScriptType contentStyleType externalResourcesRequired height id onabort onactivate onclick onerror onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup onresize onscroll onunload onzoom preserveAspectRatio requiredExtensions requiredFeatures style systemLanguage version viewBox width x xml:base xml:lang xml:space y zoomAndPan'),
    'switch':               o('class externalResourcesRequired id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures style systemLanguage transform xml:base xml:lang xml:space'),
    'symbol':               o('class externalResourcesRequired id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup preserveAspectRatio style viewBox xml:base xml:lang xml:space'),
    'text':                 o('class dx dy externalResourcesRequired id lengthAdjust onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures rotate style systemLanguage textLength transform x xml:base xml:lang xml:space y'),
    'textPath':             o('class externalResourcesRequired id lengthAdjust method onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures spacing startOffset style systemLanguage textLength xlink:arcrole xlink:href xlink:role xlink:title xlink:type xml:base xml:lang xml:space'),
    'title':                o('class id style xml:base xml:lang xml:space'),
    'tref':                 o('class dx dy externalResourcesRequired id lengthAdjust onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures rotate style systemLanguage textLength x xlink:arcrole xlink:href xlink:role xlink:title xlink:type xml:base xml:lang xml:space y'),
    'tspan':                o('class dx dy externalResourcesRequired id lengthAdjust onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures rotate style systemLanguage textLength x xml:base xml:lang xml:space y'),
    'use':                  o('class externalResourcesRequired height id onactivate onclick onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup requiredExtensions requiredFeatures style systemLanguage transform width x xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y'),
    'view':                 o('externalResourcesRequired id preserveAspectRatio viewBox viewTarget xml:base xml:lang xml:space zoomAndPan'),
    'vkern':                o('g1 g2 id k u1 u2 xml:base xml:lang xml:space'),
  });

  /** @internal */
  private readonly _svgPresentationElements = o('a altGlyph animate animateColor circle clipPath defs ellipse feBlend feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap feFlood feGaussianBlur feImage feMerge feMorphology feOffset feSpecularLighting feTile feTurbulence filter font foreignObject g glyph glyphRef image line linearGradient marker mask missing-glyph path pattern polygon polyline radialGradient rect stop svg switch symbol text textPath tref tspan use');

  /** @internal */
  private readonly _svgPresentationAttributes = o('alignment-baseline baseline-shift clip-path clip-rule clip color-interpolation-filters color-interpolation color-profile color-rendering color cursor direction display dominant-baseline enable-background fill-opacity fill-rule fill filter flood-color flood-opacity font-family font-size-adjust font-size font-stretch font-style font-variant font-weight glyph-orientation-horizontal glyph-orientation-vertical image-rendering kerning letter-spacing lighting-color marker-end marker-mid marker-start mask opacity overflow pointer-events shape-rendering stop-color stop-opacity stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width stroke text-anchor text-decoration text-rendering unicode-bidi visibility word-spacing writing-mode');

  /** @internal */
  private readonly SVGElement: typeof SVGElement;
  public constructor() {
    const platform = resolve(IPlatform);
    this.SVGElement = platform.globalThis.SVGElement;

    const div = platform.document.createElement('div');
    div.innerHTML = '<svg><altGlyph /></svg>';
    if (div.firstElementChild!.nodeName === 'altglyph') {
      // handle chrome casing inconsistencies.
      const svg = this._svgElements;
      let tmp = svg.altGlyph;
      svg.altGlyph = svg.altglyph;
      svg.altglyph = tmp;
      tmp = svg.altGlyphDef;
      svg.altGlyphDef = svg.altglyphdef;
      svg.altglyphdef = tmp;
      tmp = svg.altGlyphItem;
      svg.altGlyphItem = svg.altglyphitem;
      svg.altglyphitem = tmp;
      tmp = svg.glyphRef;
      svg.glyphRef = svg.glyphref;
      svg.glyphref = tmp;
    }
  }

  public isStandardSvgAttribute(node: INode, attributeName: string): boolean {
    if (!(node instanceof this.SVGElement)) {
      return false;
    }

    return (
      this._svgPresentationElements[node.nodeName] === true && this._svgPresentationAttributes[attributeName] === true ||
      this._svgElements[node.nodeName]?.[attributeName] === true
    );
  }
}
