//
// Copyright 2013 SG|facilitor. 053-4800 700,  support@sgfacilitor.nl
//
// TrakaFacilitor.js
// Staat elke nn minuten gescheduled en wisselt informatie uit tussen Trake en FACILITOR
//
var fclturl = "http://facilitor.pzh.local/?fac_id=PZHO";
var trakaudl = "traka.udl";
var APIKEY = "OFFlefZFRKpIRVnHmYOOEwgDdOGJoAVL"; // Pseudo-user tbv. TRAKA-API

// graceperiod: elke wijziging in een reservering in FACILITOR wordt gedurende de graceperiod
//              iedere run van de adapter doorgeven.
//              Reserveringen die vandaag (of eerder) beginnen en nog niet zijn afgelopen
//              worden *altijd* doorgegeven
var graceperiod = 1 * 60;

//var proxy = "127.0.0.1:8888"; // Voor debugging met Fiddler
CabFieldID = 1; // Fietsenstalling

if (WScript.Arguments.length > 0)
    var fclturl = WScript.Arguments(0);

if (WScript.Arguments.length > 1)
    var trakaudl = WScript.Arguments(1);

var fso = new ActiveXObject("Scripting.FileSystemObject")

var TRAKA = new ActiveXObject("ADODB.Connection");
TRAKA.Open('File Name=' + trakaudl);

var adParamInput = 1;
var adOutput = 2;
var adDate = 7
var adDBDate = 133
var adDBTime = 134
var adDBTimeStamp = 135
var adVarChar = 200;
var adVarWChar = 202;
var adInteger = 3;
var adStateOpen = 1;
var adUseClient = 3;
var adOpenStatic = 3;
var adCmdStoredProc = 4;
var adCmdText = 1;

function apiFACILITOR(API, req)
{
    var xmlhttp = new ActiveXObject("WinHTTP.WinHTTPRequest.5.1");
    SXH_PROXY_SET_PROXY = 2;
    if (typeof proxy != "undefined" && proxy)
        xmlhttp.setProxy(SXH_PROXY_SET_PROXY, proxy);

    var xmlUrl = fclturl + "&API=" + API + "&APIKEY=" + APIKEY + "&json=1&pretty=1&tm=" + (new Date).getTime();
    xmlhttp.open("POST", xmlUrl, false);

    xmlhttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8")

    var msg = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
            + "<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"
            + "               xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
            + "               xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>"
            + "  <soap:Body>"
            + req
            + "  </soap:Body>"
            + "</soap:Envelope>";

    xmlhttp.send(msg);

    if (xmlhttp.status == 200)
    {
        try
        {
            var txt = xmlhttp.responseText || "{}";
            var result_data = eval("(" + txt + ")");
            return result_data;
        }
        catch (e)
        {
            // Waarschijnlijk http-error terug gekregen
            __Log("eval failed: " + e.description);
            __Log(txt);
            return {};
        }
    }
    else
    {
        __Log("Status: "+xmlhttp.status);
        __Log(xmlhttp.responseText);
    }
}

function param(cmd, naam, type, len, val)
{
    var param = cmd.CreateParameter(naam, type, adParamInput, len);
    cmd.Parameters.Append(param);
    param.Value = val;
}

