/*
 * steghide 0.5.1 - a steganography program
 * Copyright (C) 1999-2003 Stefan Hetzl <shetzl@chello.at>
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

#ifndef SH_SAMPLEVALUE_H
#define SH_SAMPLEVALUE_H

#include <functional>
#include <unordered_map>

#include "common.h"

class CvrStgFile;

/**
 * \class SampleValue
 * \brief the value of a sample in a CvrStgFile
 *
 * This is the abstract base class for all AuSampleValue, BmpSampleValue, etc. classes
 *
 * For two sample values s1 and s2:
 *
 * s1->calcDistance(s2) == s2->calcDistance(s1) is always true.
 *
 * s1->isNeighbour(s2) == s2->isNeighbour(s1) is always true.
 *
 * s1 and s2 are called opposite if s1->getBit() != s2->getBit()
 *
 * s1 and s2 are called neighbours if s1->isNeighbour(s2) is true
 *
 * s1->getKey() == s2->getKey() iff s1 == s2
 *
 * s1 == s2 implies s1->getDistance(s2) == 0
 * BUT: s1->getDistance(s2) == 0 does not imply s1 == s2
 *      example: 8-bit bmp palette image - same color value for two different indices
 *
 * s1 == s2 implies s1->getBit() == s2->getBit()
 *
 * s1->getDistance(s2) == 0 implies s1->getBit() == s2->getBit()
 *
 * <b>NOTE:</b> SampleValue and all derived classes rely on the Globals object
 * pointed to by the Globs pointer. This means that it must be set correctly
 * before using any method of a SampleValue (or derived) object.
 **/
class SampleValue {
  public:
    SampleValue(void);

    virtual ~SampleValue(void);

    /**
     * get the nearest (with the least distance to this sample value) sample value whose
     * embedded value equals the specified target
     * \param t the target embedded value
     *
     * If two or more target sample values have equal distance each of them should be
     * returned with equal probability.
     *
     * The returned SampleValue object should be deleted by the callser.
     **/
    virtual SampleValue *getNearestTargetSampleValue(EmbValue t) const = 0;

    /**
     * calculate the distance between the sample value s and this sample value
     * \param s a sample value of the same type as this
     * \return the distance
     **/
    virtual UWORD32 calcDistance(const SampleValue *s) const = 0;

    /**
     * return a short name uniquely identifying this sample value
     **/
    virtual std::string getName(void) const = 0;

    /**
     * is the sample value s a neighbour of this sample value ?
     * \return true iff this and s are neighbours
     *
     * This is implemented as (calcDistance() <= Radius) but may be overridden by
     * derived classes.
     **/
    virtual bool isNeighbour(const SampleValue *s) const;

    /**
     * get the value that is embedded in this sample value (must be >=0 and <EmbValueModulus)
     * \return the embedded value
     **/
    EmbValue getEmbeddedValue(void) const { return EValue; };

    /**
     * get the key for this sample
     * \return a key which must be different for two different samples values.
     **/
    UWORD32 getKey(void) const { return Key; };

    /**
     * two sample values are equal iff their keys are equal
     **/
    bool operator==(const SampleValue &sv) const { return (getKey() == sv.getKey()); };
    bool operator!=(const SampleValue &sv) const { return (getKey() != sv.getKey()); };

    bool operator<(const SampleValue &sv) const { return (getKey() < sv.getKey()); };

    UWORD32 getNumEdges(EmbValue t) const { return NumEdges[t]; };
    void setNumEdges(EmbValue t, UWORD32 ne) { NumEdges[t] = ne; };
    void incNumEdges(EmbValue t);
    void decNumEdges(EmbValue t);

    void setLabel(unsigned long l) { Label = l; };
    unsigned long getLabel(void) const { return Label; };

    void print(unsigned short spc = 0) const;

  protected:
    /// the bit that is embedded in this sample value - must be set in constructor of derived class
    EmbValue EValue;

    /// the key of this sample value - must be different for two different sample values - must be
    /// set in constructor of derived class
    UWORD32 Key;

  private:
    unsigned long Label;

    /**
     * NumEdges[t] contains the number of edges that are added to a vertex if
     * this sample value with corresponding target value t is added to the vertex
     **/
    UWORD32 *NumEdges;
};

struct SampleValuesEqual : public std::binary_function<SampleValue *, SampleValue *, bool> {
    bool operator()(const SampleValue *s1, const SampleValue *s2) const { return (*s1 == *s2); }
};

struct SampleValuesLess : public std::binary_function<SampleValue *, SampleValue *, bool> {
    bool operator()(const SampleValue *s1, const SampleValue *s2) const { return (*s1 < *s2); }
};

struct SampleValueHash : public std::unary_function<SampleValue *, size_t> {
    size_t operator()(const SampleValue *s) const {
        std::hash<UWORD32> h;
        return h(s->getKey());
    }
};

#endif // ndef SH_CVRSTGSAMPLE_H
