contract APMRegistry is AragonApp, AppProxyFactory, APMInternalAppNames {
    /* Hardcoded constants to save gas
    bytes32 public constant CREATE_REPO_ROLE = keccak256("CREATE_REPO_ROLE");
    */
    bytes32 public constant CREATE_REPO_ROLE = 0x2a9494d64846c9fdbf0158785aa330d8bc9caf45af27fa0e8898eb4d55adcea6;

    string private constant ERROR_INIT_PERMISSIONS = "APMREG_INIT_PERMISSIONS";
    string private constant ERROR_EMPTY_NAME = "APMREG_EMPTY_NAME";

    AbstractENS public ens;
    ENSSubdomainRegistrar public registrar;

    event NewRepo(bytes32 id, string name, address repo);

    /**
    * NEEDS CREATE_NAME_ROLE and POINT_ROOTNODE_ROLE permissions on registrar
    * @param _registrar ENSSubdomainRegistrar instance that holds registry root node ownership
    */
    function initialize(ENSSubdomainRegistrar _registrar) public onlyInit {
        initialized();

        registrar = _registrar;
        ens = registrar.ens();

        registrar.pointRootNode(this);

        // Check APM has all permissions it needss
        ACL acl = ACL(kernel().acl());
        require(acl.hasPermission(this, registrar, registrar.CREATE_NAME_ROLE()), ERROR_INIT_PERMISSIONS);
        require(acl.hasPermission(this, acl, acl.CREATE_PERMISSIONS_ROLE()), ERROR_INIT_PERMISSIONS);
    }

    /**
    * @notice Create new repo in registry with `_name`
    * @param _name Repo name, must be ununsed
    * @param _dev Address that will be given permission to create versions
    */
    function newRepo(string _name, address _dev) public auth(CREATE_REPO_ROLE) returns (Repo) {
        return _newRepo(_name, _dev);
    }

    /**
    * @notice Create new repo in registry with `_name` and first repo version
    * @param _name Repo name
    * @param _dev Address that will be given permission to create versions
    * @param _initialSemanticVersion Semantic version for new repo version
    * @param _contractAddress address for smart contract logic for version (if set to 0, it uses last versions' contractAddress)
    * @param _contentURI External URI for fetching new version's content
    */
    function newRepoWithVersion(
        string _name,
        address _dev,
        uint16[3] _initialSemanticVersion,
        address _contractAddress,
        bytes _contentURI
    ) public auth(CREATE_REPO_ROLE) returns (Repo)
    {
        Repo repo = _newRepo(_name, this); // need to have permissions to create version
        repo.newVersion(_initialSemanticVersion, _contractAddress, _contentURI);

        // Give permissions to _dev
        ACL acl = ACL(kernel().acl());
        acl.revokePermission(this, repo, repo.CREATE_VERSION_ROLE());
        acl.grantPermission(_dev, repo, repo.CREATE_VERSION_ROLE());
        acl.setPermissionManager(_dev, repo, repo.CREATE_VERSION_ROLE());
        return repo;
    }

    function _newRepo(string _name, address _dev) internal returns (Repo) {
        require(bytes(_name).length > 0, ERROR_EMPTY_NAME);

        Repo repo = newClonedRepo();

        ACL(kernel().acl()).createPermission(_dev, repo, repo.CREATE_VERSION_ROLE(), _dev);

        // Creates [name] subdomain in the rootNode and sets registry as resolver
        // This will fail if repo name already exists
        bytes32 node = registrar.createNameAndPoint(keccak256(abi.encodePacked(_name)), repo);

        emit NewRepo(node, _name, repo);

        return repo;
    }

    function newClonedRepo() internal returns (Repo repo) {
        repo = Repo(newAppProxy(kernel(), repoAppId()));
        repo.initialize();
    }

    function repoAppId() internal view returns (bytes32) {
        return keccak256(abi.encodePacked(registrar.rootNode(), keccak256(abi.encodePacked(REPO_APP_NAME))));
    }
}
function initialize(ENSSubdomainRegistrar _registrar) public onlyInit {
    initialized();

    registrar = _registrar;
    ens = registrar.ens();

    registrar.pointRootNode(this);

    // Check APM has all permissions it needss
    ACL acl = ACL(kernel().acl());
    require(acl.hasPermission(this, registrar, registrar.CREATE_NAME_ROLE()), ERROR_INIT_PERMISSIONS);
    require(acl.hasPermission(this, acl, acl.CREATE_PERMISSIONS_ROLE()), ERROR_INIT_PERMISSIONS);
}

/**
 * NEEDS CREATE_NAME_ROLE and POINT_ROOTNODE_ROLE permissions on registrar
 * @param _registrar ENSSubdomainRegistrar instance that holds registry root node ownership
 */