"use strict";
/**
 * Contains the [[AccessController]] class
 * @packageDocumentation
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.AccessController = void 0;
const acl_1 = require("./acl");
const rdflib_1 = require("rdflib");
const access_groups_1 = require("./access-groups");
const acl_control_1 = require("./acl-control");
const utils = __importStar(require("../utils"));
const debug = __importStar(require("../debug"));
const style_1 = require("../style");
/**
 * Rendered HTML component used in the databrowser's Sharing pane.
 */
class AccessController {
    subject;
    noun;
    context;
    statusElement;
    targetIsProtected;
    targetDoc;
    targetACLDoc;
    defaultHolder;
    defaultACLDoc;
    prospectiveDefaultHolder;
    store;
    dom;
    mainCombo;
    defaultsCombo;
    isContainer;
    defaultsDiffer;
    rootElement;
    isUsingDefaults;
    constructor(subject, noun, context, statusElement, targetIsProtected, targetDoc, targetACLDoc, defaultHolder, defaultACLDoc, prospectiveDefaultHolder, store, dom) {
        this.subject = subject;
        this.noun = noun;
        this.context = context;
        this.statusElement = statusElement;
        this.targetIsProtected = targetIsProtected;
        this.targetDoc = targetDoc;
        this.targetACLDoc = targetACLDoc;
        this.defaultHolder = defaultHolder;
        this.defaultACLDoc = defaultACLDoc;
        this.prospectiveDefaultHolder = prospectiveDefaultHolder;
        this.store = store;
        this.dom = dom;
        this.rootElement = dom.createElement('div');
        this.rootElement.setAttribute('style', style_1.style.aclGroupContent);
        this.isContainer = targetDoc.uri.slice(-1) === '/'; // Give default for all directories
        if (defaultHolder && defaultACLDoc) {
            this.isUsingDefaults = true;
            const aclDefaultStore = (0, acl_1.adoptACLDefault)(this.targetDoc, targetACLDoc, defaultHolder, defaultACLDoc);
            this.mainCombo = new access_groups_1.AccessGroups(targetDoc, targetACLDoc, this, aclDefaultStore, { defaults: this.isContainer });
            this.defaultsCombo = null;
            this.defaultsDiffer = false;
        }
        else {
            this.isUsingDefaults = false;
            this.mainCombo = new access_groups_1.AccessGroups(targetDoc, targetACLDoc, this, store);
            this.defaultsCombo = new access_groups_1.AccessGroups(targetDoc, targetACLDoc, this, store, { defaults: this.isContainer });
            this.defaultsDiffer = !(0, acl_1.sameACL)(this.mainCombo.aclMap, this.defaultsCombo.aclMap);
        }
    }
    get isEditable() {
        return !this.isUsingDefaults;
    }
    render() {
        this.rootElement.innerHTML = '';
        if (this.isUsingDefaults) {
            this.renderStatus(`The sharing for this ${this.noun} is the default for folder `);
            if (this.defaultHolder) {
                const defaultHolderLink = this.statusElement.appendChild(this.dom.createElement('a'));
                defaultHolderLink.href = this.defaultHolder.uri;
                defaultHolderLink.innerText = (0, acl_control_1.shortNameForFolder)(this.defaultHolder);
            }
        }
        else if (!this.defaultsDiffer && this.isContainer) {
            this.renderStatus('This is also the default for things in this folder.');
        }
        else {
            this.renderStatus('');
        }
        this.rootElement.appendChild(this.mainCombo.render());
        if (this.defaultsCombo && this.defaultsDiffer) {
            this.rootElement.appendChild(this.renderRemoveDefaultsController());
            this.rootElement.appendChild(this.defaultsCombo.render());
        }
        else if (this.isEditable && this.isContainer) {
            this.rootElement.appendChild(this.renderAddDefaultsController());
        }
        if (!this.targetIsProtected && this.isUsingDefaults) {
            this.rootElement.appendChild(this.renderAddAclsController());
        }
        else if (!this.targetIsProtected) {
            this.rootElement.appendChild(this.renderRemoveAclsController());
        }
        return this.rootElement;
    }
    renderRemoveAclsController() {
        const useDefaultButton = this.dom.createElement('button');
        useDefaultButton.innerText = `Remove custom sharing settings for this ${this.noun} -- just use default${this.prospectiveDefaultHolder ? ` for ${utils.label(this.prospectiveDefaultHolder)}` : ''}`;
        useDefaultButton.setAttribute('style', style_1.style.bigButton);
        useDefaultButton.addEventListener('click', () => this.removeAcls()
            .then(() => this.render())
            .catch(error => this.renderStatus(error)));
        return useDefaultButton;
    }
    renderAddAclsController() {
        const addAclButton = this.dom.createElement('button');
        addAclButton.innerText = `Set specific sharing for this ${this.noun}`;
        addAclButton.setAttribute('style', style_1.style.bigButton);
        addAclButton.addEventListener('click', () => this.addAcls()
            .then(() => this.render())
            .catch(error => this.renderStatus(error)));
        return addAclButton;
    }
    renderAddDefaultsController() {
        const containerElement = this.dom.createElement('div');
        containerElement.setAttribute('style', style_1.style.defaultsController);
        const noticeElement = containerElement.appendChild(this.dom.createElement('div'));
        noticeElement.innerText = 'Sharing for things within the folder currently tracks sharing for the folder.';
        noticeElement.setAttribute('style', style_1.style.defaultsControllerNotice);
        const button = containerElement.appendChild(this.dom.createElement('button'));
        button.innerText = 'Set the sharing of folder contents separately from the sharing for the folder';
        button.setAttribute('style', style_1.style.bigButton);
        button.addEventListener('click', () => this.addDefaults()
            .then(() => this.render()));
        return containerElement;
    }
    renderRemoveDefaultsController() {
        const containerElement = this.dom.createElement('div');
        containerElement.setAttribute('style', style_1.style.defaultsController);
        const noticeElement = containerElement.appendChild(this.dom.createElement('div'));
        noticeElement.innerText = 'Access to things within this folder:';
        noticeElement.setAttribute('style', style_1.style.defaultsControllerNotice);
        const button = containerElement.appendChild(this.dom.createElement('button'));
        button.innerText = 'Set default for folder contents to just track the sharing for the folder';
        button.setAttribute('style', style_1.style.bigButton);
        button.addEventListener('click', () => this.removeDefaults()
            .then(() => this.render())
            .catch(error => this.renderStatus(error)));
        return containerElement;
    }
    renderTemporaryStatus(message) {
        // @@ TODO Introduce better system for error notification to user https://github.com/solidos/mashlib/issues/87
        this.statusElement.setAttribute('style', style_1.style.aclControlBoxStatusRevealed);
        this.statusElement.innerText = message;
        this.statusElement.setAttribute('style', style_1.style.temporaryStatusInit);
        setTimeout(() => {
            this.statusElement.setAttribute('style', style_1.style.temporaryStatusEnd);
        });
        setTimeout(() => {
            this.statusElement.innerText = '';
        }, 5000);
    }
    renderStatus(message) {
        // @@ TODO Introduce better system for error notification to user https://github.com/solidos/mashlib/issues/87
        if (!message) {
            this.statusElement.setAttribute('style', style_1.style.aclControlBoxStatusRevealed);
        }
        this.statusElement.innerText = message;
    }
    async addAcls() {
        if (!this.defaultHolder || !this.defaultACLDoc) {
            const message = 'Unable to find defaults to copy';
            debug.error(message);
            return Promise.reject(message);
        }
        const aclGraph = (0, acl_1.adoptACLDefault)(this.targetDoc, this.targetACLDoc, this.defaultHolder, this.defaultACLDoc);
        aclGraph.statements.forEach(st => this.store.add(st.subject, st.predicate, st.object, this.targetACLDoc));
        try {
            await this.store.fetcher.putBack(this.targetACLDoc);
            this.isUsingDefaults = false;
            return Promise.resolve();
        }
        catch (error) {
            const message = ` Error writing back access control file! ${error}`;
            debug.error(message);
            return Promise.reject(message);
        }
    }
    async addDefaults() {
        this.defaultsCombo = new access_groups_1.AccessGroups(this.targetDoc, this.targetACLDoc, this, this.store, { defaults: true });
        this.defaultsDiffer = true;
    }
    async removeAcls() {
        try {
            await this.store.fetcher.delete(this.targetACLDoc.uri, {});
            this.isUsingDefaults = true;
            try {
                this.prospectiveDefaultHolder = await (0, acl_1.getProspectiveHolder)(this.targetDoc.uri);
            }
            catch (error) {
                // No need to show this error in status, but good to warn about it in console
                debug.warn(error);
            }
        }
        catch (error) {
            const message = `Error deleting access control file: ${this.targetACLDoc}: ${error}`;
            debug.error(message);
            return Promise.reject(message);
        }
    }
    async removeDefaults() {
        const fallbackCombo = this.defaultsCombo;
        try {
            this.defaultsCombo = null;
            this.defaultsDiffer = false;
            await this.save();
        }
        catch (error) {
            this.defaultsCombo = fallbackCombo;
            this.defaultsDiffer = true;
            debug.error(error);
            return Promise.reject(error);
        }
    }
    save() {
        // build graph
        const newAClGraph = (0, rdflib_1.graph)();
        if (!this.isContainer) {
            (0, acl_1.makeACLGraphbyCombo)(newAClGraph, this.targetDoc, this.mainCombo.byCombo, this.targetACLDoc, true);
        }
        else if (this.defaultsCombo && this.defaultsDiffer) {
            // Pair of controls
            (0, acl_1.makeACLGraphbyCombo)(newAClGraph, this.targetDoc, this.mainCombo.byCombo, this.targetACLDoc, true);
            (0, acl_1.makeACLGraphbyCombo)(newAClGraph, this.targetDoc, this.defaultsCombo.byCombo, this.targetACLDoc, false, true);
        }
        else {
            // Linked controls
            (0, acl_1.makeACLGraphbyCombo)(newAClGraph, this.targetDoc, this.mainCombo.byCombo, this.targetACLDoc, true, true);
        }
        // add authenticated fetcher
        newAClGraph.fetcher = (0, rdflib_1.fetcher)(newAClGraph, { fetch: this.store.fetcher._fetch });
        const updater = newAClGraph.updater || new rdflib_1.UpdateManager(newAClGraph);
        // save ACL resource
        return new Promise((resolve, reject) => {
            updater.put(this.targetACLDoc, newAClGraph.statementsMatching(undefined, undefined, undefined, this.targetACLDoc), 'text/turtle', (uri, ok, message) => {
                if (!ok) {
                    return reject(new Error(`ACL file save failed: ${message}`));
                }
                this.store.fetcher.unload(this.targetACLDoc);
                this.store.add(newAClGraph.statements);
                this.store.fetcher.requested[this.targetACLDoc.uri] = 'done'; // missing: save headers
                this.mainCombo.store = this.store;
                if (this.defaultsCombo) {
                    this.defaultsCombo.store = this.store;
                }
                this.defaultsDiffer = !!this.defaultsCombo && !(0, acl_1.sameACL)(this.mainCombo.aclMap, this.defaultsCombo.aclMap);
                debug.log('ACL modification: success!');
                resolve();
            });
        });
    }
}
exports.AccessController = AccessController;
//# sourceMappingURL=access-controller.js.map