// Taak 1: Update Traka met alle komende fiets/sleutel-reserveringen+mutaties uit FACILITOR
//   FACILITOR-gegevens in res-parameter bevat:
//   RES_KEY,
//   RES_ID,
//   FIETS_OMS,
//   SLEUTEL_ID,
//   VAN,
//   TOT,
//   USER_KEY,
//   VNAAM,
//   ANAAM,
//   PASNR,
//   FO_STATUS_KEY,
//   LAST_CHANGE,
//   RES_VERWIJDER
function SendReservering2TRAKA(res)
{
    var dvan = xmltojsdate(res.VAN);
    var dtot = xmltojsdate(res.TOT);
    __Log(res.FIETS_OMS + " => " + (res.ANAAM||"") + " (" + (res.VNAAM||"") + ")");
    __Log(" van: " + dvan);
    __Log(" tot: " + dtot);
    if (!res.RES_VERWIJDER)
    {
    	var dupd = xmltojsdate(res.LAST_CHANGE);
    	__Log(" upd: " + dupd);
    }
    else
    {
    	var ddel = xmltojsdate(res.RES_VERWIJDER);
    	__Log(" del: " + ddel);
    }

    // 1. Bijwerken FACILITOR-id van evt. reeds bestaande user met PASNR in
    //    TRAKA (om nog een keer toevoegen in 2. te voorkomen). 
    var cmd = new ActiveXObject("ADODB.Command");
    cmd.ActiveConnection = TRAKA;
    cmd.CommandType = adCmdText;
    var sql = "UPDATE TUsers"
            + "   SET ExternalFieldID = ?"
            + " WHERE PrimaryID = ?";
    cmd.CommandText = sql;
    param(cmd, "", adVarChar, 255, String(res.USER_KEY));
    param(cmd, "", adVarChar, 255, String(res.PASNR));
    cmd.Execute();

    // 2. Toevoegen/bijwerken user in TRAKA.
    var cmd = new ActiveXObject("ADODB.Command")
    cmd.ActiveConnection = TRAKA;
    cmd.CommandType = adCmdStoredProc;
    cmd.CommandText = "SP_TPL_ADDEDITUSER5";
    cmd.Parameters.Refresh(); // ipv diverse param(cmd, "@ExternalFieldID", adVarChar, 255, String(res.USER_KEY));
    cmd.Parameters("@ExternalFieldID").Value = String(res.USER_KEY);
    cmd.Parameters("@Forename").Value = res.VNAAM || "";
    cmd.Parameters("@Surname").Value =  res.ANAAM || "";
    cmd.Parameters("@Primary").Value = res.PASNR || "";
    cmd.Parameters("@Secondary").Value = "";
    cmd.Parameters("@UDetail_01").Value = "";
    cmd.Parameters("@UDetail_02").Value = "";
    cmd.Parameters("@UDetail_03").Value = "";
    cmd.Parameters("@UDetail_04").Value = "";
    cmd.Parameters("@UDetail_05").Value = "";
    cmd.Parameters("@UDetail_06").Value = "";
    cmd.Parameters("@UDetail_07").Value = "";
    cmd.Parameters("@UDetail_08").Value = "";
    cmd.Parameters("@UDetail_09").Value = "";
    cmd.Parameters("@UDetail_10").Value = "";
    cmd.Parameters("@UDetail_11").Value = "";
    cmd.Parameters("@RegionName").Value = "";
    cmd.Parameters("@ActiveFlag").Value = 1;
    cmd.Parameters("@ActiveDate").Value = toTrakaDateString (new Date);
    cmd.Parameters("@ExpiryDate").Value = ""; // If zero length string, then set to '01/06/2020' (mmddyyyy)!
    cmd.Parameters("@AccessLevels").Value = ""; // If ADD/NEW=No levels granted and if EDIT=No changes made!
    cmd.Parameters("@iFobAllowance").Value = 200;
    cmd.Parameters("@UserID").Value = 999999; // Optional NIP code
    cmd.Parameters("@Reserved1").Value = "";
    cmd.Parameters("@Reserved2").Value = "";
    cmd.Parameters("@Reserved3").Value = "";
    cmd.Parameters("@Reserved4").Value = "";
    cmd.Execute();

    // 3. Bepalen User-id volgens TRAKA.
    var cmd = new ActiveXObject("ADODB.Command");
    cmd.ActiveConnection = TRAKA;
    cmd.CommandType = adCmdText;
    var sql = "SELECT [FieldID], [ExternalFieldID]"
            + "  FROM [TUsers]"
            + " WHERE ExternalFieldID = ? AND deleted=0";
    cmd.CommandText = sql;
    param(cmd, "", adVarChar, 255, String(res.USER_KEY));
    var rst = cmd.Execute();
    var UserFieldID  = rst("FieldID").Value;
__Log(" UserFieldID: " + UserFieldID);
    rst.Close();

    // 4. Bepalen Fob-id volgens TRAKA.
    var cmd = new ActiveXObject("ADODB.Command");
    cmd.ActiveConnection = TRAKA;
    cmd.CommandType = adCmdText;
    var sql = "SELECT [FieldID]"
            + "  FROM [VFobs]"
            + " WHERE CabFieldID = ?"
            + "   AND PosID = ?";
    cmd.CommandText = sql;
    param(cmd, "", adVarChar, 255, CabFieldID);
    param(cmd, "", adVarChar, 255, String(res.SLEUTEL_ID));
    var rst = cmd.Execute();
    var FobFieldID  = rst("FieldID").Value;
__Log(" FobFieldID: " + FobFieldID);
    rst.Close();

    var cmd = new ActiveXObject("ADODB.Command");
    cmd.ActiveConnection = TRAKA;
    cmd.CommandType = adCmdStoredProc;
    if (!res.RES_VERWIJDER)
    {
        // 5a. Toevoegen/bijwerken boeking in TRAKA.
        cmd.CommandText = "SP_TPL_ADDEDITFOBBOOKING";
        cmd.Parameters.Refresh();
        cmd.parameters("@ExternalFieldID").Value = String(res.RES_ID);
        cmd.parameters("@UserFieldID").Value =  UserFieldID;
        cmd.parameters("@CabFieldID").Value =  CabFieldID; // Fixed
        cmd.parameters("@FobFieldID").Value =  FobFieldID;
        cmd.parameters("@BookingStart").Value =  xmltojsdate(res.VAN).getVarDate();
        cmd.parameters("@BookingEnd").Value =  xmltojsdate(res.TOT).getVarDate();
        cmd.parameters("@Notes").Value =  "Booking by FACILITOR";
        cmd.parameters("@ByRef_forename").Value =  "";
        cmd.parameters("@ByRef_surname").Value =  "";
        cmd.parameters("@ByRef_reference").Value =  "";
    }
    else
    {
        // 5b. Verwijderen boeking in TRAKA.
        cmd.CommandText = "SP_TPL_DELETEFOBBOOKING";
        cmd.Parameters.Refresh();
        cmd.parameters("@ExternalFieldID").Value = String(res.RES_ID);
    }
    cmd.Execute();
}

