/*
    $Revision$
    $Id$

    File:               imp_shared.js
    Description:        Helper functie
    Parameters:
        import_app_key  Importfunctie key
    Context:
    Note:               ========= LET OP LET OP =========
                        Dit bestand wordt ook gebruikt door ../../utils/gen_import/gen_import.wsf
                            Daar hebben we door CScript maar heel weinig functies!
                            DUS: Geen L() functie voor locale!!!!
                             EN: Geen Server.CreateObject maar new ActiveXObject
                        Dit bestand wordt ook gebruikt door ../api/api_gen_import.asp
                        ========= LET OP LET OP =========
*/

// fileStream moet onze data bevatten
// import_app_key de import-functie
// params import_key mag/zal leeg zijn voor het eerste bestand
//        customerId: "UWVA"
//        ref_key: optioneel een key die in FAC_IMPORT_REFKEY gaat.
//        fac_home: net boven de APPL dus bijv. "d:/apps/facilitor/"
//        filepathname: optioneel: stoppen we in fac_import_filenaam
//        keep_old: oude imports van dezelfde soort van maximaal keep_old
//                  seconden worden nog even aangehouden. Default 0
//                  Nodig als je meerdere parallelle imports wilt ondersteunen
//                  (vanuit api_gen_import bijvoorbeeld)
//        keep_backup: als fac_import_app_folder gezet is wordt een backup
//                     van de import file gemaakt onder fac_import_app_folder/BACKUP2015/....
//                     Merk op: vanuit gen_import.wsf wordt keep_backup *niet* gezet
//                              omdat gen_import.wsf (nog) zelf de backup maakt
//        stylesheet: optionele overrule van de import-XSL-stylesheet
function impReadStream(fileStream, import_app_key, params)
{
    params = params || {};
    if (!("keep_old" in params))
        params.keep_old = 0;

    var import_key = params.import_key || -1;
    if (import_key < 0)
    {
        var this_import_where = " fac_import_app_key = " + import_app_key;
        if (params.keep_old > 0)
            this_import_where += " AND fac_import_datum_gelezen <= SYSDATE - " + parseInt(params.keep_old) + " /60/60/24";

        // Hadden we nog iets openstaan voor deze import_app_key? Dan wissen we die eerst
        // We ondersteunen namelijk geen *twee* openstaande imports van dezelfde functie
        var sql = "DELETE FROM fac_import"
                + " WHERE " + this_import_where
                + "   AND fac_import_datum_verwerkt IS NULL";
        Oracle.Execute(sql); // cascadeert vanzelf FAC_IMP_LOG en FAC_IMP_FILE

        // Merk op: verwerkte FAC_IMPORT records blijven eeuwig bestaan
        // TODO: Ooit saved files opruimen op basis van fac_import_backupdir

        // Ook: *alle* oude FAC_IMP_FILE voor deze import gaan nu weg
        var sql = "DELETE FROM fac_imp_file"
                + " WHERE fac_import_key IN"
                + "    (SELECT fac_import_key"
                + "       FROM fac_import"
                + "      WHERE " + this_import_where + ")";
        Oracle.Execute(sql);

        // Tenslotte oude logging opruimen. De laatste laten we compleet staan,
        // van de rest alleen de samenvatting regels.
        var sql = "DELETE FROM imp_log"
                + " WHERE imp_log_status <> 'S'"
                + "   AND fac_import_key IN"
                + "         (SELECT fac_import_key"
                + "            FROM fac_import"
                + "           WHERE " + this_import_where + ")" // dezelfde soort
                + "   AND fac_import_key < "
                + "         (SELECT MAX(fac_import_key)"
                + "            FROM fac_import"
                + "           WHERE " + this_import_where + ")";
        Oracle.Execute(sql);

        var sql = "SELECT fac_s_fac_import_key.nextval FROM DUAL";
        var oRs = Oracle.Execute(sql);
        import_key = oRs(0).Value;
        oRs.Close;

        sql = "INSERT INTO fac_import"
            + "       (fac_import_key,"
            + "       fac_import_app_key,"
            + "       fac_import_refkey,"
            + "       fac_import_filenaam)"
            + " VALUES( " + import_key + ", "
            +               import_app_key + ", "
            +               (params.ref_key||"NULL") + ","
            +               safe.quoted_sql(params.filepathname, 256) + ")";
        Oracle.Execute(sql);
    }
    else
    {   // Mocht een import-functie *echt* iets met de filenaam willen doen dan kan hij hem zelf opzoeken
        // in fac_import. Voor multi-import slaan we daartoe de laatste filenaam op.
        sql = "UPDATE fac_import"
            + "   SET fac_import_filenaam = " + safe.quoted_sql(params.filepathname,256)
            + " WHERE fac_import_key = " + import_key;
        Oracle.Execute(sql);
    }
    var sql = "SELECT i.fac_import_app_code "
            + ",      i.fac_import_app_prefix "
            + ",      i.fac_import_app_xsl "
            + ",      i.fac_import_app_charset "
            + ",      i.fac_import_app_action "
            + ",      i.fac_import_app_folder"  // voor eventuele backup
            + "  FROM fac_import_app i"
            + " WHERE i.fac_import_app_key = " + import_app_key;
    var oRs = Oracle.Execute(sql);

    var import_app = oRs("fac_import_app_code").value;
    var import_action = oRs("fac_import_app_action").value;
    var procprefix = oRs("fac_import_app_prefix").value||"FAC";
    var charset = oRs("fac_import_app_charset").value;
    if (params.stylesheet)  // vanuit de API bv, die is sterker
        var xslfile = params.fac_home + "CUST/" + params.customerId + "/" + params.stylesheet;
    else
        var xslfile = oRs("fac_import_app_xsl").value?params.fac_home + "CUST/" + params.customerId + "/" + oRs("fac_import_app_xsl").value:"";
    var folder = oRs("fac_import_app_folder").Value;
    oRs.Close();

    var backDir = backupfolder(import_app, folder, import_key);
    // Registeren (ook al doen we er verder nog niets mee)
    sql = "UPDATE fac_import"
        + "   SET fac_import_backupdir = " + safe.quoted_sql(backDir)
        + " WHERE fac_import_key = " + import_key;
    Oracle.Execute(sql);

    if (backDir && params.keep_backup)
    {
        var oZIP = new ActiveXObject("SLNKDWF.Zip");
        oZIP.New(backDir + safe.filename(params.filepathname) + ".zip");
        fileStream.Position = 0;
        oZIP.ZipFromStream(params.filepathname, fileStream);
        oZIP.Close();
    }

    if( !fileStream.Size )   // Leeg bestand?
    {
      return { success: false,
               backDir: backDir, // Ongeldig bestand wel weg-moven
               warning: "Import error: file is empty" };
    }

    // Altijd naar win1252 converteren, dat heeft onze database ook
    fileStream.Position = 0;
    fileStream.Type = 2; // adTypeText nu
    fileStream.Charset = charset || 'Windows-1252'; // de bron

    var win1252Stream = new ActiveXObject("ADODB.Stream");
    win1252Stream.Open();
    win1252Stream.CharSet = "Windows-1252"; // het doel
    fileStream.copyTo(win1252Stream);
    //win1252Stream.SaveToFile(Server.MapPath("./win1252Stream.txt"), 2);

    if (xslfile)
    {
      // Zet xml om in string
      __Log("XML2STR: with XSL: " + xslfile);

      // Load the XML
      var source = new ActiveXObject("MSXML2.DOMDocument.6.0");
      source.async = false;
      source.resolveExternals = false; // van mij hoef je geen DTD's te controleren
      source.validateOnParse = false; // van mij hoef je geen DTD's te controleren
      source.setProperty("ProhibitDTD", false);  // niet moeilijk doen over DTD's

      var fileData; // Hier gaan we de data als string in stoppen
      win1252Stream.Position = 0
      if( ! source.loadXML(win1252Stream.ReadText) )   // Het bestand laden
      {
        return { success: false,
                 backDir: backDir, // Ongeldig bestand wel weg-moven
                 warning: "XML error: " + source.parseError.reason + " @ " + source.parseError.line + "." + source.parseError.linepos };
      }

      // Load the XSLT
      var style = new ActiveXObject("MSXML2.DOMDocument.6.0");
      style.async = false;
      style.resolveExternals = false; // van mij hoef je geen DTD's te controleren
      style.validateOnParse = false; // van mij hoef je geen DTD's te controleren
      style.setProperty("ProhibitDTD", false);  // niet moeilijk doen over DTD's
      if( ! style.load(xslfile) ) // De stylesheet laden
      {
        return { success: false,
                 // backDir: backDir, // het import bestand is gewoon geldig dus laten staan tot iemand de xsl-error oplost
                 warning: xslfile + "\nXSL error: " + style.parseError.reason + " @ " + style.parseError.line + "." + style.parseError.linepos };
      }
      fileData = source.transformNode(style); // terugstoppen in de fileData string
      // of?:    xslproc.output = win1252Stream;
      //         xslproc.transform();

    }
    else {
      win1252Stream.Position = 0;
      fileData = win1252Stream.ReadText;
    }

    //
    // fileData bevat nu de geimporteerde file. Wat gaan we er mee doen
    // A) Naar Oracle (via table fac_imp_file) en daar laten verwerken
    // B) Doorsturen naar SOAP service (want daar hebben we al afhandeling gebouwd)
    //

	var read_lines = -1;
    if (import_action == 'ORACLE')
    {
        // data naar de database naar FAC_IMP_FILE
        teller = 1;
        var sqls = [];
        //var fileDataArr = fileData.split(/[\r\n]/); // gaat mis bij CSV's met newline binnen quotes
        var fileDataArr = fileData.split(/\x0D\x0A/); // CR LF is verplicht. Unix heeft alleen LF en dat ondersteunen we niet
                                                      // (alleen LF kan wel binnen quotes voor een newline in een veld) FSN#18597
        if (fileDataArr.length == 1)
            var fileDataArr = fileData.split(/\x0A/); // Misschien alleen UNIX LF's?

        if (fileDataArr.length == 1)
            var fileDataArr = fileData.split(/\x0D/); // Misschien alleen MAC CR's?

        var sqllen = 0;
        for (var regel in fileDataArr)
        {
           var line = fileDataArr[regel];
           if (line != "")
           {
               sql = teller + ", " + safe.quoted_sql(line) + "," + import_key;
               sqllen += sql.length + 28; // grove schatting
               sqls.push(sql);
               teller = teller + 1;
           }
           if (sqllen > 10000) // empirisch bepaald: fatsoenlijke performance
           {
                Oracle.Execute("INSERT INTO fac_imp_file"
                        + " (fac_imp_file_index, fac_imp_file_line, fac_import_key) SELECT "
                        +  sqls.join(" FROM DUAL UNION ALL SELECT ") + " FROM DUAL");
                sqls = [];
                sqllen = 0;
           }
        }
        if (sqls.length) // de laatste
        {
            Oracle.Execute("INSERT INTO fac_imp_file"
                    + " (fac_imp_file_index, fac_imp_file_line, fac_import_key) SELECT "
                    +  sqls.join(" FROM DUAL UNION ALL SELECT ") + " FROM DUAL");
        }

        // De _IMPORT_ proc zal doorgaans de data van FAC_IMP_FILE naar bijv. FAC_IMP_CATALOGUS overzetten
        sql = "BEGIN " + procprefix + "_IMPORT_" + import_app + "(" + import_key + "); END;"
        Oracle.Execute(sql);

        var read_lines = teller-1;
        var summarytxt = "Ingelezen regels: " + read_lines;
    }
    else if (import_action.substr(0,5) == 'SOAP:') // Wordt gebruikt vanuit ge-e-mailde XML's
    {                                              // JGL: FSN#25603, volgens mij sinds 2013 nooit in de productie gebruikt
        var url = import_action.substring(5);

        var http_request = new ActiveXObject("Microsoft.XMLHTTP");
        //SXH_PROXY_SET_PROXY = 2;
        //http_request.setProxy(SXH_PROXY_SET_PROXY, "127.0.0.1:8888"); // Fiddler
        http_request.open("POST", url, false); // false zodat sync
        http_request.setRequestHeader("Content-Type", "text/xml; charset=utf-8")
        http_request.send(fileData);

        if ( http_request.status != 200)
        {
            var txt = "Status: " + http_request.status
                    + "\nStatusText: " + http_request.statusText
                    + "\nResponseText " + http_request.responseText;
            sql = "BEGIN fac.imp_writelog (" + import_key + ","
                +                       "'E',"
                +                       safe.quoted_sql(txt) + ", NULL);"
                + "END;"
            Oracle.Execute(sql)
        }
        var summarytxt = "Via SOAP doorgestuurd.";
    }

    sql = "UPDATE fac_import SET"
        + "       fac_import_datum_gelezen = SYSDATE"
        + (params.user_key > 0 ? ", prs_perslid_key = " + params.user_key : "")
        + " WHERE fac_import_key = " + import_key;
    Oracle.Execute(sql);

    var read_lines = teller-1;

    sql = "BEGIN fac.imp_writelog (" + import_key + ","
        +                       "'S',"
        +                       safe.quoted_sql(summarytxt) + ", NULL);"
        + "END;"
    Oracle.Execute(sql)

    return { success: true, import_key: import_key, backDir: backDir, read_lines: read_lines }

}

