"use strict";

const MIMEType = require("whatwg-mimetype");
const whatwgEncoding = require("whatwg-encoding");
const { parseURL, serializeURL } = require("whatwg-url");
const sniffHTMLEncoding = require("html-encoding-sniffer");

const window = require("../../browser/Window");
const HTMLElementImpl = require("./HTMLElement-impl").implementation;
const { evaluateJavaScriptURL } = require("../window/navigation");
const { parseIntoDocument } = require("../../browser/parser");
const { documentBaseURL } = require("../helpers/document-base-url");
const { fireAnEvent } = require("../helpers/events");
const { getAttributeValue } = require("../attributes");
const idlUtils = require("../generated/utils");

function fireLoadEvent(document, frame, attaching) {
  if (attaching) {
    fireAnEvent("load", frame);

    return;
  }

  const dummyPromise = Promise.resolve();

  function onLoad() {
    fireAnEvent("load", frame);
  }

  document._queue.push(dummyPromise, onLoad);
}

function fetchFrame(serializedURL, frame, document, contentDoc) {
  const resourceLoader = document._resourceLoader;

  let request;

  function onFrameLoaded(data) {
    const sniffOptions = {
      defaultEncoding: document._encoding
    };

    if (request.response) {
      const contentType = MIMEType.parse(request.response.headers["content-type"]) || new MIMEType("text/plain");
      sniffOptions.transportLayerEncodingLabel = contentType.parameters.get("charset");

      if (contentType) {
        if (contentType.isXML()) {
          contentDoc._parsingMode = "xml";
        }
        contentDoc.contentType = contentType.essence;
      }
    }

    const encoding = sniffHTMLEncoding(data, sniffOptions);
    contentDoc._encoding = encoding;

    const html = whatwgEncoding.decode(data, contentDoc._encoding);

    try {
      parseIntoDocument(html, contentDoc);
    } catch (error) {
      const { DOMException } = contentDoc._globalObject;

      if (
        error.constructor.name === "DOMException" &&
        error.code === DOMException.SYNTAX_ERR &&
        contentDoc._parsingMode === "xml"
      ) {
        // As defined (https://html.spec.whatwg.org/#read-xml) parsing error in XML document may be reported inline by
        // mutating the document.
        const element = contentDoc.createElementNS("http://www.mozilla.org/newlayout/xml/parsererror.xml", "parsererror");
        element.textContent = error.message;

        while (contentDoc.childNodes.length > 0) {
          contentDoc.removeChild(contentDoc.lastChild);
        }
        contentDoc.appendChild(element);
      } else {
        throw error;
      }
    }

    contentDoc.close();

    return new Promise((resolve, reject) => {
      contentDoc.addEventListener("load", resolve);
      contentDoc.addEventListener("error", reject);
    });
  }

  request = resourceLoader.fetch(serializedURL, {
    element: frame,
    onLoad: onFrameLoaded
  });
}

function canDispatchEvents(frame, attaching) {
  if (!attaching) {
    return false;
  }

  return Object.keys(frame._eventListeners).length === 0;
}

function loadFrame(frame, attaching) {
  if (frame._contentDocument) {
    if (frame._contentDocument._defaultView) {
      // close calls delete on its document.
      frame._contentDocument._defaultView.close();
    } else {
      delete frame._contentDocument;
    }
  }

  const parentDoc = frame._ownerDocument;

  // https://html.spec.whatwg.org/#process-the-iframe-attributes
  let url;
  const srcAttribute = getAttributeValue(frame, "src");
  if (srcAttribute === "") {
    url = parseURL("about:blank");
  } else {
    url = parseURL(srcAttribute, { baseURL: documentBaseURL(parentDoc) || undefined }) || parseURL("about:blank");
  }
  const serializedURL = serializeURL(url);

  const wnd = window.createWindow({
    parsingMode: "html",
    url: url.scheme === "javascript" ? parentDoc.URL : serializedURL,
    parentOrigin: parentDoc._origin,
    resourceLoader: parentDoc._defaultView._resourceLoader,
    referrer: parentDoc.URL,
    cookieJar: parentDoc._cookieJar,
    pool: parentDoc._pool,
    encoding: parentDoc._encoding,
    runScripts: parentDoc._defaultView._runScripts,
    commonForOrigin: parentDoc._defaultView._commonForOrigin,
    pretendToBeVisual: parentDoc._defaultView._pretendToBeVisual
  });

  const contentDoc = frame._contentDocument = idlUtils.implForWrapper(wnd._document);
  const parent = parentDoc._defaultView;
  const contentWindow = contentDoc._defaultView;
  contentWindow._parent = parent;
  contentWindow._top = parent.top;
  contentWindow._frameElement = frame;
  contentWindow._virtualConsole = parent._virtualConsole;

  if (parentDoc._origin === contentDoc._origin) {
    contentWindow._currentOriginData.windowsInSameOrigin.push(contentWindow);
  }

  const noQueue = canDispatchEvents(frame, attaching);

  // Handle about:blank with a simulated load of an empty document.
  if (serializedURL === "about:blank") {
    // Cannot be done inside the enqueued callback; the documentElement etc. need to be immediately available.
    parseIntoDocument("<html><head></head><body></body></html>", contentDoc);
    contentDoc.close(noQueue);

    if (noQueue) {
      fireLoadEvent(parentDoc, frame, noQueue);
    } else {
      contentDoc.addEventListener("load", () => {
        fireLoadEvent(parentDoc, frame);
      });
    }
  } else if (url.scheme === "javascript") {
    // Cannot be done inside the enqueued callback; the documentElement etc. need to be immediately available.
    parseIntoDocument("<html><head></head><body></body></html>", contentDoc);
    contentDoc.close(noQueue);
    const result = evaluateJavaScriptURL(contentWindow, url);
    if (typeof result === "string") {
      contentDoc.body.textContent = result;
    }
    if (noQueue) {
      fireLoadEvent(parentDoc, frame, noQueue);
    } else {
      contentDoc.addEventListener("load", () => {
        fireLoadEvent(parentDoc, frame);
      });
    }
  } else {
    fetchFrame(serializedURL, frame, parentDoc, contentDoc);
  }
}

