module.exports = function (app, enabMods, con, otherContext) {
  const wc = require('pp-which-country');
  const _ = require("underscore");
  const libs = require("../libs/metaHandler.lib");
  const jLib = require("../libs/jsonHand.lib");
  const stringify = require('safe-stable-stringify')
  const mailValidator = require("deep-email-validator")
  const tools = require("../functions");

  // Returns all installed and non-limited modules
  app.get("/api/allModules", function (req, res) {
    res.setHeader("Content-Type", "application/json");

    const allModules = [];
    // Loop though all modules and sort all interesting ones into `allModules`
    for (let i = 0; i < enabMods.length; i++) {
      if (enabMods[i].getModuleMeta().limited == false) {
        allModules.push(enabMods[i].getModuleMeta());
      }
    }
    // Return all modules
    res.send(allModules);
  });

  // Returns all tags with usages amounts
  app.get("/api/allTags", function (req, res) {
    res.setHeader("Content-Type", "application/json");


    // Return all tags
    const allTags = otherContext.tags
    //allTags = allTags.sort(function(a,b) { return parseInt(a.z) - parseInt(b.z) } );
    const response = []
    for (const key in allTags) {
      if (allTags.hasOwnProperty(key)) {
          console.log(`${key}: ${allTags[key]}`);
          response.push({key: key, amount: allTags[key]})
      }
  }
    
    res.send(response);
  });

  // The report API endpoint
  app.get("/api/internal/report", function (req, res) {
    res.setHeader("Content-Type", "application/json");
    // First check if all needed parameters are set
    const point_id = req.query.point_id;
    const mail = req.query.mail;
    const reason = req.query.reason;
    if (!point_id || !mail || !reason) {
      res.send(JSON.stringify({
        "status": "error",
        "message": "Missing parameters"
      }));
    } else {
      // Check if the point_id is valid
      const splited = point_id.split("_");
      if (splited.length != 2) {
        res.send(JSON.stringify({
          "status": "error",
          "message": "Invalid point_id"
        }));
      } else {
        let isValid = false;
        for (let i = 0; i < enabMods.length; i++) {
          if (enabMods[i].getModuleMeta().exactName == splited[0] && enabMods[i].getModuleMeta().limited == false) {
            isValid = true;
          }
        }
        if (isValid) {
          // Check if the mail is valid
          const mailValidationRes = mailValidator.validate({ email: mail, validateSMTP: false, validateTypo: false });
          mailValidationRes.then(function (resp) {
            if (resp.valid) {
              const sqlQuery = "INSERT INTO `reports` (`point_id`, `mail`, `comment`) VALUES (?, ? , ?);";
              con.query(sqlQuery, [point_id, mail, reason], function (err, rows, fields) {
                tools.handleMysqlErrors(err, "API");
                res.send(JSON.stringify({
                  "status": "success",
                  "message": "Report sent"
                }));
              });

            } else {
              res.send(JSON.stringify({
                "status": "error",
                "message": "Invalid mail",
                "cause": resp
              }));
            }
          })

        } else {
          res.send(JSON.stringify({
            "status": "error",
            "message": "Invalid point_id"
          }));
        }
      }
    }
  });



  app.get("/api/retrieve", function (req, res) {
    // Make sure all parameters are there
    if (
      req.query.uid != undefined
    ) {
      // All parameters are there
      res.setHeader("Content-Type", "application/json");

      const uid = req.query.uid;
      let result = undefined;
      console.warn("Retrieving data for UID: " + uid);
      // Go thorugh all modules and find the one which should handle the request
      for (let i = 0; i < enabMods.length; i++) {
        if (enabMods[i].getModuleMeta().exactName == uid.split("_")[0] && enabMods[i].getModuleMeta().limited == false) {
          
          result = enabMods[i].queryItem(uid);
          console.warn("Found module: " + enabMods[i].getModuleMeta().exactName + " with result: " + result);
        }
      }
      // Because DBs are slow the module only returns a Promise and we have to wait for it
      Promise.all([result]).then(function (results) { res.send(results) });
    } else {
      // A parameter is missing
      res.status(400);
      res.setHeader("Content-Type", "application/json");
      res.send(JSON.stringify({ state: "Failed", message: "Missing arguments" }));
    }
  });

  app.get("/api/getPOI", function (req, res) {
    // All parameters are there
    res.setHeader("Content-Type", "application/json");
    // Check if the bounds are valid so `lat;lng` is present
    // First check if the `;` is present and .split() returns an array with 2 elements
    const boundNorthEast = req.query.boundingEast.split(";");
    const boundSouthWest = req.query.boundingWest.split(";");
    let filterdModules = [];
    if (boundNorthEast.length == 2 && boundSouthWest.length == 2) {
      // Length is valid. Now check if the values are numbers and they are not infinte
      if (_.isFinite(boundNorthEast[0]) && _.isFinite(boundNorthEast[1]) && _.isFinite(boundSouthWest[0]) && _.isFinite(boundSouthWest[1])) {
        // Values are valid
        const boundNorthEastLat = parseFloat(boundNorthEast[0]);
        const boundNorthEastLng = parseFloat(boundNorthEast[1]);
        const boundNorthEastLatLng = { lat: boundNorthEastLat, lng: boundNorthEastLng };
        const boundSouthWestLat = parseFloat(boundSouthWest[0]);
        const boundSouthWestLng = parseFloat(boundSouthWest[1]);
        const boundSouthWestLatLng = { lat: boundSouthWestLat, lng: boundSouthWestLng };
        // Get the country of both edges
        // If the country is diffrent, set the country to *
        const countryNorthEast = wc([boundNorthEastLng, boundNorthEastLat]);
        let countrySouthWest = wc([boundSouthWestLng, boundSouthWestLat]);
        if (countryNorthEast != countrySouthWest) {
          // Country is the same
          countrySouthWest = "*";
        }

        // Get all POIs in the given bounds
        const usefullMods = []
        for (let i = 0; i < enabMods.length; i++) {
          const meta = enabMods[i].getModuleMeta()
          //   Is the country in the module's country list?     Or is it "*"?             Or did the system decide is doesnt matter	
          if ((meta.country.indexOf(countrySouthWest) > -1 || meta.country[0] == "*" || countrySouthWest == "*") && meta.limited == false) {
            usefullMods.push(enabMods[i])
          }
        }

        // JSON filter
        if (jLib.IsJsonString(req.query.filter)) {
          const jsonData = JSON.parse(req.query.filter);
          filterdModules = usefullMods.filter(function (elm) {
            // Loops through all elements in a list and lets you decide if weather to keep 'em or not
            let amount = 0;
            for (const fId in jsonData.filters) {
              // Do this is each provided filter
              const fi = jsonData.filters[fId]; // Gets the current filters name
              switch (fi[0]) {
                case "tag":
                  if (jsonData.conjunction == "OR") {
                    if (libs.returnTags(elm).includes(fi[1])) {
                      return elm;
                    } else {
                      break;
                    }
                  } else {
                    if (libs.returnTags(elm).includes(fi[1])) {
                      amount++;
                    }
                    break;
                  }
                case "country":
                  if (jsonData.conjunction == "OR") {
                    if (libs.returnCountries(elm).includes(fi[1])) {
                      return elm;
                    } else {
                      break;
                    }
                  } else {
                    if (libs.returnCountries(elm).includes(fi[1])) {
                      amount++;
                    }
                    break;
                  }
                case "source":
                  if (jsonData.conjunction == "OR") {
                    if (elm.getModuleMeta().exactName == fi[1]) {
                      return elm;
                    } else {
                      break;
                    }
                  } else {
                    if (elm.getModuleMeta().exactName == fi[1]) {
                      amount++;
                    }
                    break;
                  }
              }
            }
            if (jsonData.conjunction == "AND") {
              if (amount == jsonData.filters.length) {
                return elm;
              }
            }
          });
        } else { // No filtering
          filterdModules = enabMods;
        }


        // Create all promises
        const allProms = []; // A list for all promises
        for (let i = 0; i < filterdModules.length; i++) {
          const module = filterdModules[i];
          const temp = module.queryData(boundNorthEastLatLng, boundSouthWestLatLng, true); // Returns a promise of the results, because DBs are slowwwww
          allProms.push(temp); // Put the promises into a list
        }

        // Wait for all promises and return them
        Promise.all(allProms).then(function (result) {
          const allResultsInOne = []
          for (let i = 0; i < result.length; i++) {
            for (let d = 0; d < result[i].length; d++) {
              allResultsInOne.push(result[i][d])
            }
          }
          res.send(stringify(allResultsInOne));
        }).catch(function (error) {
          console.error(error)
          res.status(500).send(error); // We should probably not do this in a PROD env but    FIXME: Only in DEV env
        });
      } else {
        // Values are not valid
        res.status(400);
        res.send(JSON.stringify({ state: "Failed", message: "Bounding box is not valid" }));
        return (0);
      }
    } else {
      res.send(
        JSON.stringify({ state: "Failed", message: "Invalid arguments" })
      );
      res.status(400);
      return;
    }
  });


  app.get("/api/getPOILocations", function (req, res) {
    // Make sure all parameters are there

    // All parameters are there
    res.setHeader("Content-Type", "application/json");
    if (_.isFinite(req.query.lat) && _.isFinite(req.query.lng) && _.isFinite(req.query.radius)) {
      const lng = parseFloat(req.query.lng);
      const lat = parseFloat(req.query.lat);
      const radius = parseFloat(req.query.radius);

      // Filter by usefull modules
      const country = wc([lng, lat]); // Find out which country is at this location
      const usefullMods = [];
      // Go through all modules and find all fiting ones
      for (let i = 0; i < enabMods.length; i++) {
        const meta = enabMods[i].getModuleMeta()
        if ((meta.country.indexOf(country) > -1 || meta.country[0] == "*") && meta.limited == false) {
          usefullMods.push(enabMods[i])
        }
      }

      const allProms = [];
      for (let i = 0; i < usefullMods.length; i++) {
        const module = usefullMods[i]
        const temp = module.queryData(lat, lng, radius) // Returns a promise of the results
        allProms.push(temp)
      }

      // Wait for all promises to be resolved
      Promise.all(allProms).then(function (result) {
        const allResultsInOne = []
        for (let i = 0; i < result.length; i++) {
          for (let d = 0; d < result[i].length; d++) {
            allResultsInOne.push(result[i][d])
          }
        }
        res.send(allResultsInOne);
      }).catch(function (error) {
        console.error(error)
        res.status(500).send(error); // TODO: again only do it in DEV envs
      });
    } else {
      res.send(
        JSON.stringify({ state: "Failed", message: "Invalid arguments" })
      );
      res.status(400);
      return;
    }
  });
}