// import_key bevat een te verwerken key
// params: customerId: "UWVA"
function impProcessStream(import_key, params)
{
    var result = {message: "", success: false};

    var sql  = "SELECT ia.fac_import_app_key"
             + ",      fac_import_app_code"
             + ",      fac_import_app_prefix"
             + ",      fac_import_app_folder"
             + ",      fac_import_filenaam"
             + "  FROM fac_import_app ia"
             + ",      fac_import i"
             + " WHERE ia.fac_import_app_key = i.fac_import_app_key"
             + "   AND i.fac_import_key = " + import_key;
    var oRs = Oracle.Execute(sql);
    var import_app_key = oRs("fac_import_app_key").Value;
    var impcode = oRs("fac_import_app_code").Value;
    var prefix = oRs("fac_import_app_prefix").Value||"FAC";
    var filenaam = oRs("fac_import_filenaam").Value;
    var folder = oRs("fac_import_app_folder").Value;
    oRs.Close();

    // Start de UPDATE procedure om de database aan te passen
    sql = "BEGIN "
        + prefix + "_UPDATE_" + impcode
        + "(" + import_key + ");"
        + " END;"

    Oracle.Execute(sql);

    // Optioneel kan er nog klantspecifiek wat achteraan komen in geval van Catalogus-import
    // Bijv. NFIB#21853
    if (impcode == 'CATALOGUS')
    {
        var sql = "SELECT object_type"
                + "  FROM user_objects"
                + " WHERE object_name = " + safe.quoted_sql(customerId + "_UPDATE_CATALOGUS");
        var oRs = Oracle.Execute(sql);

        if (!oRs.eof && oRs(0).Value == "PROCEDURE")
        {   // Geldige aanwezig, roep aan
            sql = "BEGIN "
                + customerId + "_UPDATE_CATALOGUS"
                + "(" + import_key + ");"
                + " END;"
            Oracle.Execute(sql);
        }
        oRs.Close();
    }

    // Nu kijken of we nog iets met flexbijlagen moeten doen
    //
    // LET OP(?): Vanuit gen_import.wsf hebben we geen S() noch allerlei
    //            andere functies die we hier gebruiken!!
    //            Dit werkt dus (vooralsnog) alleen vanuit de GUI en via SOAP!!
    //

    var flexcode = null;
    var sql = "SELECT fac_result_waarde"
            + "  FROM fac_result"
            + " WHERE fac_result_naam = 'flexcode'"
            + "   AND fac_result_sessionid = 'IMPORT:{0}'".format(import_key);
    var oRs = Oracle.Execute(sql);
    if (!oRs.Eof)
    {   // melding S undefined? We ondersteunen dit nog niet vanuit gen_import.wsf
        flexcode = oRs("fac_result_waarde").Value;
    }
    oRs.Close();

    if (flexcode) // FIN:12345:18:F (12345=factuurkey, 18=kenmerkkey, F=factuur (versus R=regels)
    {
        if (!folder)
            abort_with_warning("IMPORT CONFIGURATION ERROR:\nflexcode result but FAC_IMPORT_APP_FOLDER is not set");

        if (!this.flexProps)
            INTERNAL_ERROR_FLEXCODE; // Niet ondersteund vanuit gen_import.wsf

        var backDir = backupfolder(impcode, folder, import_key);
        var res = extractAttachments(backDir + safe.filename(filenaam) + ".zip", safe.filename(filenaam), flexcode);
        if (!res.success)
            return res;
    }

    sql = "UPDATE fac_import"
        + "   SET fac_import_datum_verwerkt = SYSDATE"
        + (params.user_key > 0 ? ", prs_perslid_key = " + params.user_key : "")
        + " WHERE fac_import_key = " + import_key
        + "   AND fac_import_datum_verwerkt IS NULL";
    Oracle.Execute(sql);

    // Van elke import bewaren we in IMP_FILE maximaal één verwerkte
    // import (en maximaal één *niet verwerkte*)
    // Aangezien de onze net verwerkt is gooien we de vorige weg
    sql = "DELETE FROM fac_imp_file"
        + " WHERE fac_import_key IN"
        + "     (SELECT ander.fac_import_key"
        + "        FROM fac_import ander"
        + "       WHERE ander.fac_import_app_key = " + import_app_key
        + "         AND ander.fac_import_key <> "  + import_key
        + "     )";
    Oracle.Execute(sql);

    // Tenslotte hele fac_import (en automatisch bijbehorende S logging) opruimen.
    // De laatste 10 laten we altijd compleet staan, evenals alles van de laatste week
    var sql = "DELETE fac_import fi1"
            + " WHERE fi1.fac_import_app_key = " + import_app_key
            + "       AND fac_import_datum_verwerkt < SYSDATE - 7" // Laatste week blijft
            + "       AND fac_import_key < (SELECT MIN(fac_import_key)"
            + "                               FROM (  SELECT fac_import_key"
            + "                                         FROM fac_import fi2"
            + "                                        WHERE fi2.fac_import_app_key = " + import_app_key
            + "                                     ORDER BY fac_import_key DESC)"
            + "                              WHERE ROWNUM <= 10)"; // Laatste 10 blijven
    Oracle.Execute(sql);

    result.message = (typeof L != "undefined"?L("lcl_imp_inf_processed"):"Ingelezen gegevens zijn verwerkt"); // Geen L vanuit gen_import.wsf!
    result.success = true;
    return result;
}

