import webpage from 'webpage';
import evaluates from './evaluates';
import URI from '../node_modules/urijs/src/URI.js';

export default class Crawler {

  constructor() {
    this.page = null;

    this.modules = [];

    this.index = 0;
    this.limit = null;
    this.searchLinks = true;

    this.baseUrl = null;
    this.toCrawl = [];
    this.result = {};

    this.crawlingEnd = null;
  }

  /*********** MAIN FUNCTIONS **************/

  invokeModules(event) {
    var eventName = event;
    [].shift.apply(arguments);

    this.modules.forEach((m) => {
      if (m && m.hasOwnProperty(eventName)) {
        m[eventName].apply(null, arguments);
      }
    });
  }

  filterUrls(urls) {
    var self = this;

    return urls
      .filter((url, index) => {
        return (urls.indexOf(url) === index) && (url.indexOf(self.baseUrl) === 0 || !new RegExp('^(#|ftp|javascript|http|mailto|tel)').test(url));
      })
      .map((url) => {
          return new URI(url, self.baseUrl);
      })
      .filter((url) => {
        return (url.hostname === self.baseUrl.hostname) && !self.result[url.toString()] && (self.toCrawl.indexOf(url.toString()) === -1);
      })
      .map((url) => {
        return url.toString();
      });
  }

  /*********** PHANTOM FUNCTIONS **************/

  init(modules, config) {
    var self = this;

    if (config.limit) {
      this.limit = config.limit;
    }

    // Init modules
    self.modules = modules;
    this.invokeModules('init', config);

    // Create PhantomJS page.
    var page = webpage.create();
    page.onResourceRequested = (requestData, networkRequest) => { self.invokeModules('onResourceRequested', requestData, networkRequest); };
    page.onResourceReceived = (response) => { self.invokeModules('onResourceReceived', response) };
    page.onLoadFinished = (status) => { self.invokeModules('onLoadFinished', status) };
    self.page = page;
  }

  start(baseUrl) {
    this.baseUrl = new URI(baseUrl);
    this.toCrawl[this.index] = baseUrl;

    this.crawl(this.toCrawl[this.index]);
  }

  crawl(url) {
    var self = this;

    if (!this.result[url]) {
      this.result[url] = {};

      this.invokeModules('initPage', this.result[url]);

      this.page.open(url, (status) => {
        console.log(url + ': ' + status);

        self.invokeModules('pageLoaded', this.page);

        if (self.searchLinks) {
          var urls = self.page.evaluate(evaluates.findUrls);
          self.toCrawl = self.toCrawl.concat(self.filterUrls(urls));
        }

        self.next();

      });
    }
    else {
      self.next();
    }
  }

  next() {
    this.index++;

    if (this.index < this.limit && this.toCrawl[this.index]) {
      this.crawl(this.toCrawl[this.index]);
    }
    else {
      if (this.crawlingEnd) {
        this.crawlingEnd();
      }
    }
  }
}

@@ -687,38 +687,16 @@
 urls) %7B%0A
-    var self = this;%0A%0A
     retu
@@ -943,20 +943,20 @@
 RI(url, 
-self
+this
 .baseUrl
@@ -1025,20 +1025,20 @@
 ame === 
-self
+this
 .baseUrl
@@ -1283,38 +1283,16 @@
 nfig) %7B%0A
-    var self = this;%0A%0A
     if (
@@ -1371,20 +1371,20 @@
 les%0A    
-self
+this
 .modules
@@ -1561,28 +1561,28 @@
 quest) =%3E %7B 
-self
+this
 .invokeModul
@@ -1682,28 +1682,28 @@
 ponse) =%3E %7B 
-self
+this
 .invokeModul
@@ -1780,20 +1780,20 @@
 s) =%3E %7B 
-self
+this
 .invokeM
@@ -1828,24 +1828,25 @@
 tus) %7D;%0A
+%0A
     
-self
+this
 .page = 
@@ -2019,30 +2019,8 @@
 ) %7B%0A
-    var self = this;%0A%0A
     
@@ -2222,20 +2222,20 @@
         
-self
+this
 .invokeM
@@ -2280,20 +2280,20 @@
     if (
-self
+this
 .searchL
@@ -2321,20 +2321,20 @@
  urls = 
-self
+this
 .page.ev
@@ -2371,20 +2371,20 @@
         
-self
+this
 .toCrawl
@@ -2386,20 +2386,20 @@
 Crawl = 
-self
+this
 .toCrawl
@@ -2406,20 +2406,20 @@
 .concat(
-self
+this
 .filterU
@@ -2445,28 +2445,28 @@
  %7D%0A%0A        
-self
+this
 .next();%0A%0A  

delete self, use 'this' shared by arrows