"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProofPointRegistryRoot = void 0;
var ethers_1 = require("ethers");
var ProofPointRegistryStorage1_json_1 = __importDefault(require("../build/ProofPointRegistryStorage1.json"));
var proofPointRegistry_1 = require("./proofPointRegistry");
var proofPointEvent_1 = require("./proofPointEvent");
/**
 * Proof point registry root
 * Represents the eternal storage contract of the ProofPointRegistry, which has a well known address
 * and provides methods to deploy and upgrade the logic contract as well as to get a proxy to the logic
 * contract by looking up its address in the eternal storage contract.
 */
var ProofPointRegistryRoot = /** @class */ (function () {
    /**
     * Creates an instance of proof point registry root.
     * @param address the well-known address of the deployed eternal storage contract.
     * @param provider an ethers.providers.JsonRpcProvider to use for interacting with the blockchain.
     */
    function ProofPointRegistryRoot(address, provider) {
        this._address = address;
        this._provider = provider;
        this._contract = new ethers_1.Contract(this._address.toString(), ProofPointRegistryStorage1_json_1.default.abi, provider);
    }
    /**
     * Gets an instance of ProofPointRegistry representing the current logic contract that is controlling
     * this eternal storage contract.
     * @param storage A @StorageProvider to use for storing and retrieving bulk data.
     * @param httpClient An @HttpClient to use for fetching DID documents.
     * @returns An instance of @ProofPointRegistry to use for interacting with proof points.
     */
    ProofPointRegistryRoot.prototype.getRegistry = function (storage, httpClient) {
        if (storage === void 0) { storage = null; }
        if (httpClient === void 0) { httpClient = null; }
        return __awaiter(this, void 0, void 0, function () {
            var logicAddress, _a, _b, registry;
            return __generator(this, function (_c) {
                switch (_c.label) {
                    case 0:
                        _b = (_a = proofPointEvent_1.EthereumAddress).parse;
                        return [4 /*yield*/, this._contract.getOwner()];
                    case 1:
                        logicAddress = _b.apply(_a, [_c.sent()]);
                        registry = new proofPointRegistry_1.ProofPointRegistry(this._address, logicAddress, this._provider, storage, httpClient);
                        return [2 /*return*/, registry];
                }
            });
        });
    };
    /**
     * Gets the address of the registry root - which is the address of the eternal storage contract.
     * @returns address of registry root.
     */
    ProofPointRegistryRoot.prototype.getAddress = function () {
        return this._address;
    };
    /**
     * Determines whether the deployed logic contract is the latest known version. If not then the
     * {@link upgrade} method can be called to deploy the latest logic contract and update the plumbing
     * so that the latest version will be used for future interactions.
     * @returns true if the {@link upgrade} method can be called to upgrade the logic contract.
     */
    ProofPointRegistryRoot.prototype.canUpgrade = function () {
        return __awaiter(this, void 0, void 0, function () {
            var version;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.getLogicContractVersion()];
                    case 1:
                        version = _a.sent();
                        return [2 /*return*/, version < proofPointRegistry_1.PROOF_POINT_REGISTRY_VERSION];
                }
            });
        });
    };
    /**
     * Deploys an instance of the Proof Point registry, including an eternal storage contract and a logic
     * contract.
     * @param fromAddress the Ethereum account to use for signing transactions. This will become the admin account that must be used for all future smart contract upgrades.
     * @param web3 a web3 instance to use for interacting with the Ethereum blockchain.
     * @returns a {@link ProofPointRegistryRoot} for interacting with the newly deployed contracts.
     */
    ProofPointRegistryRoot.deploy = function (provider, from) {
        return __awaiter(this, void 0, void 0, function () {
            var signer, factory, eternalStorage, logic, registryRoot;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        signer = provider.getSigner(from.toString());
                        factory = new ethers_1.ethers.ContractFactory(ProofPointRegistryStorage1_json_1.default.abi, ProofPointRegistryStorage1_json_1.default.bytecode, signer);
                        return [4 /*yield*/, factory.deploy()];
                    case 1:
                        eternalStorage = _a.sent();
                        // deploy logic contract pointing to eternal storage
                        factory = new ethers_1.ethers.ContractFactory(proofPointRegistry_1.ProofPointRegistryAbi[proofPointRegistry_1.PROOF_POINT_REGISTRY_VERSION].abi, proofPointRegistry_1.ProofPointRegistryAbi[proofPointRegistry_1.PROOF_POINT_REGISTRY_VERSION].bytecode, signer);
                        return [4 /*yield*/, factory.deploy(eternalStorage.address)];
                    case 2:
                        logic = _a.sent();
                        // set logic contract as owner of eternal storage
                        return [4 /*yield*/, eternalStorage.setOwner(logic.address)];
                    case 3:
                        // set logic contract as owner of eternal storage
                        _a.sent();
                        registryRoot = new ProofPointRegistryRoot(proofPointEvent_1.EthereumAddress.parse(eternalStorage.address), provider);
                        return [2 /*return*/, registryRoot];
                }
            });
        });
    };
    /**
     * Upgrades Proof Point registry. Performs the upgrade procedure to deploy an instance of the latest
     * logic contract, then set that as the owner of the eternal storage contract. You must control the admin
     * account to do this. Throws if already at latest version. Use {@link canUpgrade} to determine whether
     * this method can be called.
     */
    ProofPointRegistryRoot.prototype.upgrade = function () {
        return __awaiter(this, void 0, void 0, function () {
            var admin, signer, factory, logic, contractWithSigner;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.canUpgrade()];
                    case 1:
                        if (!(_a.sent())) {
                            throw new Error("Cannot upgrade Proof Point registry: Already at or above current version.");
                        }
                        return [4 /*yield*/, this._contract.getAdmin()];
                    case 2:
                        admin = _a.sent();
                        signer = this._provider.getSigner(admin);
                        factory = new ethers_1.ethers.ContractFactory(proofPointRegistry_1.ProofPointRegistryAbi[proofPointRegistry_1.PROOF_POINT_REGISTRY_VERSION].abi, proofPointRegistry_1.ProofPointRegistryAbi[proofPointRegistry_1.PROOF_POINT_REGISTRY_VERSION].bytecode, signer);
                        return [4 /*yield*/, factory.deploy(this._address.toString())];
                    case 3:
                        logic = _a.sent();
                        contractWithSigner = this._contract.connect(signer);
                        return [4 /*yield*/, contractWithSigner.setOwner(logic.address)];
                    case 4:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    ProofPointRegistryRoot.prototype.getLogicContractVersion = function () {
        return __awaiter(this, void 0, void 0, function () {
            var logicAddress, registry, version, e_1;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this._contract.getOwner()];
                    case 1:
                        logicAddress = _a.sent();
                        registry = new ethers_1.ethers.Contract(logicAddress, proofPointRegistry_1.ProofPointRegistryAbi[proofPointRegistry_1.PROOF_POINT_REGISTRY_VERSION].abi, this._provider);
                        _a.label = 2;
                    case 2:
                        _a.trys.push([2, 4, , 5]);
                        return [4 /*yield*/, registry.getVersion()];
                    case 3:
                        version = _a.sent();
                        return [2 /*return*/, version];
                    case 4:
                        e_1 = _a.sent();
                        // version 1 does not have the getVersion method.
                        return [2 /*return*/, 1];
                    case 5: return [2 /*return*/];
                }
            });
        });
    };
    return ProofPointRegistryRoot;
}());
exports.ProofPointRegistryRoot = ProofPointRegistryRoot;