function backupfolder(import_app, folder, import_key)
{
    if (!folder)
        return null;

    // <folder>\BACKUP<jaar>-<maand>\<import_app>\<fulldate>_<import_key>
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var jsDate = new Date();

    folder = folder.replace("/", "\\");
    if (folder.substr(folder.length-1) != "\\")
        folder += "\\";
    var backDir = folder + "BACKUP" + jsDate.getFullYear() + "-" + padout(jsDate.getMonth() + 1) + "\\";
    if (!fso.FolderExists(backDir))
        fso.CreateFolder(backDir);

    backDir += import_app + "\\";
    if (!fso.FolderExists(backDir))
        fso.CreateFolder(backDir);

    var dtstring = padout(jsDate.getFullYear())  + "-" + padout(jsDate.getMonth() + 1) + "-" + padout(jsDate.getDate());

    backDir += dtstring + "_" + import_key + "\\";
    if (!fso.FolderExists(backDir))
        fso.CreateFolder(backDir);

    return backDir;
}

function XMLval(xml, tag)
{
    if (!xml)
        return null;
    var xx = xml.getElementsByTagName(tag);
    if (!xx || !xx.length)
        return null;
    xx = xx[0].childNodes
    if (!xx || !xx.length)
        return null;
    return xx[0].nodeValue;
}

