'use strict'
var Ethdebugger = require('../Ethdebugger')
var remixLib = require('remix-lib')
var EventManager = remixLib.EventManager
var traceHelper = remixLib.helpers.trace
var OffsetToColumnConverter = remixLib.OffsetToColumnConverter

var StepManager = require('./stepManager')
var VmDebuggerLogic = require('./VmDebugger')

function Debugger (options) {
  var self = this
  this.event = new EventManager()

  this.offsetToLineColumnConverter = options.offsetToLineColumnConverter || (new OffsetToColumnConverter())
  this.compiler = options.compiler

  this.debugger = new Ethdebugger({
    web3: options.web3,
    compilationResult: () => {
      var compilationResult = this.compiler.lastCompilationResult
      if (compilationResult) {
        return compilationResult.data
      }
      return null
    }
  })

  this.breakPointManager = new remixLib.code.BreakpointManager(this.debugger, (sourceLocation) => {
    return self.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file, this.compiler.lastCompilationResult.source.sources, this.compiler.lastCompilationResult.data.sources)
  }, (step) => {
    self.event.trigger('breakpointStep', [step])
  })

  this.debugger.setBreakpointManager(this.breakPointManager)

  this.debugger.event.register('newTraceLoaded', this, function () {
    self.event.trigger('debuggerStatus', [true])
  })

  this.debugger.event.register('traceUnloaded', this, function () {
    self.event.trigger('debuggerStatus', [false])
  })

  this.event.register('breakpointStep', function (step) {
    self.step_manager.jumpTo(step)
  })
}

Debugger.prototype.registerAndHighlightCodeItem = function (index) {
  const self = this
  // register selected code item, highlight the corresponding source location
  if (!self.compiler.lastCompilationResult) return
  self.debugger.traceManager.getCurrentCalledAddressAt(index, (error, address) => {
    if (error) return console.log(error)
    self.debugger.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, index, self.compiler.lastCompilationResult.data.contracts, function (error, rawLocation) {
      if (!error && self.compiler.lastCompilationResult && self.compiler.lastCompilationResult.data) {
        var lineColumnPos = self.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, self.compiler.lastCompilationResult.source.sources, self.compiler.lastCompilationResult.data.sources)
        self.event.trigger('newSourceLocation', [lineColumnPos, rawLocation])
      } else {
        self.event.trigger('newSourceLocation', [null])
      }
    })
  })
}

Debugger.prototype.updateWeb3 = function (web3) {
  this.debugger.web3 = web3
}

Debugger.prototype.debug = function (blockNumber, txNumber, tx, loadingCb) {
  const self = this
  let web3 = this.debugger.web3

  return new Promise((resolve, reject) => {
  if (this.debugger.traceManager.isLoading) {
      return resolve()
  }

  if (tx) {
    if (!tx.to) {
      tx.to = traceHelper.contractCreationToken('0')
    }
      self.debugTx(tx, loadingCb)
      return resolve()
  }

  try {
    if (txNumber.indexOf('0x') !== -1) {
      return web3.eth.getTransaction(txNumber, function (_error, result) {
          if (_error) return reject(_error)
          if (!result) return reject('cannot find transaction ' + txNumber)
        let tx = result
        self.debugTx(tx, loadingCb)
          return resolve()
      })
    }
    web3.eth.getTransactionFromBlock(blockNumber, txNumber, function (_error, result) {
        if (_error) return reject(_error)
        if (!result) return reject('cannot find transaction ' + blockNumber + ' ' + txNumber)
      let tx = result
      self.debugTx(tx, loadingCb)
        return resolve()
    })
  } catch (e) {
    console.error(e.message)
      return reject(e.message)
  }
  })
}

Debugger.prototype.debugTx = function (tx, loadingCb) {
  const self = this
  this.step_manager = new StepManager(this.debugger, this.debugger.traceManager)

  this.debugger.codeManager.event.register('changed', this, (code, address, instIndex) => {
    self.debugger.callTree.sourceLocationTracker.getSourceLocationFromVMTraceIndex(address, this.step_manager.currentStepIndex, this.debugger.solidityProxy.contracts, (error, sourceLocation) => {
      if (!error) {
        self.vmDebuggerLogic.event.trigger('sourceLocationChanged', [sourceLocation])
      }
    })
  })

  this.vmDebuggerLogic = new VmDebuggerLogic(this.debugger, tx, this.step_manager, this.debugger.traceManager, this.debugger.codeManager, this.debugger.solidityProxy, this.debugger.callTree)
  this.vmDebuggerLogic.start()

  this.step_manager.event.register('stepChanged', this, function (stepIndex) {
    if (!stepIndex) {
      return self.event.trigger('endDebug')
    }

    self.debugger.codeManager.resolveStep(stepIndex, tx)
    self.step_manager.event.trigger('indexChanged', [stepIndex])
    self.vmDebuggerLogic.event.trigger('indexChanged', [stepIndex])
    self.registerAndHighlightCodeItem(stepIndex)
  })

  loadingCb()
  this.debugger.debug(tx)
}

Debugger.prototype.unload = function () {
  this.debugger.unLoad()
  this.event.trigger('debuggerUnloaded')
}

module.exports = Debugger

@@ -2867,16 +2867,18 @@
 t) =%3E %7B%0A
+  
   if (th
@@ -2938,21 +2938,25 @@
 solve()%0A
+  
   %7D%0A%0A
+  
   if (tx
@@ -2959,24 +2959,26 @@
  (tx) %7B%0A    
+  
 if (!tx.to) 
@@ -2975,24 +2975,26 @@
  (!tx.to) %7B%0A
+  
       tx.to 
@@ -3034,24 +3034,26 @@
 en('0')%0A    
+  
 %7D%0A      self
@@ -3103,21 +3103,27 @@
 e()%0A
+  
   %7D%0A%0A  
+ 
+ 
 try %7B%0A
+  
     
@@ -3155,24 +3155,26 @@
 ) !== -1) %7B%0A
+  
       return
@@ -3360,24 +3360,26 @@
 er)%0A        
+  
 let tx = res
@@ -3378,24 +3378,26 @@
 tx = result%0A
+  
         self
@@ -3457,17 +3457,23 @@
     
+  
 %7D)%0A    
+ 
+ 
 %7D%0A
+  
     
@@ -3694,24 +3694,26 @@
 mber)%0A      
+  
 let tx = res
@@ -3712,24 +3712,26 @@
 tx = result%0A
+  
       self.d
@@ -3781,19 +3781,23 @@
 e()%0A    
+  
 %7D)%0A
+  
   %7D catc
@@ -3800,24 +3800,26 @@
 catch (e) %7B%0A
+  
     console.
@@ -3866,16 +3866,18 @@
 essage)%0A
+  
   %7D%0A  %7D)
@@ -4767,18 +4767,83 @@
 if (
-!stepIndex
+typeof stepIndex !== 'number' %7C%7C stepIndex %3E= self.step_manager.traceLength
 ) %7B%0A

fix health checking