/*jslint nomen: true, indent: 2, maxerr: 3, maxlen: 80*/
/*global window, RSVP, rJS, jIO, Handlebars*/
(function (window, RSVP, rJS, jIO, Handlebars) {
    "use strict";

    var gadget_klass = rJS(window),
      template_element = gadget_klass.__template_element,
      concurrent_requests = 3,
      proxies = [
        "https://softinst116265.host.vifib.net/erp5/ERP5Site_getHTTPResource",
        "https://softinst116446.host.vifib.net/erp5/ERP5Site_getHTTPResource"
      ],
      build_template_progress = Handlebars.compile(template_element
                                .getElementById("build-template-progress")
                                .innerHTML);

    function get_random_id() {
      return '_' + Math.random().toString(36).substr(2, 9);
    }

    function showError(error_message) {
      console.log(error_message);
    }

    gadget_klass

    .declareAcquiredMethod("getSetting", "getSetting")
    .declareAcquiredMethod("setSetting", "setSetting")
    .declareAcquiredMethod("notifySubmitted", "notifySubmitted")

    .ready(function () {
      var gadget = this;
      return this.getDeclaredGadget("model")
      .push(function (model) {
        return gadget.changeState({
          model_gadget: model
        });
      });
    })

    .declareMethod("preRenderDocument", function (parent_options) {
      var gadget = this;
      return gadget.jio_get(parent_options.jio_key)
      .push(function (parent_document) {
        return parent_document;
      });
    })

    .declareAcquiredMethod("add_to_index", "add_to_index")
    .declareAcquiredMethod("save_index", "save_index")

    .declareMethod("getIndexToBuild", function () {
      return this.getSetting('build_dict')
        .push(function (build_dict) {
          var key,
              entry;
          for (key in build_dict) {
            if (!entry)
              entry = build_dict[key];
            else {
              if (entry.status === build_dict[key].status)
                continue;
              if (entry.status && !build_dict[key].status)
                entry = build_dict[key];
              else if ((entry.status === "built" ||
                        entry.status === "built failed") &&
                       (build_dict[key].status !== "built" &&
                        build_dict[key].status !== "built failed"))
                entry = build_dict[key];
            }
          }
          if (entry && (entry.status === 'built' ||
              entry.status === "built failed"))
            return;
          return entry;
        });
    })

    //declare service to build index in background
    .declareService(function () {
      var gadget = this;

      function build() {
        var status;
        return gadget.getIndexToBuild()
          .push(function (entry) {
            var link_list,
                tmp_element;
            if (!entry)
              // wait 5s and check again if there is something to build
              return RSVP.delay(5000);
            else {
              gadget.state.current_build = entry;
              link_list = entry.links.split("\n").filter(l => l.length > 0);
              return gadget.build_index(link_list, entry.index_name)
                .push(function () {
                  console.log("done building index");
                  return gadget.save_index(entry.index_name)
                    .push(function (result) {
                      status = 'built';
                      return gadget.notifySubmitted({
                        message: "Index '" + entry.index_name + "' built : " +
                          result + " item(s) added.",
                        status: "success"
                      });
                    });
                })
                .push(undefined, function (my_error) {
                  showError(my_error);
                  gadget.state.current_build = undefined;
                  status = 'build failed';
                  return gadget.notifySubmitted({
                    message: "Index '" + entry.index_name + "' built failed!",
                    status: "error"
                  });
                })
                .push(function () {
                  gadget.state.current_build = undefined;
                  return gadget.getSetting('build_dict', {});
                })
                .push(function (current) {
                  if (!current[entry.jio_key]) {
                    console.log("Error: cannot get key '" + entry.jio_key +
                                "' in build_dict");
                    return;
                  }
                  current[entry.jio_key].status = status;
                  return gadget.setSetting('build_dict', current);
                })
                .push(function () {
                  return gadget.state.model_gadget.updateIndexDoc(
                    entry.jio_key,
                    {status: 'built'}
                  );
                })
                .push(function () {
                  return gadget.state.model_gadget.pushIndex(entry.index_name);
                });
            }
          })
          .push(function () {
            return build();
          });
      }

      return build();
    })

    .onLoop(function () {
      var gadget = this,
          container = document.getElementById('buildactivities'),
          element;

      if (!container)
        return;
      element = container.querySelector('.build-list');
      if (!element)
        return;
      return gadget.getSetting('build_dict')
        .push(function (build_dict) {
          var key,
              tmp_element,
              building;
          if (build_dict) {
            while (element.firstChild) {
              element.firstChild.remove();
            }
            for (key in build_dict) {
              building = false;
              tmp_element = document.createElement('tr');
              if (gadget.state.current_build && (build_dict[key].index_name ===
                  gadget.state.current_build.index_name)) {
                tmp_element.classList.add('building');
                building = true;
              }
              tmp_element.innerHTML = build_template_progress({
                title: build_dict[key].index_name,
                status: build_dict[key].status || 'not built',
                building: building
              });
              element.appendChild(tmp_element);
            }
          }
        });
    }, 5000)

    .declareMethod("build_index", function (links, index_name) {
      var gadget = this,
          promise_list = [],
          i;
      for (i = 0; i < links.length; i += 1) {
        promise_list.push(gadget.get_and_handle(links[i], index_name));
      }
      return RSVP.all(promise_list);
    })

    .declareMethod("get_and_handle", function (link, index_name) {
      var gadget = this,
          rng,
          proxy;
      return new RSVP.Queue()
        .push(function () {
          rng = Math.floor(Math.random() * Math.floor(proxies.length));
          proxy = proxies[rng];
          return jIO.util.ajax({url : proxy + "?url=" + link});
        })
        .push(function (page) {
          if (!page)
            return;
          if (link.includes("rss") || link.includes("atom")) {
            return gadget.parse_rss(
              page.currentTarget.response,
              link,
              index_name);
          } else if (link.includes("xml")) {
            return gadget.parse_sitemap(
              page.currentTarget.response,
              link,
              index_name);
          }
        })
        .push(undefined, function (error) {
          console.log(error);
        });
    })

    .declareMethod("parse_rss", function (rss_file, file_name, index_name) {
      var gadget = this,
          promise_list = [],
          parser = new RSSParser({defaultRSS: 2.0});

      return new RSVP.Queue()
        .push(function () {
          return parser.parseString(rss_file);
        })
        .push(function (result) {
          var promise_list = [];
          //removes items without title
          result.items = result.items.filter(item => item.title);
          result.items.forEach(function (item) {
            if (!item.link || item.link.includes("https://localhost:"))
              item.link = "no-link";
            promise_list.push(gadget.add_to_index({
              id : get_random_id(),
              link : item.link,
              title : item.title,
              content : item.content
            }, index_name));
          });
          return RSVP.all(promise_list);
        });
    })

    .declareMethod("parse_sitemap",
                   function (links_file, file_name, index_name) {
      var gadget = this,
          links = new DOMParser().parseFromString(links_file, "text/xml")
            .getElementsByTagName("url"),
          links_modified = [],
          links_file_split,
          links_subset,
          i,
          j,
          queue = new RSVP.Queue(),
          parse_subset;

      parse_subset = function (subset) {
        queue
          .push(function () {
            return gadget.parse_subset(subset, index_name);
          });
      };
      if (links.length === 0) {
        links_file_split = links_file.split("\n");
        for (i = 0; i < links_file_split.length; i += 1) {
          if (links_file_split[i].includes("https://") ||
              links_file_split[i].includes("http://")) {
            links_modified[i] = [links_file_split[i]];
          }
        }
        if (links_modified.length === 0) {
          showError("no links found in sitemap file : " + file_name);
          return null;
        }
      } else {
        for (i = 0; i < links.length; i += 1) {
          links_modified[i] = [links[i]
                               .getElementsByTagName('loc')[0].textContent];
        }
      }

      for (i = 0; i < links_modified.length; i += concurrent_requests) {
        links_subset = [];
        if ((i + concurrent_requests) > links_modified.length) {
          links_subset = links_modified.slice(i);
        } else {
          links_subset = links_modified.slice(i, (i + concurrent_requests));
        }
        parse_subset(links_subset);
      }

      links = null;
      return queue;
    })

    .declareMethod("parse_subset", function (links, index_name) {
      var gadget = this,
          promise_list = [],
          i;
      for (i = 0; i < links.length; i += 1) {
        promise_list.push(gadget.get_sitemap_item(links[i], index_name, 1));
      }
      return RSVP.all(promise_list);
    })

    .declareMethod("get_sitemap_item", function (link, index_name, attempt) {
      var gadget = this;

      return new RSVP.Queue()
        .push(function () {
          var rng = Math.floor(Math.random() * Math.floor(proxies.length)),
              proxy = proxies[rng];
          return jIO.util.ajax({url : proxy + "?url=" + link});
        })
        .push(function (page) {
          if (page !== undefined) {
            return gadget.parse_sitemap_item(page, index_name);
          }
          else
            return null;
        })
        .push(undefined, function (my_error) {
          if (!my_error.target) {
            showError(my_error);
          }  else if (my_error.target.status === 500 && attempt <= 3) {
            return gadget.get_sitemap_item(link, index_name, attempt + 1);
          }
          else
            showError(my_error);
        });
    })

    .declareMethod("parse_sitemap_item", function (page, index_name) {
      var gadget = this,
          regex,
          title = "",
          tmp_div,
          content,
          result;
      regex = page.currentTarget.response.match(/<title[^>]*>([^<]+)<\/title>/);
      if (regex)
        title = regex[1];
      regex = page.currentTarget.response
        .match(/<body[^>]*>((.|[\n\r])*)<\/body>/gim);
      if (regex) {
        tmp_div = document.createElement("div");
        tmp_div.innerHTML = regex[0]
          // remove all <script> sections
          .replace(/(<script[^>]*>[^>]*.*<\/script>)/gm, '');
        // remove footers, headers
        tmp_div.querySelectorAll(
            'link, style, script, footer, header, noscript, picture')
          .forEach(function (item) {
          item.remove();
        });
      }
      if (!tmp_div)
        return;
      result = {
        id : get_random_id(),
        link : page.currentTarget.responseURL.slice(proxies[0].length),
        title : title,
        content : tmp_div.textContent || tmp_div.innerText || ""
      };
      // remove all useless character
      result.content = result.content
        .replace(/[\t\r\n]+/g, '')
        .replace(/ +(?= )/g, '');
      return gadget.add_to_index(result, index_name)
        .push(function () {
          while (tmp_div.firstChild) {
            tmp_div.removeChild(tmp_div.lastChild);
          }
          tmp_div = null;
          result = null;
        });
    });

  }(window, RSVP, rJS, jIO, Handlebars));
