/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

const { windows, isBrowser, isInteractive, isDocumentLoaded,
        getOuterId } = require("../window/utils");
const { InputPort } = require("./system");
const { lift, merges, foldp, keepIf, start, Input } = require("../event/utils");
const { patch } = require("diffpatcher/index");
const { Sequence, seq, filter, object, pairs } = require("../util/sequence");


// Create lazy iterators from the regular arrays, although
// once https://github.com/mozilla/addon-sdk/pull/1314 lands
// `windows` will be transforme to lazy iterators.
// When iterated over belowe sequences items will represent
// state of windows at the time of iteration.
const opened = seq(function*() {
  const items = windows("navigator:browser", {includePrivate: true});
  for (let item of items) {
      yield [getOuterId(item), item];
  }
});
const interactive = filter(([_, window]) => isInteractive(window), opened);
const loaded = filter(([_, window]) => isDocumentLoaded(window), opened);

// Helper function that converts given argument to a delta.
const Update = window => window && object([getOuterId(window), window]);
const Delete = window => window && object([getOuterId(window), null]);


// Signal represents delta for last top level window close.
const LastClosed = lift(Delete,
                        keepIf(isBrowser, null,
                               new InputPort({topic: "domwindowclosed"})));
exports.LastClosed = LastClosed;

const windowFor = document => document && document.defaultView;

// Signal represent delta for last top level window document becoming interactive.
const InteractiveDoc = new InputPort({topic: "chrome-document-interactive"});
const InteractiveWin = lift(windowFor, InteractiveDoc);
const LastInteractive = lift(Update, keepIf(isBrowser, null, InteractiveWin));
exports.LastInteractive = LastInteractive;

// Signal represent delta for last top level window loaded.
const LoadedDoc = new InputPort({topic: "chrome-document-loaded"});
const LoadedWin = lift(windowFor, LoadedDoc);
const LastLoaded = lift(Update, keepIf(isBrowser, null, LoadedWin));
exports.LastLoaded = LastLoaded;


const initialize = input => {
  if (!input.initialized) {
    input.value = object(...input.value);
    Input.start(input);
    input.initialized = true;
  }
};

// Signal represents set of top level interactive windows, updated any
// time new window becomes interactive or one get's closed.
const Interactive = foldp(patch, interactive, merges([LastInteractive,
                                                      LastClosed]));
Interactive[start] = initialize;
exports.Interactive = Interactive;

// Signal represents set of top level loaded window, updated any time
// new window becomes interactive or one get's closed.
const Loaded = foldp(patch, loaded, merges([LastLoaded, LastClosed]));
Loaded[start] = initialize;
exports.Loaded = Loaded;