function fromFACILITORtoTRAKA()
{
    __Log("*** fromFACILITORtoTRAKA ***");

    // Alles wat na 'van'-graceperiod gewijzigd(!) is in FACILITOR sturen we
    // naar TRAKA.
    // Let op: De view pzho_v_api_dienstfietsen is ietwat getruct: voor alle
    // reserveringen die *vandaag* vallen wordt last_change op SYSDATE gezet.
    // Die komen dus altijd mee.
    var van = new Date();
    van.setMinutes(van.getMinutes() - graceperiod);

    var req = "<GetDienstfietsen>"
            + "    <last_change>" + jsdatextoxml(van) + "</last_change>"
            + "</GetDienstfietsen>";
    var res_data = apiFACILITOR("TRAKA", req);
    if (res_data && res_data.reservering)
    {
        if (!(res_data.reservering instanceof Array)) // Als er maar eentje is
            res_data.reservering = [res_data.reservering];

        __Log("Aantal reserveringen naar TRAKA: " + res_data.reservering.length);

        for (i in res_data.reservering)
        {
            SendReservering2TRAKA(res_data.reservering[i]);
        }
    }
    else
        __Log("Geen reserveringen te verzenden");
}

// Taak 2: Update aan/afwezigheid van fietsen/sleutels in FACILITOR tov. situatie uit Traka
function SendReservering2FACILITOR(deel_key, status, naam)
{
    var req = "<deel type=\"update\" key=\"" + deel_key + "\">"
            + "    <omschrijving>" + naam + "</omschrijving>"
            //+ "    <tijd>2014-01-01T12:00:00</tijd>"
            + "    <status>" + status + "</status>"
            + "</deel>"
    var res_data = apiFACILITOR("INSSET", req);
    __Log(naam + " is nu " + status);
}

