/*
    $Revision$
    $Id$

    File:               exp_shared.js
    Description:        Helper functie
    Parameters:
        export_app_key  Exportfunctie key
    Context:
    Note:               ========= LET OP LET OP =========
                        Dit bestand wordt ook gebruikt door ../../utils/gen_export/gen_export.wsf
                            Daar hebben we door CScript maar heel weinig functies!
                            DUS: Geen L() functie voor locale!!!!
                             EN: Geen Server.CreateObject maar new ActiveXObject
                             EN: Slechts enkele functies van safe.
                        ========= LET OP LET OP =========

                        gen_export_app_flags: 00001 (1)  zip het resultaat
                                              00010 (2)  manual allowed (future use)
*/

// fileStream moet onze data bevatten
// export_app_key de export-functie
// params export_key mag/zal leeg zijn voor het eerste bestand
//        customerId: "ABCD"
//        fac_home: net boven de APPL dus bijv. "d:/apps/facilitor/"
function expCreateStream(export_key, params)
{
    preExport(params);
    var exportFile = getFileName(params.fileprefix, params.postfix, params.applrun, params.timestamp);
    var streamData = exportToStream(params, exportFile); // exportFile voor als compress
    if (!params.preview)
        postExport(params);

    if (params.compress)
        exportFile += '.zip';

    return { exportFile: exportFile
           , streamData: streamData
           };
}


function getSettings(app_key)
{
    // Read settings from database
    //
    var settings = {};
    var sql = "SELECT *"
            + "  FROM fac_export_app"
            + " WHERE fac_export_app_key = " + app_key;
    var oRs = Oracle.Execute(sql);
    if (oRs.Eof)
    {
        __Log("Unable to find " + app_key + " in fac_export_app");
    }
    else
    {
        var sql = "UPDATE fac_export_app"
                + "   SET fac_export_app_datum = SYSDATE" // voor het laatst gebruikt
                + " WHERE fac_export_app_key = " + app_key;
        Oracle.Execute(sql);

        settings = { code:          oRs("fac_export_app_code").Value
                   , oms:           oRs("fac_export_app_oms").Value
                   , xsl:           oRs("fac_export_app_xsl").Value || ""
                   , charset:       oRs("fac_export_app_charset").Value || "Windows-1252"
                   , folder:        oRs("fac_export_app_folder").Value  || ""
                   , prefix:        oRs("fac_export_app_prefix").Value  // straks fallback naar params.custid die we hier nog niet hebben
                   , postfix:       oRs("fac_export_app_postfix").Value || ""
                   , timestamp:     oRs("fac_export_app_timestamp").Value || ""
                   , flags:         oRs("fac_export_app_flags").Value || 0
                   , functie_key:   oRs("fac_functie_key").Value || -1
                   , logpostfix:    oRs("fac_export_app_log_postfix").Value || ""
                   , fileprefix:    oRs("fac_export_app_file_prefix").Value || ""
                   }
    }
    oRs.Close();
    if (settings.flags & 1)
        settings.compress = true;
    if (settings.flags & 2)
        settings.manual = true;
    return settings;
}


function preExport(params)
{
    // Clean up the mess of the previous run
    //
    var sql = "DELETE FROM imp_log"
            + " WHERE imp_log_applicatie = " + safe.quoted_sql(params.code);
    __Log("SQL: " + sql);
    Oracle.Execute(sql);
    //
    // Call the database procedure for selecting the export results.
    var selectProc = (params.prefix||params.custid) + "_SELECT_" + params.code;
    var sql = "SELECT DISTINCT name"
            + "  FROM user_source"
            + " WHERE type = 'PROCEDURE'"
            + "   AND name = " + safe.quoted_sql(selectProc.toUpperCase());
    //__Log("SQL: " + sql);
    var oRs = Oracle.Execute(sql);
    if(!oRs.Eof)
    {
        sql = "BEGIN " + selectProc + "(" + safe.quoted_sql(params.code) + ", " + safe.quoted_sql(toDateTimeString(params.applrun)) + "); END;";
        __Log("SQL: " + sql);
        Oracle.Execute(sql);
    }
    else
    {
        //__Log("Optional select/pre-export procedure not found: " + selectProc);
    }
    oRs.Close();
}

