/*
 * ARX: Efficient, Stable and Optimal Data Anonymization
 * Copyright (C) 2012 - 2014 Florian Kohlmayer, Fabian Prasser
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package org.deidentifier.arx.framework.check;

import org.deidentifier.arx.ARXConfiguration;
import org.deidentifier.arx.framework.check.StateMachine.Transition;
import org.deidentifier.arx.framework.check.distribution.IntArrayDictionary;
import org.deidentifier.arx.framework.check.groupify.HashGroupify;
import org.deidentifier.arx.framework.check.groupify.IHashGroupify;
import org.deidentifier.arx.framework.check.history.History;
import org.deidentifier.arx.framework.data.Data;
import org.deidentifier.arx.framework.data.DataManager;
import org.deidentifier.arx.framework.lattice.Node;
import org.deidentifier.arx.metric.Metric;

/**
 * This class orchestrates the process of checking a node for k-anonymity
 * 
 * @author Prasser, Kohlmayer
 */
public class NodeChecker implements INodeChecker {

    /** The current hash groupify. */
    protected IHashGroupify        currentGroupify;

    /** The history. */
    protected History              history;

    /** The last hash groupify. */
    protected IHashGroupify        lastGroupify;

    /** The metric. */
    protected Metric<?>            metric;

    /** The state machine. */
    protected StateMachine         stateMachine;

    /** The data transformer. */
    protected Transformer          transformer;

    /** The data. */
    private final Data             data;

    /** The config */
    private final ARXConfiguration config;

    /**
     * Creates a new NodeChecker instance.
     * 
     * @param manager
     *            The manager
     * @param metric
     *            The metric
     * @param config
     *            The anonymization configuration
     * @param historyMaxSize
     *            The history max size
     * @param snapshotSizeDataset
     *            The history threshold
     * @param snapshotSizeSnapshot
     *            The history threshold replacement
     */
    public NodeChecker(final DataManager manager, final Metric<?> metric, final ARXConfiguration config, final int historyMaxSize, final double snapshotSizeDataset, final double snapshotSizeSnapshot) {

        // Initialize all operators
        this.metric = metric;
        this.config = config;
        this.data = manager.getDataQI();
        final int initialSize = (int) (manager.getDataQI().getDataLength() * 0.01d);

        final IntArrayDictionary dictionarySensValue;
        final IntArrayDictionary dictionarySensFreq;

        if ((config.getRequirements() & ARXConfiguration.REQUIREMENT_DISTRIBUTION) != 0) {
            dictionarySensValue = new IntArrayDictionary(initialSize);
            dictionarySensFreq = new IntArrayDictionary(initialSize);
        } else {
            // Just to allow bytecode instrumentation
            dictionarySensValue = new IntArrayDictionary(0);
            dictionarySensFreq = new IntArrayDictionary(0);
        }

        this.history = new History(manager.getDataQI().getArray().length, historyMaxSize, snapshotSizeDataset, snapshotSizeSnapshot, config, dictionarySensValue, dictionarySensFreq);

        this.stateMachine = new StateMachine(history);
        this.currentGroupify = new HashGroupify(initialSize, config);
        this.lastGroupify = new HashGroupify(initialSize, config);

        this.transformer = new Transformer(manager.getDataQI().getArray(), manager.getHierarchies(), manager.getDataSE().getArray(), config, dictionarySensValue, dictionarySensFreq);
    }

    @Override
    public void check(final Node node) {

        // Store snapshot from last check
        if (stateMachine.getLastNode() != null) {
            history.store(stateMachine.getLastNode(), currentGroupify, stateMachine.getLastTransition().snapshot);
        }

        // Transition
        final Transition transition = stateMachine.transition(node);

        // Switch groupifies
        final IHashGroupify temp = lastGroupify;
        lastGroupify = currentGroupify;
        currentGroupify = temp;
        currentGroupify.clear();

        // Apply transition
        switch (transition.type) {
        case UNOPTIMIZED:
            currentGroupify = transformer.apply(transition.projection, node.getTransformation(), currentGroupify);
            break;
        case ROLLUP:
            currentGroupify = transformer.applyRollup(transition.projection, node.getTransformation(), lastGroupify, currentGroupify);
            break;
        case SNAPSHOT:
            currentGroupify = transformer.applySnapshot(transition.projection, node.getTransformation(), currentGroupify, transition.snapshot);
            break;
        }

        // Mark as checked
        node.setChecked();

        // Propagate k-anonymity
        node.setKAnonymous(currentGroupify.isKAnonymous());

        // Propagate anonymity and information loss
        if (currentGroupify.isAnonymous()) {
            node.setAnonymous(true);
            metric.evaluate(node, currentGroupify);
        } else {
            node.setInformationLoss(null);
            node.setAnonymous(false);
        }
    }

    @Override
    public Data getBuffer() {
        return new Data(transformer.getBuffer(), data.getHeader(), data.getMap(), data.getDictionary());
    }

    @Override
    public ARXConfiguration getConfiguration() {
        return config;
    }

    @Override
    public Data getData() {
        return data;
    }

    @Override
    public int getNumberOfGroups() {
        return currentGroupify.size();
    }

    @Override
    public IHashGroupify getGroupify() {
        return currentGroupify;
    }

    @Override
    public int getNumberOfOutlyingGroups() {
        return currentGroupify.getGroupOutliersCount();
    }

    /**
     * Returns the checkers history, if any
     * @return
     */
    public History getHistory() {
        return history;
    }

    @Override
    @Deprecated
    public double getInformationLoss(final Node node) {
        check(node);
        metric.evaluate(node, currentGroupify);
        return node.getInformationLoss().getValue();

    }

    @Override
    public Metric<?> getMetric() {
        return metric;
    }

    @Override
    public int getNumberOfOutlyingTuples() {
        return currentGroupify.getTupleOutliersCount();
    }

    @Override
    @Deprecated
    public Data transform(final Node node) {

        // Apply transition and groupify
        currentGroupify.clear();
        currentGroupify = transformer.apply(0L, node.getTransformation(), currentGroupify);

        // Determine outliers and set infoloss
        if (!node.isChecked()) {
            node.setChecked();
            node.setAnonymous(currentGroupify.isAnonymous());
            metric.evaluate(node, currentGroupify);
            node.setTagged();
        }

        return getBuffer();
    }

    @Override
    public Data transformAndMarkOutliers(final Node node) {

        // Apply transition and groupify
        currentGroupify.clear();
        currentGroupify = transformer.apply(0L, node.getTransformation(), currentGroupify);

        // Determine outliers and set infoloss
        node.setAnonymous(currentGroupify.isAnonymous());
        if (!node.isChecked()) {
            node.setChecked();
            metric.evaluate(node, currentGroupify);
            node.setTagged();
        }

        // Find outliers
        if (config.getAbsoluteMaxOutliers() != 0) {
            currentGroupify.markOutliers(transformer.getBuffer());
        }

        // Return the buffer
        return getBuffer();
    }
}

@@ -7055,458 +7055,53 @@
     
-// Apply transition and groupify%0A        currentGroupify.clear();%0A        currentGroupify = transformer.apply(0L, node.getTransformation(), currentGroupify);%0A%0A        // Determine outliers and set infoloss%0A        if (!node.isChecked()) %7B%0A            node.setChecked();%0A            node.setAnonymous(currentGroupify.isAnonymous());%0A            metric.evaluate(node, currentGroupify);%0A            node.setTagged();%0A        %7D%0A%0A        return getBuffer(
+throw new RuntimeException(%22Not implemented!%22
 );%0A 

Remove implementation of deprecated method