#ifdef  INS
/*
 * $Revision$
 * $Id$
 */

CREATE OR REPLACE PACKAGE ins AS
    FUNCTION delete_deelkoppeling( DeelKey IN NUMBER ) RETURN BOOLEAN;
    FUNCTION get_description(pentityname IN VARCHAR2, pparentkey IN NUMBER, pprefix IN VARCHAR2) RETURN VARCHAR2;
    FUNCTION nextdescription(pprefix IN VARCHAR2, pparentkey IN NUMBER, pseparator IN VARCHAR2 DEFAULT 'default') RETURN VARCHAR2;
    PROCEDURE copyInsKenmerken(pdeel_key IN NUMBER, ptosrtdeel_key IN NUMBER);
    PROCEDURE altersrtdeel(pdeel_key IN NUMBER, ptosrtdeel_key IN NUMBER);
    FUNCTION sprintf (ps IN varchar2, pins_deel_key IN number) RETURN varchar2;
    FUNCTION contrsprintf (ps IN varchar2, pins_deelsrtcontr_key IN NUMBER) RETURN varchar2;
    FUNCTION nextcyclusdate (p_insdeel IN NUMBER, p_srtcontrole IN NUMBER, p_scenario IN NUMBER, p_steps IN NUMBER DEFAULT 0) RETURN DATE;
    FUNCTION nextcyclusdatesteps (p_insdeel IN NUMBER, p_srtcontrole IN NUMBER, p_steps IN NUMBER DEFAULT 1) RETURN NUMBER;
    PROCEDURE setinspectstatus (pdlsrtcontrkey IN NUMBER, pstatus IN NUMBER, puserkey IN NUMBER);
    FUNCTION state_compare (str1 IN VARCHAR, str2 IN VARCHAR) RETURN NUMBER;
    FUNCTION state_diff (str1 IN VARCHAR, str2 IN VARCHAR) RETURN NUMBER;
    FUNCTION state_thresholdcompare (pdeel_key IN NUMBER, pdays NUMBER DEFAULT NULL) RETURN NUMBER;
    PROCEDURE compress_states (pdodelete IN NUMBER DEFAULT 1);
END ins;
/