function refreshAccessors(document) {
  const { _defaultView } = document;

  if (!_defaultView) {
    return;
  }

  const frames = document.querySelectorAll("iframe,frame");

  // delete accessors for all frames
  for (let i = 0; i < _defaultView._length; ++i) {
    delete _defaultView[i];
  }

  _defaultView._length = frames.length;
  Array.prototype.forEach.call(frames, (frame, i) => {
    Object.defineProperty(_defaultView, i, {
      configurable: true,
      enumerable: true,
      get() {
        return frame.contentWindow;
      }
    });
  });
}

class HTMLFrameElementImpl extends HTMLElementImpl {
  constructor(globalObject, args, privateData) {
    super(globalObject, args, privateData);
    this._contentDocument = null;
  }
  _attrModified(name, value, oldVal) {
    super._attrModified(name, value, oldVal);
    if (name === "src") {
      // iframe should never load in a document without a Window
      // (e.g. implementation.createHTMLDocument)
      if (this._attached && this._ownerDocument._defaultView) {
        loadFrame(this);
      }
    }
  }

  _detach() {
    super._detach();

    if (this.contentWindow) {
      this.contentWindow.close();
    }

    refreshAccessors(this._ownerDocument);
  }

  _attach() {
    super._attach();

    if (this._ownerDocument._defaultView) {
      loadFrame(this, true);
    }
    refreshAccessors(this._ownerDocument);
  }

  get contentDocument() {

    let r = this._contentDocument;
    // Object.defineProperties(r, {
    //   getElementById:{
    //     value: function (id) {
    //       console.log('getElementById')
    //       let prs = {'pr1': [{"x":20.109375,"y":8,"width":11.9375,"height":55.9375,"top":8,"right":32.046875,"bottom":63.9375,"left":20.109375}],
    //         'pr2': [{"x":8,"y":263.081298828125,"width":4,"height":89.71237182617188,"top":263.081298828125,"right":12,"bottom":352.7936706542969,"left":8}],
    //         'pr3':[{"x":-3.4028232737618185e+32,"y":316.052490234375,"width":6.805646547523637e+32,"height":364.77001953125,"top":316.052490234375,"right":3.4028232737618185e+32,"bottom":680.822509765625,"left":-3.4028232737618185e+32}],
    //         'pr4': [{"x":353.04345703125,"y":228.5551300048828,"width":150.2568359375,"height":213.0459747314453,"top":228.5551300048828,"right":503.30029296875,"bottom":441.6011047363281,"left":353.04345703125}],
    //         'pr5': [{"x":64.515625,"y":594.9375,"width":70.734375,"height":193,"top":594.9375,"right":135.25,"bottom":787.9375,"left":64.515625}],
    //         'pr6': [{"x":7.888884544372559,"y":787.4771728515625,"width":28.22222900390625,"height":116.920654296875,"top":787.4771728515625,"right":36.11111354827881,"bottom":904.3978271484375,"left":7.888884544372559}],
    //         'pr7': [{"x":22.546875,"y":553.390625,"width":72,"height":23,"top":553.390625,"right":94.546875,"bottom":576.390625,"left":22.546875}],
    //         'pr8': [{"x":262.546875,"y":753.390625,"width":41.515625,"height":70,"top":753.390625,"right":304.0625,"bottom":823.390625,"left":262.546875}],
    //         'pr9': [{"x":262.546875,"y":793.390625,"width":164,"height":20,"top":793.390625,"right":426.546875,"bottom":813.390625,"left":262.546875}],
    //         'pr10': [{"x":262.546875,"y":588.390625,"width":20,"height":10,"top":588.390625,"right":282.546875,"bottom":598.390625,"left":262.546875}],}
    //       if (prs[id] !== undefined){
    //         return res[id]
    //       }
    //
    //       return _getElementById.apply(this, id)
    //     }
    //   }
    // })
    return r;
  }

  get contentWindow() {
    return this.contentDocument ? this.contentDocument._defaultView : null;
  }
}

module.exports = {
  implementation: HTMLFrameElementImpl
};