function postExport(params)
{
  // Call the database procedure for creation of exportfile.
  var toMulti = params.fileprefix.charAt(0) == "*";
  // if toMulti, fileprefix translates to the filename_column
  var fileprefix = toMulti ? params.fileprefix.substr(1) : params.fileprefix;
  var filename = getFileName(fileprefix, params.postfix, params.applrun, params.timestamp);
  var exportProc = (params.prefix||params.custid) + "_EXPORT_" + params.code;
  var sql = "SELECT DISTINCT name"
          + "  FROM user_source"
          + " WHERE type='PROCEDURE'"
          + "   AND name = " + safe.quoted_sql(exportProc.toUpperCase());
  //__Log("SQL: " + sql);
  var oRs = Oracle.Execute(sql);
  if (!oRs.Eof)
  {
    var sql = "BEGIN "
            + exportProc + "(" + safe.quoted_sql(params.code)
                         + "," + safe.quoted_sql(toDateTimeString(params.applrun))
                         + ",'-1'"                            //+ safe.quoted_sql(iniOraclePath)  // ??
                         + "," + safe.quoted_sql(filename)
                         + ");"
            + " END;"
    __Log("SQL: " + sql);
    Oracle.Execute(sql);
  }
  else
  {
    //__Log("Optional (post-)export procedure not found: " + exportProc);
  }
  oRs.Close();
}

// Lever null op als er geen select-view is gedefinieerd.
function exportToStream(params, exportFile)
{
    var exportView = (params.prefix||params.custid) + "_V_EXPORT_" + params.code;
    var sql = "SELECT DISTINCT view_name"
            + "  FROM USER_VIEWS"
            + " WHERE view_name = " + safe.quoted_sql(exportView.toUpperCase());
    //__Log("SQL: " + sql);
    var oRs = Oracle.Execute(sql);
    if (oRs.Eof)
    {
        //__Log("Optional export view not found: " + exportView);
        oRs.Close()
        return null;
    }
    else
    {
        var fstr;
        var toMulti = params.fileprefix.charAt(0) == "*";
        if (toMulti)
        {
            var last_filename;
            var this_filename;
            var fstr_arr = [];
            var filename_col = params.fileprefix.substr(1);
            var sql_v = "SELECT " + filename_col + ", result"
                      + "  FROM " + exportView
                      + " ORDER BY " + filename_col + ", result_order";
            __Log("SQL: " + sql_v);
            var oRs_v = Oracle.Execute(sql_v);

            while (!oRs_v.Eof)
            {
                last_filename = oRs_v(filename_col).Value;
                this_filename = last_filename;
                fstr = openStreamWriteText({charset: params.charset});
                while (!oRs_v.Eof && last_filename == this_filename)
                {
                    fstr.WriteText(oRs_v("result").Value, 1); // 0=adWriteChar, 1=adWriteLine (LineSeparator required)
                    oRs_v.MoveNext();
                    if (!oRs_v.Eof)
                        this_filename = oRs_v(filename_col).Value;
                }
                if (params.xsl)
                    fstr = transformStream(fstr, params.xsl, params);
                fstr.Position = 0;
                fstr_arr.push({ "filename": last_filename, "fstr": fstr });
            }
        }
        else
        {
            fstr = openStreamWriteText({charset: params.charset});
            var sql_v = "SELECT result"
                      + "  FROM " + exportView
                      + " ORDER BY result_order";
            __Log("SQL: " + sql_v);
            var oRs_v = Oracle.Execute(sql_v);
            while (!oRs_v.Eof)
            {
                fstr.WriteText(oRs_v("result").Value, 1); // 0=adWriteChar, 1=adWriteLine (LineSeparator required)
                oRs_v.MoveNext();
            }
            if (params.xsl)
                fstr = transformStream(fstr, params.xsl, params);
            fstr.Position = 0;
        }
        oRs_v.Close();
    }
    oRs.Close();

    if (!params.compress || toMulti) // Zip not supported for toMulti
        return (toMulti ? fstr_arr : fstr);

    var oZIP = new ActiveXObject("SLNKDWF.Zip");
    var fso = new ActiveXObject("Scripting.FileSystemObject");
    var temp = params.tempFolder + fso.GetTempName();
    oZIP.New(temp) //;; Helaas niet *naar* stream
    oZIP.ZipFromStream(exportFile, fstr);
    oZIP.Close();
    var oStream = new ActiveXObject("ADODB.Stream");
    oStream.Open;
    oStream.Type = 1; // adTypeBinary

    oStream.LoadFromFile(temp);
    oStream.Position = 0;
    fso.DeleteFile(temp);
    return oStream;
}