CREATE OR REPLACE PACKAGE BODY ins AS

    FUNCTION delete_deelkoppeling( DeelKey IN NUMBER )
        RETURN BOOLEAN
    AS
    BEGIN
       DELETE FROM ins_deelkoppeling
       WHERE ins_deelkoppeling_van_key = DeelKey
          OR ins_deelkoppeling_naar_key = DeelKey;
       RETURN TRUE;
    EXCEPTION
       WHEN OTHERS THEN RETURN FALSE;
    END;

    -- For backwards compatibility only (used in some CUST procedures) DEPRECATED!
    FUNCTION get_description( pentityname  IN VARCHAR2
                            , pparentkey   IN NUMBER
                            , pprefix      IN VARCHAR2) RETURN VARCHAR2 IS
    BEGIN
      IF pentityname = 'LPN_INS_DEEL'
      THEN
          RETURN ins.nextdescription(pprefix, NULL, '');
      END IF;

      IF pentityname = 'LPN_INS_ONDERDEEL'
      THEN
          RETURN ins.nextdescription(pprefix, pparentkey, '');
      END IF;
      RETURN 'unsupported use of ins.get_description';
    END;

    -- Determine max+1 volgnr for an object (deel or onderdeel) op basis van het format prefix+volgnummer
    -- De laagste is 001 maar vervolgens wordt het gevonden aantal cijfers aangehouden, dus prefix0001+1 = prefix0002
    -- Zo nodig wordt er een cijfer bij gedaan. De prefix wordt case-insensitive beschouwd.
    FUNCTION nextdescription( pprefix      IN VARCHAR2
                            , pparentkey   IN NUMBER
                            , pseparator   IN VARCHAR2 DEFAULT 'default') RETURN VARCHAR2 IS
      nextvolgnr       VARCHAR2(30);  -- overdreven
      returnstring     ins_deel.ins_deel_omschrijving%TYPE;
      lprefix          VARCHAR2(30);  -- overdreven, inclusief evt separator
      lseparator       VARCHAR2(30);   -- ook overdreven
    BEGIN
      IF pseparator = 'default'
      THEN
        lseparator := fac.getsetting('ins_nr_separator');
      ELSE
        lseparator := pseparator;
      END IF;
      lprefix := pprefix || lseparator;
      -- uniqueness is assumed over the entire database
      IF pparentkey IS NULL
      THEN
        SELECT CASE
               WHEN COALESCE(MAX(TO_NUMBER(SUBSTR(ins_deel_upper, LENGTH(lprefix) + 1))) + 1, 1) < 1000
               THEN COALESCE(LPAD(TO_CHAR(MAX(TO_NUMBER(SUBSTR(ins_deel_upper, LENGTH(lprefix) + 1))) + 1), MAX(LENGTH(ins_deel_upper) - LENGTH(lprefix)), '0'), '001')
               ELSE TO_CHAR(MAX(TO_NUMBER(SUBSTR(ins_deel_upper, LENGTH(lprefix) + 1))) + 1)
               END nextvolgnr
          INTO nextvolgnr
          FROM ins_deel
         WHERE ins_deel_upper LIKE UPPER(lprefix) || '%'
           AND fac.safe_to_integer(SUBSTR(ins_deel_upper, LENGTH(lprefix) + 1)) IS NOT NULL
           AND LTRIM(SUBSTR (ins_deel_upper, LENGTH (lprefix) + 1), '0') = TO_CHAR(fac.safe_to_integer ((SUBSTR (ins_deel_upper, LENGTH (lprefix) + 1))))
           AND ins_deel_parent_key IS NULL
           AND ins_deel_verwijder IS NULL;
      ELSIF pparentkey IS NOT NULL
      THEN
        SELECT CASE
               WHEN COALESCE(MAX(TO_NUMBER(SUBSTR(ins_deel_upper, LENGTH(lprefix) + 1))) + 1, 1) < 1000
               THEN COALESCE(LPAD(TO_CHAR(MAX(TO_NUMBER(SUBSTR(ins_deel_upper, LENGTH(lprefix) + 1))) + 1), MAX(LENGTH(ins_deel_upper) - LENGTH(lprefix)), '0'), '001')
               ELSE TO_CHAR(MAX(TO_NUMBER(SUBSTR(ins_deel_upper, LENGTH(lprefix) + 1))) + 1)
               END nextvolgnr
          INTO nextvolgnr
          FROM ins_deel
         WHERE ins_deel_upper LIKE UPPER(lprefix) || '%'
           AND fac.safe_to_integer(SUBSTR(ins_deel_upper, LENGTH(lprefix) + 1)) IS NOT NULL
           AND ins_deel_parent_key = pparentkey
           AND ins_deel_verwijder IS NULL;
      END IF;
      returnstring := pprefix || lseparator || nextvolgnr;
      RETURN returnstring;
    END;

    PROCEDURE copyInsKenmerken(pdeel_key IN NUMBER, ptosrtdeel_key IN NUMBER)
    AS
        CURSOR c_kenmerkdeel (pdeel_key IN NUMBER, ptosrtdeel_key IN NUMBER)
        IS
        SELECT kd1.ins_kenmerkdeel_key, k2.ins_kenmerk_key
          FROM ins_kenmerk      k1,
               ins_kenmerk      k2,
               ins_kenmerkdeel  kd1,
               ins_srtdeel      sd2,
               ins_srtgroep     sg2,
               ins_srtkenmerk   sk
         WHERE     k1.ins_srtkenmerk_key = k2.ins_srtkenmerk_key
               AND k1.ins_kenmerk_key <> k2.ins_kenmerk_key
               AND sk.ins_srtkenmerk_key = k1.ins_srtkenmerk_key
               AND COALESCE (k1.ins_kenmerk_omschrijving,
                             sk.ins_srtkenmerk_omschrijving) =
                   COALESCE (k2.ins_kenmerk_omschrijving,
                             sk.ins_srtkenmerk_omschrijving)
               AND sd2.ins_srtgroep_key = sg2.ins_srtgroep_key
               AND k1.ins_kenmerk_niveau = k2.ins_kenmerk_niveau
               AND k1.ins_kenmerk_niveau <> 'C'
               AND kd1.ins_kenmerk_key = k1.ins_kenmerk_key
               AND k2.ins_srtinstallatie_key =
                   (CASE
                        WHEN k2.ins_kenmerk_niveau = 'D' THEN sg2.ins_discipline_key
                        WHEN k2.ins_kenmerk_niveau = 'G' THEN sg2.ins_srtgroep_key
                        WHEN k2.ins_kenmerk_niveau = 'S' THEN sd2.ins_srtdeel_key
                    END)
               AND kd1.ins_deel_key = pdeel_key
               AND sd2.ins_srtdeel_key = ptosrtdeel_key;

        CURSOR c_facbijlagen (pdeel_key IN NUMBER, ptosrtdeel_key IN NUMBER)
        IS
        SELECT fb1.fac_bijlagen_key, k2.ins_kenmerk_key
          FROM ins_kenmerk     k1,
              ins_kenmerk     k2,
              fac_bijlagen    fb1,
              ins_srtdeel     sd2,
              ins_srtgroep    sg2,
              ins_srtkenmerk  sk
        WHERE     k1.ins_srtkenmerk_key = k2.ins_srtkenmerk_key
              AND k1.ins_kenmerk_key <> k2.ins_kenmerk_key
              AND sk.ins_srtkenmerk_key = k1.ins_srtkenmerk_key
              AND COALESCE (k1.ins_kenmerk_omschrijving,
                            sk.ins_srtkenmerk_omschrijving) =
                  COALESCE (k2.ins_kenmerk_omschrijving,
                            sk.ins_srtkenmerk_omschrijving)
              AND sd2.ins_srtgroep_key = sg2.ins_srtgroep_key
              AND k1.ins_kenmerk_niveau = k2.ins_kenmerk_niveau
              AND k1.ins_kenmerk_niveau <> 'C'
              AND fb1.fac_bijlagen_kenmerk_key = k1.ins_kenmerk_key
              AND k2.ins_srtinstallatie_key =
                  (CASE
                        WHEN k2.ins_kenmerk_niveau = 'D' THEN sg2.ins_discipline_key
                        WHEN k2.ins_kenmerk_niveau = 'G' THEN sg2.ins_srtgroep_key
                        WHEN k2.ins_kenmerk_niveau = 'S' THEN sd2.ins_srtdeel_key
                    END)
              AND fb1.fac_bijlagen_refkey = pdeel_key
              AND sd2.ins_srtdeel_key = ptosrtdeel_key;
    BEGIN
        FOR kd_upd IN c_kenmerkdeel (pdeel_key, ptosrtdeel_key)
        LOOP
            UPDATE ins_kenmerkdeel
               SET ins_kenmerk_key = kd_upd.ins_kenmerk_key
             WHERE ins_kenmerkdeel_key = kd_upd.ins_kenmerkdeel_key;
        END LOOP;

        FOR fb_upd IN c_facbijlagen (pdeel_key, ptosrtdeel_key)
        LOOP
            UPDATE fac_bijlagen
               SET fac_bijlagen_kenmerk_key = fb_upd.ins_kenmerk_key
             WHERE fac_bijlagen_key = fb_upd.fac_bijlagen_key;
        END LOOP;
    END;

    -- omkatten van het objectsoort van een object. Controleert wat technische voorwaarden qua binding
    -- maar kijkt niet naar bv autorisaties. Trackt alleen als er ook echt iets verandert
    PROCEDURE altersrtdeel(pdeel_key IN NUMBER, ptosrtdeel_key IN NUMBER)
    AS
        fromsrtdeel_key           ins_deel.ins_srtdeel_key%TYPE;
        fromsrtdeelomschrijving   ins_srtdeel.ins_srtdeel_omschrijving%TYPE;
        frombinding               ins_deel.ins_alg_ruimte_type%TYPE;
        frombindingval            NUMBER;
        tobinding                 ins_srtdeel.ins_srtdeel_binding%TYPE;
        tosrtdeelomschrijving     ins_srtdeel.ins_srtdeel_omschrijving%TYPE;
    BEGIN
        SELECT d.ins_srtdeel_key,
              d.ins_alg_ruimte_type,
              lcl.x ('ins_srtdeel_omschrijving', sd.ins_srtdeel_key, sd.ins_srtdeel_omschrijving)
          INTO fromsrtdeel_key, frombinding, fromsrtdeelomschrijving
          FROM ins_deel d, ins_srtdeel sd
        WHERE d.ins_srtdeel_key = sd.ins_srtdeel_key AND ins_deel_key = pdeel_key;

        -- frombinding is one of A(8),P(16),R(1),T(4),W(2)
        SELECT DECODE (frombinding,  'R', 1,  'W', 2,  'T', 4,  'A', 8,  'P', 16,  32)
          INTO frombindingval
          FROM DUAL;

        BEGIN
            SELECT ins_srtdeel_binding,
                  lcl.x ('ins_srtdeel_omschrijving', ins_srtdeel_key, ins_srtdeel_omschrijving)
              INTO tobinding, tosrtdeelomschrijving
              FROM ins_srtdeel
            WHERE     ins_srtdeel_key = ptosrtdeel_key
                  AND BITAND (ins_srtdeel_binding, frombindingval) = frombindingval;

            IF fromsrtdeel_key <> ptosrtdeel_key
            THEN
                UPDATE ins_deel
                  SET ins_srtdeel_key = ptosrtdeel_key
                WHERE ins_deel_key = pdeel_key;

                -- de redundante ins_deel.ins_discipline_key wordt door de trigger bijgewerkt

                copyInsKenmerken(pdeel_key, ptosrtdeel_key);

                -- het is nu mogelijk dat er kenmerken (ins_kenmerkdeel) zijn die qua definitie niet horen
                -- tot de kenmerken van de nieuwe soort. Ik vind dat niet erg.

                -- de tracking zou discipline/srtgroep/srtdeel -> discipline/srtgroep/srtdeel kunnen zijn
                -- voor de volledigheid, maar daar mag iemand om gaan vragen, zo essentieel is dat niet
                fac.trackaction (
                    'INSUPD',
                    pdeel_key,
                    TO_NUMBER (SYS_CONTEXT ('USERENV', 'CLIENT_IDENTIFIER')),
                    NULL,
                    fromsrtdeelomschrijving || lcl.l ('lcl_trackto') || tosrtdeelomschrijving);
            END IF;
        EXCEPTION
            WHEN NO_DATA_FOUND
            THEN
                DBMS_OUTPUT.put_line ('Cannot alter srtdeel: bindings do not match');
        END;
    END;

    FUNCTION sprintf (ps IN varchar2, pins_deel_key IN number)
       RETURN varchar2
    IS
       lins_deel_omschrijving ins_deel.ins_deel_omschrijving%TYPE;
       lins_deel_opmerking ins_deel.ins_deel_opmerking%TYPE;
       lins_srtdeel_omschrijving ins_srtdeel.ins_srtdeel_omschrijving%TYPE;
       s varchar2 (2048 CHAR);
    BEGIN
       s := ps;
         -- We support substitution of placeholders in the messages
         -- ##DESC##   = ins_deel_omschrijving (eigenlijk de ID, maar te laat om aan te passen)
         -- ##REMARK## = ins_deel_opmerking
         -- ##TYPE##   = ins_srtdeel_omschrijving
         IF INSTR2 (s, '#') > 0
         THEN
           SELECT ins_deel_omschrijving, ins_deel_opmerking, ins_srtdeel_omschrijving
             INTO lins_deel_omschrijving, lins_deel_opmerking, lins_srtdeel_omschrijving
             FROM ins_deel id, ins_srtdeel isd
            WHERE id.ins_srtdeel_key = isd.ins_srtdeel_key
              AND ins_deel_key = pins_deel_key;
           s :=  REPLACE (REPLACE (REPLACE (REPLACE (s, '##DESC##', lins_deel_omschrijving), '##KEY##', TO_CHAR (pins_deel_key)), '##REMARK##', lins_deel_opmerking), '##TYPE##', lins_srtdeel_omschrijving) ;
         END IF;

       RETURN s;
    END;

    FUNCTION contrsprintf (ps IN varchar2, pins_deelsrtcontr_key IN number)
       RETURN varchar2
    IS
       s varchar2 (2048 CHAR);
    BEGIN
       s := ps;
         IF INSTR2 (s, '#') > 0
         THEN
           s :=  REPLACE (s, '##KEY##', TO_CHAR (pins_deelsrtcontr_key));
         END IF;

       RETURN s;
    END;

    -- Berekent de n-de (p_steps) controledatum na p_date.
    -- Indien p_steps niet is meegegeven dan wordt de eerstvolgende controledatum na nu teruggegeven.
    -- steps = 0, (NULL): 1-ste vandaag of in de toekomst (als meegegeven datum (p_date) de huidige datum is, dan deze weer teruggeven).
    -- steps = n: n-volgende vanaf meegegeven datum (p_date). Als deze berekende datum in de toekomst ligt en steps is nog niet null, dan wordt NULL teruggegeven).
    -- De einddatum van het object (xcp.ins_srtcontroledl_xcp_eind) wordt niet meegenomen. De volgende inspectiedatum wordt gewoon teruggeven.
    FUNCTION nextcyclusdate (p_insdeel     IN NUMBER,
                             p_srtcontrole IN NUMBER,
                             p_scenario    IN NUMBER,
                             p_steps       IN NUMBER DEFAULT 0)
        RETURN DATE
    IS
        inspdate   DATE;
    BEGIN
        SELECT fac.nextcyclusdate (COALESCE ( (SELECT CASE xcp.ins_srtcontrole_mode
                                                      WHEN 0
                                                      THEN idsc.ins_deelsrtcontrole_datum_org
                                                      ELSE idsc.ins_deelsrtcontrole_datum
                                                      END ins_deelsrtcontrole_datum
                                                 FROM ins_deelsrtcontrole idsc
                                                WHERE idsc.ins_srtcontrole_key = xcp.ins_srtcontrole_key
                                                  AND idsc.ins_deelsrtcontrole_status IN (5,6)
                                                  AND idsc.ins_scenario_key = 1
                                                  AND idsc.ins_deel_key = xcp.ins_deel_key
                                                  -- Voor mjob moeten de NIET succesvolle inspecties niet worden meegenomen.
                                                  -- Voor periodieke inspectie laten we het vooralsnog hetzelfde.
                                                  AND idsc.ins_deelsrtcontrole_key = (SELECT MAX(ins_deelsrtcontrole_key)
                                                                                        FROM (SELECT idsc2.ins_deelsrtcontrole_key
                                                                                                   , idsc2.ins_deel_key
                                                                                                   , idsc2.ins_srtcontrole_key
                                                                                                   , 0 ctr_ismjob
                                                                                                FROM ins_deelsrtcontrole idsc2
                                                                                               WHERE idsc2.ins_deelsrtcontrole_status IN (5,6)
                                                                                                 AND idsc2.ins_scenario_key = 1
                                                                                              UNION
                                                                                              SELECT idsc2.ins_deelsrtcontrole_key
                                                                                                   , idsc2.ins_deel_key
                                                                                                   , idsc2.ins_srtcontrole_key
                                                                                                   , 1 ctr_ismjob
                                                                                                FROM ins_deelsrtcontrole idsc2,
                                                                                                     ins_controlemode cm
                                                                                               WHERE idsc2.ins_controlemode_key = cm.ins_controlemode_key
                                                                                                 AND idsc2.ins_deelsrtcontrole_status IN (5,6)
                                                                                                 AND idsc2.ins_scenario_key = 1
                                                                                                 AND cm.ins_controlemode_success = 1) aa
                                                                                       WHERE aa.ins_deel_key = xcp.ins_deel_key
                                                                                         AND aa.ins_srtcontrole_key = xcp.ins_srtcontrole_key
                                                                                         AND aa.ctr_ismjob = xcp.ctr_ismjob)),
                                              xcp.ins_srtcontroledl_xcp_startdat,
                                              xcp.ins_deel_aanmaak),
                                   xcp.ins_srtcontrole_mode,
                                   xcp.ins_srtcontrole_eenheid,
                                   xcp.ins_srtcontrole_periode,
                                   xcp.ins_srtcontrole_bits,
                                   p_steps,
                                   xcp.ctr_ismjob
                                  ) inspectie_next
           INTO inspdate
           FROM ins_v_defined_inspect_xcp xcp
          WHERE xcp.ins_deel_key = p_insdeel
            AND xcp.ins_srtcontrole_key = p_srtcontrole
            AND xcp.ins_scenario_key = p_scenario;

        RETURN inspdate;
    END;

    -- De einddatum van het object (xcp.ins_srtcontroledl_xcp_eind) wordt niet meegenomen. De volgende inspectiedatum wordt gewoon teruggeven.
    -- Wordt alleen gebruikt voor Active Situatie (AS) soort controle's. Scenario wordt NULL verondersteld.
    FUNCTION nextcyclusdatesteps (p_insdeel     IN NUMBER,
                                  p_srtcontrole IN NUMBER,
                                  p_steps       IN NUMBER DEFAULT 1)     -- Standaard beginnen te tellen bij 1.
        RETURN NUMBER
    IS
        lsteps     NUMBER;
    BEGIN
        SELECT fac.nextcyclusdatesteps (COALESCE ( (SELECT COALESCE(idsc.ins_deelsrtcontrole_plandatum, idsc.ins_deelsrtcontrole_datum_org) ins_deelsrtcontrole_datum
                                                      FROM ins_deelsrtcontrole idsc
                                                     WHERE xcp.ins_srtcontrole_key = idsc.ins_srtcontrole_key
                                                       AND idsc.ins_deelsrtcontrole_status IN (5,6)
                                                       AND idsc.ins_scenario_key = 1
                                                       AND idsc.ins_deel_key = xcp.ins_deel_key
                                                       AND idsc.ins_deelsrtcontrole_key = (SELECT MAX(idsc2.ins_deelsrtcontrole_key)
                                                                                             FROM ins_deelsrtcontrole idsc2
                                                                                             WHERE idsc2.ins_deel_key = xcp.ins_deel_key
                                                                                             AND idsc2.ins_srtcontrole_key = xcp.ins_srtcontrole_key
                                                                                              AND idsc2.ins_deelsrtcontrole_status IN (5,6)
                                                                                              AND idsc2.ins_scenario_key = 1)),
                                                    xcp.ins_srtcontroledl_xcp_startdat,
                                                    ins_deel_aanmaak),
                                        xcp.ins_srtcontrole_mode,
                                        xcp.ins_srtcontrole_eenheid,
                                        xcp.ins_srtcontrole_periode,
                                        xcp.ins_srtcontrole_bits,
                                        p_steps
                                       ) inspectie_steps
          INTO lsteps
          FROM ins_v_defined_inspect_xcp xcp
         WHERE xcp.ins_deel_key = p_insdeel
           AND xcp.ins_srtcontrole_key = p_srtcontrole
           AND xcp.ins_scenario_key = 1;

        RETURN lsteps;
    END;

    -- Geef deelsrtcontrolekey, gewenste status en userkey, en de status wortdt
    -- gezet, waarbij zonodig (indien verandering) een trackrecord wordt gemaakt
    -- Als de status niet wijzigt, wordt ook geen trackrecord gemaakt
    PROCEDURE setinspectstatus (pdlsrtcontrkey IN NUMBER, pstatus IN NUMBER, puserkey IN NUMBER)
    AS
       currentstatus ins_deelsrtcontrole.ins_deelsrtcontrole_status%TYPE;
       newstatus ins_deelsrtcontrole.ins_deelsrtcontrole_status%TYPE;
       eventcode fac_srtnotificatie.fac_srtnotificatie_code%TYPE;
    BEGIN
       SELECT   ins_deelsrtcontrole_status
         INTO   currentstatus
         FROM   ins_deelsrtcontrole
        WHERE   ins_deelsrtcontrole_key = pdlsrtcontrkey;
       CASE
          WHEN pstatus = 0  -- Gepland
          THEN
             newstatus :=  pstatus;
          WHEN pstatus = 2  -- In behandeling
          THEN
             newstatus :=  pstatus;
             eventcode :=  'INSCBE';
          WHEN pstatus = 3 AND currentstatus = 2 -- Ter uitvoering
          THEN
             newstatus :=  pstatus;
             eventcode :=  'INSCTU';
          WHEN pstatus = 5  -- Afgemeld
          THEN
             newstatus :=  pstatus;
             eventcode :=  'INSCAF';
          WHEN pstatus = 6  -- Verwerkt
          THEN
             newstatus :=  pstatus;
             eventcode :=  'INSCVE';
          ELSE
             -- Invalid statuschange or no change
             newstatus :=  NULL;
       END CASE;
       IF newstatus IS NOT NULL
       THEN
          -- vooralsnog lopen de notificaties 1-op-1 met de tracking
          -- noticode :=  eventcode;
          UPDATE   ins_deelsrtcontrole
             SET   ins_deelsrtcontrole_status = newstatus
           WHERE   ins_deelsrtcontrole_key = pdlsrtcontrkey;
          -- We know that trackaction doesnt do tracking if eventcode is null
          fac.trackaction (eventcode, pdlsrtcontrkey, puserkey, NULL, NULL);
       END IF;
    END;

    -- Return the numeric difference between the values of str1 and str2 (first minus second)
    -- Rerturn NULL if undecided for whatever reason
    FUNCTION state_diff (str1 IN VARCHAR, str2 IN VARCHAR)
        RETURN NUMBER
    AS
        nrval1   NUMBER (12, 5);
        nrval2   NUMBER (12, 5);
        retval   NUMBER;
    BEGIN
        nrval1 := fac.safe_to_number (REPLACE (str1, ',', '.'));
        nrval2 := fac.safe_to_number (REPLACE (str2, ',', '.'));

        IF nrval1 IS NOT NULL AND nrval2 IS NOT NULL
        THEN
            retval := (nrval1 - nrval2);
        END IF;

        RETURN retval;
    END;

    -- The return value from state_compare is 0 if the two strings are equal,
    -- less than 0 if str1 compares less than str2 , and greater than 0
    -- if str1 compares greater than str2 (within the first maxlen characters).
    FUNCTION state_compare (str1 IN VARCHAR, str2 IN VARCHAR)
        RETURN NUMBER
    AS
    BEGIN
        RETURN SIGN (state_diff (str1, str2));
    END;

    -- Return >0 if the current state of a sensorvalue exceeds the threshold
    -- Return NULL if undecided for whatever reason (no value, no threshold, no sensor
    -- If pdays is given, the sensorstate must be set within the last pdays days
    -- This provides the option of neglecting an irrelevant too old value
    FUNCTION state_thresholdcompare (pdeel_key IN NUMBER, pdays NUMBER DEFAULT NULL)
        RETURN NUMBER
    AS
        lstate            ins_deel.ins_deel_state%TYPE;
        lstatethreshold   ins_srtdeel.ins_srtdeel_statethreshold%TYPE;
        retval            NUMBER;
    BEGIN
        IF pdeel_key IS NOT NULL
        THEN
            BEGIN
                IF pdays IS NULL
                THEN
                    SELECT ins_deel_state, ins_srtdeel_statethreshold
                      INTO lstate, lstatethreshold
                      FROM ins_deel d, ins_srtdeel sd
                     WHERE     d.ins_srtdeel_key = sd.ins_srtdeel_key
                           AND ins_deel_verwijder IS NULL
                           AND d.ins_deel_key = pdeel_key;
                ELSE
                    SELECT ins_deel_state, ins_srtdeel_statethreshold
                      INTO lstate, lstatethreshold
                      FROM ins_deel d, ins_srtdeel sd
                     WHERE     d.ins_srtdeel_key = sd.ins_srtdeel_key
                           AND ins_deel_verwijder IS NULL
                           AND ins_deel_statedate > SYSDATE - pdays
                           AND d.ins_deel_key = pdeel_key;
                END IF;

                retval := state_compare (lstate, lstatethreshold);
            EXCEPTION
                WHEN NO_DATA_FOUND
                THEN
                    retval := NULL;                   -- dat wassieal maar dan is het maar duidelijk
            END;
        END IF;

        RETURN retval;
    END;

    -- Archiveer de meetwaarde van discrete sensoren ouder danins_state_history dagen
    -- naar een dag-histogram zodat je er nog wel daggemiddelden van hebt maar
    -- niet meer de granulaire ballast.
    -- parameter pdontdelete is alleen voor testers
    PROCEDURE compress_states (pdodelete IN NUMBER DEFAULT 1)
    AS
        CURSOR c_days (eerste_dag IN DATE, laatste_dag IN DATE)
        IS
              SELECT DISTINCT TRUNC (h.ins_deel_statedate) uitvoer_dag
                FROM ins_deel_state_history h
                   , ins_deel d
                   , ins_srtdeel s
               WHERE     h.ins_deel_key = d.ins_deel_key
                     AND d.ins_srtdeel_key = s.ins_srtdeel_key
                     AND BITAND (s.ins_srtdeel_sensortype, 1) = 1
                     AND h.ins_deel_statedate BETWEEN eerste_dag AND (laatste_dag + 1)
            ORDER BY 1;

        keep_days             NUMBER (3);
        first_day_available   DATE;
        last_day_compressed   DATE;
        start_day_compress    DATE;
        end_day_compress      DATE;
    BEGIN
        -- Tot hoeveel dagen terug houden we de ruwe data?
        keep_days := TO_NUMBER (fac.getsetting ('ins_state_history'));
        -- dbms_output.put_line('today: '||to_char(today,'dd-mm-yyyy'));
        -- Vandaag comprimeren mag niet want je weet niet of je alle meetwaarden al hebt.
        end_day_compress := TRUNC (SYSDATE) - keep_days;

        IF (end_day_compress = TRUNC (SYSDATE))
        THEN
            end_day_compress := end_day_compress - 1;
        END IF;

        -- Voor welke dagen moet ins_deel_state_history gecomprimeerd worden?
        -- (voor het geval compress_states een aantal dagen niet heeft gewerkt.)
        SELECT TRUNC (COALESCE (MIN (ins_deel_statedate), end_day_compress))
          INTO first_day_available
          FROM ins_deel_state_history;

        -- dbms_output.put_line('first_day_available: ' ||to_char(first_day_available,'dd-mm-yyyy'));
        SELECT COALESCE (TRUNC (MAX (ins_deel_statedate)), first_day_available)
          INTO last_day_compressed
          FROM ins_deel_state_history_all;

        -- dbms_output.put_line('last_day_compressed: ' ||to_char(last_day_compressed,'dd-mm-yyyy'));
        SELECT GREATEST (first_day_available, (last_day_compressed + 1))
          INTO start_day_compress
          FROM DUAL;

        --DBMS_OUTPUT.put_line ('Compress from '|| TO_CHAR (start_day_compress, 'dd-mm-yyyy')||' to '|| TO_CHAR (end_day_compress, 'dd-mm-yyyy'));

        FOR r_day IN c_days (start_day_compress, end_day_compress)
        LOOP
            --DBMS_OUTPUT.put_line ('Compress ' || TO_CHAR (r_day.uitvoer_dag, 'dd-mm-yyyy'));
            --
            -- Haal de statuswijzigingen per dag. De laatste status loopt tot middernacht.
            -- Ook de status van middernacht tot de eerste statuswijziging deze dag moet worden opgehaald.
            -- Deze kan meerdere dagen voor deze dag voor het laatst gezet zijn!

            INSERT INTO ins_deel_state_history_all (ins_deel_key,
                                                    ins_deel_statedate,
                                                    ins_deel_state,
                                                    ins_deel_statetime)
                  SELECT x.ins_deel_key,
                         r_day.uitvoer_dag,
                         x.status,
                         ROUND (SUM (x.eindtijd - x.starttijd), 3) totaaltijd
                    FROM ((  SELECT a.ins_deel_state       status,
                                    r_day.uitvoer_dag      starttijd,
                                    MIN (c.ins_deel_statedate) eindtijd,
                                    a.ins_deel_key
                               FROM ins_deel_state_history a, ins_deel_state_history c
                              WHERE     a.ins_deel_key = c.ins_deel_key
                                    AND c.ins_deel_statedate BETWEEN r_day.uitvoer_dag
                                                                 AND r_day.uitvoer_dag + 1
                                    AND a.ins_deel_statedate =
                                        (SELECT MAX (b.ins_deel_statedate)
                                           FROM ins_deel_state_history b
                                          WHERE     b.ins_deel_key = a.ins_deel_key
                                                AND b.ins_deel_statedate < r_day.uitvoer_dag)
                           GROUP BY a.ins_deel_key, a.ins_deel_state)
                          UNION
                          (  SELECT a.ins_deel_state                                      status,
                                    a.ins_deel_statedate                                  starttijd,
                                    LEAST (MIN (b.ins_deel_statedate), r_day.uitvoer_dag + 1) eindtijd,
                                    a.ins_deel_key
                               FROM ins_deel_state_history a, ins_deel_state_history b
                              WHERE     a.ins_deel_statedate BETWEEN r_day.uitvoer_dag
                                                                 AND r_day.uitvoer_dag + 1
                                    AND a.ins_deel_key = b.ins_deel_key
                                    AND a.ins_deel_state <> b.ins_deel_state
                                    AND a.ins_deel_statedate < b.ins_deel_statedate
                           GROUP BY a.ins_deel_statedate, a.ins_deel_state, a.ins_deel_key)) x
                GROUP BY x.ins_deel_key, x.status
                ORDER BY 1, 2;

            COMMIT;
        END LOOP;

        IF pdodelete = 1
        THEN
            -- Verwijder alle records uit ins_deel_state_history ouder dan keep_days dagen.
            -- dat is van de discrete sensoren die zojuist gearchiveer zijn,
            -- maar dus ook de van de meetwaardesensoren, die willen we niet behouden
            -- besloten we vooralsnog. Mocht er data van niet-sensoren staan (illegaal)
            -- dan gooien we die niet weg.
            DELETE FROM ins_deel_state_history
                  WHERE ins_deel_statedate <= TRUNC (SYSDATE - keep_days)
                    AND ins_deel_key IN
                    (SELECT ins_deel_key
                       FROM ins_deel d, ins_srtdeel s
                      WHERE d.ins_srtdeel_key = s.ins_srtdeel_key AND s.ins_srtdeel_sensortype <> 0);
        END IF;
    END;

END ins;
/

REGISTERRUN('$Id$')

#endif  // INS
