text: const Logger = require("./Logger.js")();
  const IFC = require("./InspectorFrontController.js");
  const fs = require("fs");
  const Path = require("path");
  const InMemoryDb = require("../connectors/inmemory/InMemoryDb.js").InMemoryDb;
  //const UT = require("./Utils.js");
  
  const TASK_CODE = {
      NO_RESULT: 0,
      SUCCESS: 1,
      DATA_UPDATE: 2
  };
  
  const TYPE = {
      BOOT: 'BOOT',
      POST_APP_SCAN: 'POST_APP_SCAN',
      POST_PLATFORM_SCAN: 'POST_PLATFORM_SCAN',
      POST_DEV_SCAN: 'POST_DEV_SCAN',
      ON_DEMAND: 'ON_DEMAND',
  };
  
  class StaticTask{
  
      constructor(config){
          this.task = null;
          this.condition = null;
          this.onDataUpdate = null;
          this.onNoResult = null;
          this.onSuccess = null;
  
          for(let i in config) this[i] = config[i];
      }
  
      setCondition(fn){
          this.task = fn;
      } 
  
      setTask(fn){
          this.condition = fn;
      }
  
      onSuccess(fn){
          this.onSuccess = fn;
      }
  
      onNoResult(fn){
          this.onNoResult = fn;
      }
  
      onDataUpdate(fn){
          this.onDataUpdate = fn;
      }
  
      exec(ctx, event){
          if(this.condition != null && this.condition(ctx))
              this.task(ctx, event);
          else
              this.task(ctx, event);
      }
  }
  
  
  
  class Inspector{
      
      constructor(config){
          this.id = null;
          this.name = null;
          this.description = null;
  
          this.context = null;
          this.hookSet = null;
          this.staticTasks = null;
          this.running = false;
          this.listener = {};
          this.events = [];
          this.gui_available = false;
          this.frontController = null;
          this.preRegisteredTags = [];
          this.db = null;
  
          /**
           * @type {String}
           * @field
           */
          this.color = null;
  
          this.installed = false;
          this.step = TYPE.BOOT;
  
          for(let i in config){
              this[i] = config[i];
              if(i==="hookSet"){
                  this.id = config[i].id;
                  this.name = config[i].name;
                  this.description = config[i].description;
              }
          }
  
          return this;
      }
  
  
      isInstalled(){
          return this.installed;
      }
  
      useGUI(){
          this.gui_available = true;
      }
  
      useMemoryDB( pConnector = null){
          this.db = new InMemoryDb(pConnector);
  
          return this.db;
      }
  
      getDB(){
          return this.db;
      }
  
  
      /**
       * To forward an HTTP GET request from the web server handler to the inspector front controller if available
       * Internal use
       */
      performGet(req,res){
          if(this.frontController.hasHandler(IFC.HANDLER.GET)){
              return this.frontController.performGet(req,res);
          }else{
              return { error: true, msg:"Unavailable GET handler for this inspector" };
          }
      }
  
  
      /**
       * To forward an HTTP POST request from the web server handler to the inspector front controller if available
       * Internal use
       */
      performPost(req,res){
          if(this.frontController.hasHandler(IFC.HANDLER.POST)){
              return this.frontController.performPost(req,res);
          }else{
              return { error: true, msg:"Unavailable POST handler for this inspector" };
          }
      }
  
      /**
       * To invoke the StaticTask instances associated to the given event. 
       */
      broadcastEvent(event){
          let event_type = event.type;
  
          //Logger.info( event_type, this.listener[event_type]);
          //console.log(this.listener);
          if(this.listener[event_type] != null){
              for(let i=0; i<this.listener[event_type].length; i++){
                  // TODO : async / co
                  // console.log(this.listener[event_type][i]);
                  this.listener[event_type][i].exec(this.context, event, this);
              }
          }
          return true;
      }
  
      /**
       * To declare new event handler. If the param `task`is  a function, a new StaticTask is create implicitly.
       */
      on(event_type, task){
          if(this.listener[event_type] == null)
              this.listener[event_type] = [];
          
          if(task instanceof StaticTask)
              this.listener[event_type].push(task);
          else
              this.listener[event_type].push(new StaticTask(task));
              
          return this;
      }
  
  
      registerTagCategory(name,tags){
          this.preRegisteredTags.push({ name:name, tags:tags });
      }
  
  
      /**
       * @method
       */
      injectContext(ctx){
          this.context = ctx;
  
          if(this.hookSet != null){
              this.hookSet.injectContext(ctx);
              Logger.info("[Inspector::injectContext][HookSet] "+this.id+" registered !");
          }else{
              Logger.info("[Inspector::injectContext] "+this.id+" has not hook set.");
          }
  
          // register front controller
          let path = Path.join(__dirname,"..","inspectors",this.id,"service","main.js");
          if(fs.existsSync(path)){
              this.frontController = require(path).injectContext(ctx);
              Logger.info("[Inspector::injectContext][FrontController] "+this.id+" registered !");
          }
          /*
          else if(Path.basename(ctx.config.getDexcaliburPath()) !== "src"){
              path = Path.join(ctx.config.getDexcaliburPath(),"inspectors",this.id,"service","main.js");
              if(fs.existsSync(path)){
                  this.frontController = require(path).injectContext(ctx);
                  Logger.info("[Inspector::injectContext][FrontController] "+this.id+" registered !");
              }
          }*/
  
  
          // declare TagCategory
          let anal = ctx.getAnalyzer();
          for(let i=0; i<this.preRegisteredTags.length; i++){
              anal.addTagCategory(this.preRegisteredTags[i].name, this.preRegisteredTags[i].tags)
          }
  
          /*if(this.db instanceof InMemoryDb){
              //this.db.setContext(this.context);
          }*/
  
          return this;
      }
  
      setStartStep(pStep){
          this.step = pStep;
      }
  
      getStartStep(){
          return this.step;
      }
  
      setHookSet(hs){
          this.hookSet = hs;
          this.hookSet.color = this.color;
  
          if(this.id==null) this.id = hs.id;
          if(this.name==null) this.name = hs.name;
          if(this.description==null) this.description = hs.description;
      }
  
      getHookSet(){
          return this.hookSet;
      }
  
      getStaticTasks(){
          return this.staticTasks;
      }
  
      getStaticTask(name){
          return this.staticTasks[name];
      }
  
      getID(){
          return this.id;
      }
  
      deploy(){
          this.running = true;
          this.hookSet.deploy();
      };
  
      // Inspector life-cycle
      /*
      turnOn(){
          this.running = true;
          if(this.hookSet!=null)
              this.hookSet.enable();
      }
  
      turnOff(){
          return this.staticTasks[name];
          if(this.hookSet!=null)
              this.hookSet.disable();
      }*/
  
      /**
       * emit a new event on the main event bus
       */
      emits(name,event){
          if(this.events.indexOf(name)===-1)
              this.events.push(name);
  
          this.context.bus.send({ 
              type: name, 
              data: event 
          });
      }
  
      /**
       * 
       * @param {*} pStep
       * @method 
       */
      isStartAt(pStep){
          return (this.step === pStep)
      }
  
      /**
       * 
       * @param {*} callback 
       * @method
       */
      restore(callback=null){
          let self = this;
          let savePath = Path.join(this.context.workspace.getSaveDir(), this.id+".json");
          fs.exists(savePath, function(exist){
              if(!exist){
                  return ;
              }
              
              fs.readFile(savePath, 'ascii', function(err, data){
                  let o = JSON.parse(data);
                  self.db.unserialize(o);
                  if(typeof callback === 'function')
                      callback(self);
              })
          })
      }
  
      /**
       * @method
       */
      save(){
          if(!this.db instanceof InMemoryDb) return null;
  
          let self = this;
          let savePath = Path.join(this.context.workspace.getSaveDir(), this.id+".json");
          fs.exists(savePath, function(exist){
              if(exist){
                  fs.unlinkSync(savePath);
              }
              
              fs.open(savePath, 'w+', function(err, fd){
                  if(err){
                      console.log("Save file cannot be created");
                      return;
                  }
  
                  let data = self.db.serialize();
                  fs.write(fd, JSON.stringify(data), function(err){
                      if(err){
                          console.log("Save file cannot be created");
                          return;
                      }
                      console.log("Inspector "+self.id+" backed up");
                      fs.close(fd,function(err){});
                  });
              })
          })
      }
  
      /**
       * To cast the current object to an object ready to be serialize (it avoids cyclic reference)
       * @returns {Object} 
       * @method
       */
      toJsonObject(){
          let o = {};
          o.id = this.id;
          o.description = this.description;
          o.name = this.name;
          o.running = this.running;
          o.events = this.events;
          o.hooks = this.hookSet.toJsonObject();
          o.listener = [];
          o.gui_available = this.gui_available;
          for(let i in this.listener)
              o.listener.push({ n:i });
          
          return o;
      }
  }
  
  
  module.exports = {
   //   InspectorController: InspectorController,
      Inspector: Inspector,
      StaticTask: StaticTask,
      RET: TASK_CODE,
      STEP: TYPE
  };