function transformStream(stream, xsl, params)
{
    //return stream;
    var xslfile = params.fac_home + "CUST/" + params.customerId + "/" + xsl;
    __Log("Transforming export using " + xslfile);

    var source = new ActiveXObject("MSXML2.DOMDocument.6.0");
    source.async = false;
    source.resolveExternals = false; // XML-data vertrouwen we niet altijd
    source.validateOnParse = false; // maar dan kun je geen DTD's meer controleren
    source.setProperty("ProhibitDTD", false);  // niet moeilijk doen over DTD's

    stream.Position = 0;
//    if (0) // testing
//    source.loadXML("<facilitor><FCLTattachment encoding='hex' templatenodename='test'>367079</FCLTattachment></facilitor>");
//    else
    if( ! source.load(stream) )   // Het bestand laden
    {
        var err = "XML error: " + source.parseError.reason + " @ " + source.parseError.line + "." + source.parseError.linepos;
        __DoLog(err);
        throw({ description: err });
    }

    // Load the XSLT
    var style = new ActiveXObject("MSXML2.DOMDocument.6.0");
    style.async = false;
    style.resolveExternals = true; // XSL kan includes hebben
    style.validateOnParse = true; // en moet correct zijn
    style.setProperty("ProhibitDTD", false);  // niet moeilijk doen over DTD's
    if( ! style.load(xslfile) ) // De stylesheet laden
    {
        var err = xslfile + "\nXSL error: " + style.parseError.reason + " @ " + style.parseError.line + "." + style.parseError.linepos;
        __DoLog(err);
        throw({ description: err });
    }

    var XMLresult = new ActiveXObject("Msxml2.DOMDocument.6.0");
    source.transformNodeToObject(style, XMLresult);

    // kijk of er <FCLTattachment>bijlagenkey<FCLTattachment> in
    // de XMLresult zit en vervang die eventueel
    mergeXMLAttachments3(XMLresult, style);

    var oStream = openStreamWriteText({charset: params.charset});
    XMLresult.save(oStream);

    return oStream;
}

function mergeXMLAttachments3(iXml, style)
{
    var xmltag = "//FCLTattachment";
    // Doorzoek iXml op <FCLTattachment>345</FCLTattachment>
    // waarbij 345 een fac_bijlagen_key is
    // en vervang die door
    // <Attachment>
    //      <BinaryData>...b64data...</BinaryData>
    //      <Name>naam</Name>
    //      <Size>1235</Size>
    // </Attachment>
    var NodeList = iXml.documentElement.selectNodes(xmltag);
    __Log("Gezocht op FCLTattachment");
    if (NodeList.length > 0)
    {
        __Log("*> mergeXMLAttachments");

        for (var index=0; index < NodeList.length; index++)
        {
            var node = NodeList[index];
            var bijlage_key   = node.text;
            var nodename      = node.attributes.getNamedItem("nodename")?node.attributes.getNamedItem("nodename").text:"Attachment";
            var node_encoding = node.attributes.getNamedItem("encoding"); // als die NIET is gevuld doen we default "base64"
            var encoding = "base64";
            if (node_encoding)
            {
                encoding = node_encoding.text;
            }
            var node_templatenodename = node.attributes.getNamedItem("templatenodename"); // als die is gevuld doen we een post-parse met de stylesheet
            var templatenodename = '';
            if (node_templatenodename)
            {
                templatenodename = node_templatenodename.text;
            }
            __Log("Including fac_bijlagen {0}".format(bijlage_key));
            var att_list = [];
            var sql = "SELECT r.fac_bijlagen_disk_directory"
                    + "     , r.fac_bijlagen_filename"
                    + "     , r.fac_bijlagen_file_size"
                    + "  FROM fac_v_bijlagen r"
                    + " WHERE r.fac_bijlagen_verwijder IS NULL"
                    + "   AND r.fac_bijlagen_key = " + bijlage_key;
            var oRs = Oracle.Execute(sql);
            if (!oRs.eof)
            {
                att_list.push( { f_disk: oRs("fac_bijlagen_disk_directory").Value
                               , f_name: oRs("fac_bijlagen_filename").Value
                               , f_size: oRs("fac_bijlagen_file_size").Value
                               , f_path: S("flexfilespath") + "/" + oRs("fac_bijlagen_disk_directory").Value + "/" + oRs("fac_bijlagen_filename").Value
                               }
                             );
                oRs.MoveNext();
            }
            else
                __Log("fac_bijlagen record not found?");
            oRs.Close();
            for (var a=0; a < att_list.length; a++)
            {
                __Log("Embedding attachment: " + att_list[a].f_path + " (" + att_list[a].f_size + " bytes)");
                // en hier de rest voor het invoegen van de bijlagen.
                var elemAttachments = iXml.createElement(nodename);
                var elemAtt = iXml.createElement("BinaryData");

                switch (encoding)
                {
                    case "hex":    elemAtt.text = Hexify(att_list[a].f_path);
                                   break;
                    case "base64": elemAtt.text = Base64fy(att_list[a].f_path);
                                   break;
                }
                __Log("{0} encoded it becomes {1} bytes".format(encoding, elemAtt.text.length));
                elemAttachments.appendChild(elemAtt);
                elemAtt = iXml.createElement("Encoding");
                elemAtt.text = encoding;
                elemAttachments.appendChild(elemAtt);
                elemAtt = iXml.createElement("Name");
                elemAtt.text = att_list[a].f_name;
                elemAttachments.appendChild(elemAtt);
                elemAtt = iXml.createElement("Size");
                elemAtt.text = att_list[a].f_size;
                elemAttachments.appendChild(elemAtt);

                if (templatenodename != '') // post processing met de stylesheet
                {   // puo_xmltools gebruikt hier function XML2HTML() maar dat introduceert mij te veel afhankelijkheden
                    var elemWrapper = iXml.createElement(templatenodename);
                    elemWrapper.appendChild(elemAttachments);
                    var tmp_xmlDoc = new ActiveXObject("Msxml2.DOMDocument.6.0");
                    tmp_xmlDoc.async = false;
                    tmp_xmlDoc.loadXML(elemWrapper.xml);

                    //LogString2File(3, "PostProces", tmp_xmlDoc.xml, "xml");
                    var xslt   = new ActiveXObject("Msxml2.XSLTemplate.6.0");
                    style.resolveExternals = true; // anders op de volgende regel al:
                                                    // A reference to variable or parameter 'srtnotificatiecode' cannot be resolved
                    xslt.stylesheet = style;
                    var xslProc = xslt.createProcessor();
                    xslProc.input = tmp_xmlDoc;
                    xslProc.addParameter("srtnotificatiecode", "");
                    xslProc.addParameter("mode", "processattachments");
                    xslProc.transform();
                    tmp_xmlResult = xslProc.output;

                    tmp_xmlDoc.loadXML(tmp_xmlResult);
                    //LogString2File(3, "PostProcesAttach", tmp_xmlResult, "xml");

                    var NodeList2 = tmp_xmlDoc.documentElement.childNodes;
                    for (var i=0; i < NodeList2.length; i++)
                    {
                        var tmp_node = NodeList2[i];
                        //Let op: niet appendChild gebruikt, want die voegt achteraan toe aan de parentnode, waardoor de volgorde wordt aangepast.
                        //Door gebruikt van insertBefore blijft de volgorde intact, die wezenlijk voor Validatie tegen XSD (van externe partijen) kan zijn!
                        node.parentNode.insertBefore(tmp_node, node);
                    }
                }
                else
                {
                    node.parentNode.appendChild(elemAttachments);
                }
            }
            node.parentNode.removeChild(node); // dummy fcltattachments weg
        }
        __Log("*< mergeXMLAttachments");
    }
    return iXml;
}