// Traka is leidend in het bijhouden welke fietsen/sleutels al dan niet daadwerkelijk
// aanwezig zijn. Geef dat door aan FACILITOR.
// NB. Merk op dat dit geheel los staat van het feit of er al dan niet een reservering
//     voor die fiets is!
function fromTRAKAtoFACILITOR()
{
    __Log("*** fromTRAKAtoFACILITOR ***");

    // Eerst (status van) alle fietsen/sleutels uit FACILITOR ophalen.
    var req = "<GetDelen>"
            + "    <soortdeel>Fiets-Fiets</soortdeel>"
            + "    <soortgroep>Dienstfietsen</soortgroep>"
            + "    <discipline>Vervoersmiddelen</discipline>"
            //+ "    <key>163</key>"
            + "</GetDelen>";
    var ins_data = apiFACILITOR("INS", req);
    __Log("Aantal leenfietsen in FACILITOR: " + ins_data.deel.length);

    if (ins_data && ins_data.deel)
    {
        if (!(ins_data.deel instanceof Array)) // Als er maar eentje is
            ins_data.deel = [ins_data.deel];

        // Situatie uit Traka ophalen en matchen tegen situatie uit FACILITOR.
        //var sql = "SELECT PosID PositionID, Status FROM VFobs"; // Alleen tbv. test tegen VFobs!
        var sql = "SELECT PositionID, Status FROM V_TPL_CURRENTFOBHOLDERS WHERE CabTitle='Fietsenstalling'";
        var oRs = TRAKA.Execute(sql);

        while (!oRs.Eof)
        {
            var status = oRs("Status").Value;
            for (i in ins_data.deel)
            {
                var fiets = ins_data.deel[i];
                // Bepaal fietsnr door het gedeelte achter de 1e spatie te nemen (met PZH
                // afgestemde randvoorwaarde)!
                var fiets_arr = fiets.DEEL.split(" ");
                var fietsnr = fiets_arr.length == 1? fiets.DEEL: fiets_arr[1];
                if (fietsnr == oRs("PositionID").Value)
                {
                    fiets.islentout = (status != 1); // Fob aanwezig als status=1; anders weg!
                    //__Log(fiets.DEEL + " is uitgeleend volgens Traka");
                }
            }
            oRs.MoveNext();
        }

        for (i in ins_data.deel)
        {
            var fiets = ins_data.deel[i];
            if (fiets.islentout && !fiets.UITGELEEND)
            {   // Update FACILITOR dat fiets/sleutel is opgehaald
                SendReservering2FACILITOR(fiets.KEY, "INSOUT", fiets.DEEL);
            }
            if (!fiets.islentout && fiets.UITGELEEND)
            {   // Update FACILITOR dat fiets/sleutel terug is
                SendReservering2FACILITOR(fiets.KEY, "INSINN", fiets.DEEL);
            }
        }
    }
}

// Helper functies
// 2013-11-13T07:00:00
function xmltojsdate(datestamp)
{
  var pattern = /^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)$/;
  var pieces = datestamp.match(pattern);
  if(!pieces || pieces.length < 6)
    return null;

  return new Date(pieces[1], pieces[2]-1, pieces[3], pieces[4], pieces[5], pieces[6]);
}
function jsdatextoxml(dt)
{
  return toDateString(dt) + "T" + toTimeString(dt);
}
function jsdatetosqls(jsDate)
{
  return jsDate.getVarDate();
}
function padout(number) { return (number < 10) ? "0" + number : String(number); }
function toTrakaDateString(jsDate)
{
    return padout(jsDate.getMonth() + 1) + "/" + padout(jsDate.getDate()) + "/" + padout(jsDate.getFullYear());
}
// Noot: altijd jaar vooraan, goed voor logfiles, niet bedoeld voor presentatie
function toDateString(jsDate)
{
    return padout(jsDate.getFullYear()) + "-" + padout(jsDate.getMonth() + 1) + "-" + padout(jsDate.getDate());
}
function toTimeString(jsDate)
{
    return padout(jsDate.getHours()) + ":" + padout(jsDate.getMinutes()) + ":" + padout(jsDate.getSeconds());
}
function __Log(s, level)
{
    WScript.Echo(s);
    var dt = new Date;
    var logname = "TrakaFacilitor_" + padout(dt.getFullYear())  + "_" + padout(dt.getMonth() + 1) + ".log";
    var flog = fso.OpenTextFile(logname, 8 /* ForAppending */, true /* create */);
    var tms = toDateString(new Date) + " " + toTimeString(new Date);
    flog.WriteLine(tms + " " + s);
    flog.Close();
}

__Log("Starting TrakaFacilitor.js");
fromFACILITORtoTRAKA();
fromTRAKAtoFACILITOR();
__Log("Done TrakaFacilitor.js\n");