function extractAttachments(safeSourceXML, filename, flexcode)
{
    __Log("Attachments uit {0} extraheren en opslaan volgens {1}".format(safeSourceXML, flexcode));
    var arr = flexcode.split(":"); // FIN:12345:18:F
    var pModule = arr[0];
    if (arr.length > 1)
        var pKey = parseInt(arr[1], 10);
    if (arr.length > 2)
        var pKenmerkKey = parseInt(arr[2], 10);
    if (arr.length > 3)
        var pNiveau = parseInt(arr[3], 10);
    var params = flexProps(pModule, pKey, pKenmerkKey, pNiveau);

    //fileStream.CharSet = charset; // Vroeg, anders krijgen we een BOM?
    var oStream = new ActiveXObject("ADODB.Stream");
    oStream.Type = 1; //adTypeBinary
    oStream.Open;
    var oZIP = new ActiveXObject("SLNKDWF.Zip");
    oZIP.Open(safeSourceXML);
    oZIP.UnzipToStream(oStream, filename);

    var xmldoc = new ActiveXObject("Msxml2.DOMDocument.6.0");
    xmldoc.async = false;
    xmldoc.resolveExternals = false; // van mij hoef je geen DTD's te controleren
    xmldoc.validateOnParse = false; // van mij hoef je geen DTD's te controleren
    xmldoc.setProperty("ProhibitDTD", false);  // niet moeilijk doen over DTD's

    oStream.Position = 0;
    if( ! xmldoc.load(oStream) ) // De XML terug laden
    {
      return { success: false, warning: filename + "\nXML error: " + xmldoc.parseError.reason + " @ " + xmldoc.parseError.line + "." + xmldoc.parseError.linepos };
    }
    oZIP.Close();
    oStream.Close();
    //
    //  Dit is het enige formaat wat we (hardcoded) ondersteunen. DocumentType en FileType negeren we
    //	<Attachment>
    //      <AttachedData>JVBERi0xLjQKJcfsjCi9<....></AttachedData>
    //	    <DocumentType>FAC</DocumentType>
    //	    <FileType>PDF</FileType>
    //	    <FileName>0123456789123_201403159.pdf</FileName>
    //  </Attachment>

    var Attachments = xmldoc.getElementsByTagName("Attachment");
    for (var i=0; i < Attachments.length; i++)
    {
        var safefilename = safe.filename(XMLval(Attachments[i], "FileName"));
        if (!params.isAllowedName(safefilename))
        {
            // TODO: Misschien ook terugkoppelen in IMP_LOG?
            __Log("Onbekende extensie upload bijlage: {0}. Bestand is niet opgeslagen.".format(safefilename));
        }
        else
        {
            __Log('Bijlage {0} mag naar {1}'.format(safefilename, params.AttachPath));
            var AttachmentData = XMLval(Attachments[i], "AttachedData");
            if (AttachmentData)
            {
                CreateFullPath(params.AttachPath);
                encodedString2File(params.AttachPath + safefilename, AttachmentData, "bin.base64");
                __Log("Done saving: " + params.AttachPath + safefilename);
            }
            else
                __Log("Empty file skipped: " + safefilename);
        }
    }
    return { success: true };
}
