/**
 * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.
 * *
 * Copyright (C) 2011, 2012, 2013, 2014 Loic J. Duros
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see  <http://www.gnu.org/licenses/>.
 *
 */



var licenses = require('js_checker/license_definitions');
var simpleStorage = require("sdk/simple-storage");
const LAZY = licenses.types.LAZY;
var licenseRegex = [];
const END_OF_SCRIPT = require('html_script_finder/bug_fix').END_OF_SCRIPT;
const types = require("js_checker/constant_types");

const token = types.token;

var patternUtils = require('js_checker/pattern_utils').patternUtils;

var licStartLicEndRe = /@licstartThefollowingistheentirelicensenoticefortheJavaScriptcodeinthis(?:page|file)(.*)?@licendTheaboveistheentirelicensenoticefortheJavaScriptcodeinthis(?:page|file)/mi;
var licenseMagnet = /.*@license ?(magnet\:\?xt=urn\:btih\:[0-9A-Za-z]+).*/;
var licenseEndMagnet = /.*@license-end.*/i;
exports.freeCheck = {
  initLicenses: function (licenses) {
	  for (var item in licenses) {
	    this.stripLicenseToRegexp(licenses[item]);
	  }
  },

  /**
   * stripLicenseToRegexp
   *
   * Removes all non-alphanumeric characters except for the 
   * special tokens, and replace the text values that are 
   * hardcoded in license_definitions.js
   *
   */
  stripLicenseToRegexp: function (license) {
	  var i = 0, 
	      max = license.licenseFragments.length, 
	      item;

	  for (; i < max; i++) {
	    item = license.licenseFragments[i];
	    item.regex = patternUtils.removeNonalpha(item.text);

	    if (license.licenseFragments[i].type === LAZY) {

		    // do not permit words before. Since "Not" could be added
		    // and make it nonfree. e.g.: Not licensed under the GPLv3.
		    item.regex = '^(?!.*not).*' + item.regex;

	    }
      
	    item.regex = new RegExp(patternUtils.replaceTokens(item.regex), 'i'); 
	  }

	  return license;
  },

  /**
   * checkNodeFreeLicense
   *
   * Check if the node mentions a free license
   * in one of its comments.
   * 
   */
  checkNodeFreeLicense: function (n, queue) {
	  var strippedComment,
        magnetLink,
	      comment = this.getComment(n),
	      list = licenses.licenses,
	      i, j,
	      max,
	      regex,
	      frag, 
	      matchLicStart, 
        matchMagnet,
        license,
        isMagnetValid = false;

	  if (n.counter === 2 &&
	      n.parent != undefined &&
	      n.parent.type === token.SCRIPT &&
	      comment != undefined && 
	      comment != " ") {
	    strippedComment = patternUtils.removeNonalpha(comment);
	    matchLicStart = strippedComment.match(licStartLicEndRe);
      console.debug("matchMagnet is", matchMagnet);
	    if (matchLicStart) {
		    strippedComment = matchLicStart[1];
	      for (license in list) {	    
		      frag = list[license].licenseFragments;
		      max = list[license].licenseFragments.length;
		      for (i = 0;i < max; i++) {
			      if (frag[i].regex.test(strippedComment)) {
     			        return {licenseName: list[license].licenseName,
                      type: types.checkTypes.FREE} ;

			      }
		      }
	      }
	    }
      return this.matchMagnet(comment, queue);  /// check for @license -- @license-end notation.
	  }

  },

  /**
   *  matchMagnet
   *  Attempts to find valid @license [magnet] 
   *  and @license-end notation.
   */
  matchMagnet: function (comment, queue) {
    let matchMagnet = comment.match(licenseMagnet);
    if (matchMagnet) {
      let magnetLinkRe = new RegExp(matchMagnet[1].replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"));
      let list = licenses.licenses;
      let queue_end = queue.length;
      
      for (var license in list) {
		    frag = list[license].canonicalUrl;
        console.debug("frag is ", frag);
        if (frag != undefined) {
		      max = list[license].canonicalUrl.length;
          console.debug("max is", max);
		      for (i = 0;i < max; i++) {
            console.debug("current frag is", frag[i]);
			      if (frag[i].match(magnetLinkRe)) {
              for (let i = 0; i < queue_end; i++) {
                console.debug(queue[i]);
                let n = queue[i];
                comment = this.getComment(n);
                if (comment != undefined &&
                    comment.match(licenseEndMagnet) &&
                    this.checkIsLastNode(n)) {
                  // found a closing match. Just accept this script.
                  return {licenseName: list[license].licenseName,
                          type: types.checkTypes.FREE_SINGLE_ITEM};
                }
              }
            }
			    }
		    }
      }
    }
    return;
  },
  
  /**
   *  checkIsLastJsNode.
   *  returns true if n is the last node.
   *  Or if nodes before it are only comments etc (not valid code.)
   *  A special LibreJS node is appended at the end of a script tree to
   *  check if this is the last (and also for narcissus to keep the last comment
   *  in the tree.)
   * TODO: Refactor LibreJS so that END nodes can have a comment.
   */
  checkIsLastNode: function (n) {
    // first check if the comment is part of the very last statement.
    if (n.value == "this" && n.next == undefined) {
      // just make sure the last node is indeed our harmless bit of
      // js.
      if (n.tokenizer) {
        let source = n.tokenizer.source;
        let substring = source.substr(n.start, n.end+23);
        if (substring == END_OF_SCRIPT) {
          return true;
        }
        else {
          console.debug("substring is ", substring);
          return false;
        }
      }
      console.debug("Hurra! This is the end of our script");
      return true;
    }

    // isn't our last node. 
    return false;
  },
  
  /**
   * getComment
   *
   * Grab the comment(s) from the node. Concatenates
   * multiple comments.
   * 
   */
  getComment: function (n) {

	  var i = 0, length, comment = "";

	  if (n.blockComments == undefined || n.blockComments == " ") {
	    return;
	  }

	  length = n.blockComments.length;
	  if (length > 0) {
	    for (; i < length; i++) {
		    comment += n.blockComments[i];
	    }
	  }
	  if (comment == "") {
	    return;
	  }
	  return comment;

  }
  
};

exports.freeCheck.initLicenses(licenses.licenses);
