           else @plans = plans
           callback plans
 
-  fetchUserPlan: (callback = kd.noop)->
-
-    if @lastKnownUserPlan?
-      return callback @lastKnownUserPlan
-
-    kd.singletons.paymentController.subscriptions (err, subscription)=>
-
-      kd.warn "Failed to fetch subscription:", err  if err?
-
-      if err? or not subscription?
-      then callback 'free'
-      else callback @lastKnownUserPlan = subscription.planTitle
-
+
+  fetchRewards: (options, callback)->
+
+    {unit} = options
+
+    options =
+      unit  : 'MB'
+      type  : 'disk'
+
+    remote.api.JReward.fetchEarnedAmount options, (err, amount)->
+
+      if err
+        amount = 0
+        kd.warn err
+
+      amount = Math.floor amount / 1000  if unit is 'GB'
+
+      callback null, amount
+
+
+  fetchPlanCombo: (provider, callback)->
+
+    [callback, provider] = [provider, callback]  unless callback?
+    provider ?= "koding"
+
+    @fetchUserPlan (plan)=> @fetchPlans (plans)=>
+      @fetchUsage { provider }, (err, usage)=>
+        @fetchRewards { unit: 'GB' }, (err, reward)->
+          callback err, { plan, plans, usage, reward }
+
+
+  # create helpers on top of remote.ComputeProvider
+  #
+
+  create: (options, callback)->
+    remote.api.ComputeProvider.create options, (err, machine)=>
+      @reset yes  unless err?
+      callback err, machine
+
+
+  createDefaultStack: ->
+    return  unless isLoggedIn()
+    remote.api.ComputeProvider.createGroupStack (err, res)=>
+      return  if showError err
+      @reset yes
+
+
+  # remote.ComputeProvider and Kloud kite public methods
+  #
+
+  info: (machine)->
+
+    if @eventListener.followUpcomingEvents machine, yes
+      return Promise.resolve()
+
+    @eventListener.triggerState machine,
+      status      : machine.status.state
+      percentage  : 0
+
+    machineId = machine._id
+    currentState = machine.status.state
+
+    call = @getKloud().info { machineId, currentState }
+
+    .then (response)=>
+
+      kd.log "info response:", response
+      @_clearTrialCounts machine
+      @eventListener.triggerState machine,
+        status      : response.State
+        percentage  : 100
+
+      response
+
+    .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+    .catch (err)=>
+
+      (@errorHandler call, 'info', machine) err
+
+
+  destroy: (machine, force)->
+
+    destroy = (machine)=>
+
+      @eventListener.triggerState machine,
+        status      : Machine.State.Terminating
+        percentage  : 0
+
+      machine.getBaseKite( createIfNotExists = no ).disconnect()
+
+      call = @getKloud().destroy { machineId: machine._id }
+
+      .then (res)=>
+
+        @_force = no
+
+        kd.log "destroy res:", res
+        @emit "MachineBeingDestroyed", machine
+        @_clearTrialCounts machine
+        @eventListener.addListener 'destroy', machine._id
+
+      .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+      .catch (err)=>
+
+        (@errorHandler call, 'destroy', machine) err
+
+    return destroy machine  if force or @_force
+
+    ComputeController_UI.askFor 'destroy', {machine, force}, =>
+      destroy machine
+
+
+  reinit: (machine)->
+
+    ComputeController_UI.askFor 'reinit', {machine, force: @_force}, =>
+
+      @eventListener.triggerState machine,
+        status      : Machine.State.Terminating
+        percentage  : 0
+
+      machine.getBaseKite( createIfNotExists = no ).disconnect()
+
+      call = @getKloud().reinit { machineId: machine._id }
+
+      .then (res)=>
+
+        @_force = no
+
+        kd.log "reinit res:", res
+        @emit "MachineBeingDestroyed", machine
+        @_clearTrialCounts machine
+        @eventListener.addListener 'reinit', machine._id
+
+      .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+      .catch (err)=>
+
+        (@errorHandler call, 'reinit', machine) err
+
+
+  resize: (machine, resizeTo = 10)->
+
+    ComputeController_UI.askFor 'resize', {
+      machine, force: @_force, resizeTo
+    }, =>
+
+      options =
+        machineId : machine._id
+        provider  : machine.provider
+        resize    : resizeTo
+
+      remote.api.ComputeProvider.update options, (err)=>
+
+        return  if showError err
+
+        @eventListener.triggerState machine,
+          status      : Machine.State.Pending
+          percentage  : 0
+
+        machine.getBaseKite( createIfNotExists = no ).disconnect()
+
+        call = @getKloud().resize { machineId: machine._id }
+
+        .then (res)=>
+
+          @_force = no
+
+          kd.log "resize res:", res
+          @_clearTrialCounts machine
+          @eventListener.addListener 'resize', machine._id
+
+        .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+        .catch (err)=>
+
+          (@errorHandler call, 'resize', machine) err
+
+
+  build: (machine)->
+
+    @eventListener.triggerState machine,
+      status      : Machine.State.Building
+      percentage  : 0
+
+    machine.getBaseKite( createIfNotExists = no ).disconnect()
+
+    call = @getKloud().build { machineId: machine._id }
+
+    .then (res)=>
+
+      kd.log "build res:", res
+      @_clearTrialCounts machine
+      @eventListener.addListener 'build', machine._id
+
+    .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+    .catch (err)=>
+
+      (@errorHandler call, 'build', machine) err
+
+
+  start: (machine)->
+
+    @eventListener.triggerState machine,
+      status      : Machine.State.Starting
+      percentage  : 0
+
+    machine.getBaseKite( createIfNotExists = no ).isDisconnected = no
+
+    call = @getKloud().start { machineId: machine._id }
+
+    .then (res)=>
+
+      kd.log "start res:", res
+      @_clearTrialCounts machine
+      @eventListener.addListener 'start', machine._id
+
+    .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+    .catch (err)=>
+
+      (@errorHandler call, 'start', machine) err
+
+
+  stop: (machine)->
+
+    @eventListener.triggerState machine,
+      status      : Machine.State.Stopping
+      percentage  : 0
+      silent      : yes
+
+    machine.getBaseKite( createIfNotExists = no ).disconnect()
+
+    call = @getKloud().stop { machineId: machine._id }
+
+    .then (res)=>
+
+      kd.log "stop res:", res
+      @_clearTrialCounts machine
+      @eventListener.addListener 'stop', machine._id
+
+    .timeout globals.COMPUTECONTROLLER_TIMEOUT
+
+    .catch (err)=>
+
+      (@errorHandler call, 'stop', machine) err
+
+
+  setAlwaysOn: (machine, state, callback = kd.noop)->
+
+    options =
+      machineId : machine._id
+      provider  : machine.provider
+      alwaysOn  : state
+
+    remote.api.ComputeProvider.update options, (err)=>
+      @triggerReviveFor machine._id  unless err?
+      callback err
+
+
+  # Domain management
+  #
+
+  fetchDomains: do (queue=[])->
+
+    (callback = kd.noop)-> kd.singletons.mainController.ready =>
+
+      @domains ?= []
+
+      if @domains.length > 0
+        callback null, @domains
+        kd.info "Domains returned from cache."
+        return
+
+      return  if (queue.push callback) > 1
+
+      topDomain = "#{nick()}.#{globals.config.userSitesDomain}"
+
+      remote.api.JDomainAlias.some {}, (err, domains)=>
+
+        if err?
+          cb err  for cb in queue
+          queue = []
+          return
+
+        # Move topDomain to index 0
+        _domains = []
+        for jdomain in domains
+          if jdomain.domain is topDomain
+          then _domains.unshift jdomain
+          else _domains.push    jdomain
+
+        @domains = _domains
+        cb null, @domains  for cb in queue
+        queue = []
+
+
+  setDomain: (machine, newDomain, callback = kd.noop) ->
+
+    @getKloud().setDomain { machineId: machine._id, newDomain }
+    .nodeify callback
+
+
+  # Utils beyond this point
+  #
 
   triggerReviveFor:(machineId)->
 

        kd.info "Revive triggered for #{machineId}", machine


  setDomain: (machine, newDomain, callback = kd.noop) ->

    @getKloud().setDomain { machineId: machine._id, newDomain }
    .nodeify callback


  fetchPlanCombo: (provider, callback)->

    [callback, provider] = [provider, callback]  unless callback?
    provider ?= "koding"

    @fetchUserPlan (plan)=> @fetchPlans (plans)=>
      @fetchUsage { provider }, (err, usage)=>
        @fetchRewards { unit: 'GB' }, (err, reward)->
          callback err, { plan, plans, usage, reward }

  fetchRewards: (options, callback)->

    {unit} = options

    options =
      unit  : 'MB'
      type  : 'disk'

    remote.api.JReward.fetchEarnedAmount options, (err, amount)->

      if err
        amount = 0
        kd.warn err

      amount = Math.floor amount / 1000  if unit is 'GB'

      callback null, amount


  findMachineFromMachineId: (machineId)->

    for machine in @machines
      return machine  if machine._id is machineId

  findMachineFromQueryString: (queryString)->

    for machine in @machines
      return machine  if machine.queryString is queryString

  invalidateCache: (machineId)->

    machine = @findMachineFromMachineId machineId

module.exports = class ComputeController extends KDController

  @providers = globals.config.providers
  @timeout   = globals.config.COMPUTECONTROLLER_TIMEOUT
  @Error     = {
    'TimeoutError', 'KiteError', Pending: '107', NotVerified: '500'
  }

  constructor: ->

    super

