       'delete machines'   : ['member','moderator']
       'update machines'   : ['member','moderator']
       'list own machines' : ['member','moderator']
+      'execute templates' : ['member','moderator']
     sharedMethods         :
       static              :
         ping              :

    callback ?= ->
    options  ?= {}

    fetchStackTemplate client, (err, res)->

      return callback err  if err

      { account, user, group, template } = res

      stackRevision = template.template?.sum or ''

      JComputeStack = require '../stack'
      JComputeStack.create {
        title         : template.title
        config        : template.config
        baseStackId   : template._id
        groupSlug     : group.slug
        account, stackRevision
      }, (err, stack)->

        return callback err  if err

        queue         = []
        results       =
          rules       : []
          machines    : []
          domains     : []
          connections : []

        queue.push ->

          checkTemplateUsage template, account, (err)->
            return callback err  if err

            account.addStackTemplate template, (err)->
              if err then callback err else queue.next()

        template.machines?.forEach (machineInfo)->

          queue.push ->

            machineInfo.stack         = stack

            # We are passing the provided credential for the template
            # Provider implementation can override this value like we
            # did in Koding Provider ~ GG
            machineInfo.credential    = template.credentials?.first
            machineInfo.generatedFrom =
              templateId : template._id
              revision   : stackRevision

            create = (machineInfo) ->
              ComputeProvider.create client, machineInfo, (err, machine)->
                results.machines.push { err, obj: machine }
                queue.next()

            # This is optional, since for koding group for example
            # we don't want to add our admins into users machines ~ GG
            unless options.addGroupAdminToMachines
              return create machineInfo

            # TODO Do we need all admins or only some of them? ~ GG
            # Maybe some of them as admin some of them as user etc.
            group.fetchAdmin (err, admin)->

              if not err and admin and not admin.getId().equals account.getId()
                admin.fetchUser (err, adminUser)->
                  if not err and adminUser
                    machineInfo.users = [
                      { id: adminUser.getId(), sudo: yes, owner: yes }
                    ]
                  create machineInfo
              else
                create machineInfo


        template.domains?.forEach (domainInfo)->

          queue.push ->
            domain = domainInfo.domain.replace "${username}", user.username
            JProposedDomain.createDomain {
              domain, account,
              stack : stack._id
              group : group.slug
            }, (err, r)->
              console.warn err  if err?
              results.domains.push { err, obj: r }
              queue.next()

        template.connections?.forEach (c)->

          queue.push ->

            # Assign rule to domain
            if c.rules? and c.domains?

              rule   = results.rules[c.rules]
              domain = results.domains[c.domains]

              if not rule?.err and not domain?.err
                results.connections.push
                  err : new KodingError "Not implemented"
                  obj : null
              else
                results.connections.push
                  err : new KodingError "Missing edge"
                  obj : null

              queue.next()

            # Assign a domain to machine
            else if c.machines? and c.domains?

              domain  = results.domains[c.domains]
              machine = results.machines[c.machines]

              if not domain?.err and not machine?.err

                domain.obj.bindMachine machine.obj.getId(), (err)->
                  results.connections.push { err, obj: ok: !err? }
                  queue.next()

              else
                results.connections.push
                  err : new KodingError "Missing edge"
                  obj : null
                queue.next()

            else
              queue.next()

        queue.push ->

          callback null, {stack, results}

        daisy queue


  do ->

    JGroup = require '../group'
    JGroup.on 'MemberAdded', ({group, member})->

      # No need to try creating group stacks for guests or koding group members
      return  if group.slug in ['guests', 'koding']



    JAccount = require '../account'
    JAccount.on 'UsernameChanged', ({ oldUsername, username, isRegistration }) ->

      return  unless oldUsername and username
      return  if isRegistration