function openStreamWriteText(params)
{
    // params:
    //   lineseparator: -1=adCRLF, 10=adLF, 13=adCR
    //   streamwrite:   1=adSaveCreateNotExist, 2=adSaveCreateOverWrite
    //   charset:
    // var streamwrite = (params.streamwrite ? params.streamwrite : 1);

    var fileStream = new ActiveXObject("ADODB.Stream");
    fileStream.CharSet = (params.charset ? params.charset : "Windows-1252");
    fileStream.LineSeparator = (params.lineseparator ? params.lineseparator : -1);
    fileStream.Type = 2; // 1=adTypeBinary, 2=adTypeText
    fileStream.Open();

    return fileStream;
}


function getFileName(fileprefix, postfix, daterun, dateformat)
{
    var index = fileprefix.indexOf(".");
    var naam = (index==-1?fileprefix:fileprefix.substr(0, index)) + formatDate(daterun, dateformat) + postfix;
    return naam;
}


function formatDate(datum, format)
{
    var onejan = new Date(datum.getFullYear(),0,1);
    var o = { "m+" : datum.getMonth()+1                  //month
            , "d+" : datum.getDate()                     //day
            , "H+" : datum.getHours()                    //hour
            , "M+" : datum.getMinutes()                  //minute
            , "S+" : datum.getSeconds()                  //second
            , "q+" : Math.floor((datum.getMonth()+3)/3)  //quarter
            , "s"  : datum.getMilliseconds()             //millisecond
            , "w+" : Math.ceil((((datum - onejan) / 86400000) + onejan.getDay()+1)/7) // weeknumber
            };
    format = format.replace('hhmm', 'HHMM');
    if (/(y+)/.test(format))
        format = format.replace(RegExp.$1, (datum.getFullYear()+"").substr(4 - RegExp.$1.length));
    for(var k in o)
    {
        if(new RegExp("("+ k +")").test(format))
        {
            format = format.replace(RegExp.$1, RegExp.$1.length==1 ? o[k] : ("00"+ o[k]).substr((""+ o[k]).length));
        }
    }
    return format;
}

