/*
 * 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.
 *
 */

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include "AUtils.h"
#include "AuFile.h"
#include "BmpFile.h"
#include "CvrStgFile.h"
#include "DFSAPHeuristic.h"
#include "JpegFile.h"
#include "SampleValue.h"
#include "SampleValueAdjacencyList.h"
#include "Utils.h"
#include "WKSConstructionHeuristic.h"
#include "WavFile.h"
#include "common.h"
#include "error.h"
#include "msg.h"

CvrStgFile::CvrStgFile() : BinIO(NULL), SamplesPerVertex(0), Radius(0), EmbValueModulus(0) {
    Globs.TheCvrStgFile = this;
}

CvrStgFile::~CvrStgFile(void) {
    if (BinIO != NULL) {
        delete BinIO;
    }
}

void CvrStgFile::setRadius(UWORD32 r) {
    if (Args.Radius.is_set()) {
        Radius = Args.Radius.getValue();
    } else {
        Radius = r;
    }
}

void CvrStgFile::read(BinaryIO *io) { setBinIO(io); }

void CvrStgFile::write(void) {}

void CvrStgFile::transform(const std::string &stgfn) {
    delete BinIO;
    BinIO = NULL;
    BinIO = new BinaryIO(stgfn, BinaryIO::WRITE);
}

std::vector<SampleValueAdjacencyList *>
CvrStgFile::calcSVAdjacencyLists(const std::vector<SampleValue *> &svs) const {
    EmbValue m = getEmbValueModulus();
    std::vector<SampleValueAdjacencyList *> lists(m);
    for (EmbValue i = 0; i < m; i++) {
        lists[i] = new SampleValueAdjacencyList(svs.size());
    }

    for (SampleValueLabel i = 0; i < svs.size(); i++) { // to be integrated into lists
        SampleValueAdjacencyList *sval = lists[svs[i]->getEmbeddedValue()];
        for (SampleValueLabel j = 0; j < svs.size(); j++) { // line where integration should happen
            if ((svs[i]->isNeighbour(svs[j])) && (i != j)) {
                (*sval)[svs[j]].push_back(svs[i]);
            }
        }
    }

    for (EmbValue i = 0; i < m; i++) {
        lists[i]->sort();
    }

    return lists;
}

std::vector<MatchingAlgorithm *> CvrStgFile::getMatchingAlgorithms(Graph *g, Matching *m) const {
    std::vector<MatchingAlgorithm *> retval;
    retval.push_back(new WKSConstructionHeuristic(g, m));
    retval.push_back(new DFSAPHeuristic(g, m));
    return retval;
}

EmbValue CvrStgFile::getEmbeddedValue(const SamplePos pos) const {
    SampleValue *sv = getSampleValue(pos);
    EmbValue retval = sv->getEmbeddedValue();
    delete sv;
    return retval;
}

unsigned long CvrStgFile::getCapacity() const {
    float maxnvertices = getNumSamples() / getSamplesPerVertex();
    float maxnbits = maxnvertices * log2(getEmbValueModulus());
    return ((unsigned long)(maxnbits / 8));
}

std::string CvrStgFile::getHRCapacity() const { return Utils::formatHRSize(getCapacity()); }

#ifdef DEBUG
std::map<SampleKey, unsigned long> *CvrStgFile::getFrequencies() {
    unsigned long n = getNumSamples();
    std::map<SampleKey, unsigned long> *table = new std::map<SampleKey, unsigned long>();

    for (unsigned long pos = 0; pos < n; pos++) {
        SampleValue *sv = getSampleValue(pos);
        (*table)[sv->getKey()]++;
        delete sv;
    }

    return table;
}

void CvrStgFile::printFrequencies(const std::map<SampleKey, unsigned long> &freqs) {
    for (std::map<SampleKey, unsigned long>::const_iterator fit = freqs.begin(); fit != freqs.end();
         fit++) {
        std::cout << fit->first << ": " << fit->second << std::endl;
    }
}

#endif // def DEBUG

CvrStgFile::FILEFORMAT CvrStgFile::guessff(BinaryIO *io) {
    char buf[4] = {'\0', '\0', '\0', '\0'};
    FILEFORMAT retval = UNKNOWN;

    for (unsigned int i = 0; i < 2; i++) {
        buf[i] = (char)io->read8();
    }

    if (strncmp("BM", buf, 2) == 0) {
        retval = BMP;
    } else if ((unsigned char)buf[0] == 0xFF && (unsigned char)buf[1] == 0xD8) {
        retval = JPEG;
    } else {
        for (unsigned int i = 2; i < 4; i++) {
            buf[i] = (char)io->read8();
        }

        if (strncmp(".snd", buf, 4) == 0) {
            retval = AU;
        } else if (strncmp("RIFF", buf, 4) == 0) {
            retval = WAV;
        }
    }

    return retval;
}

CvrStgFile *CvrStgFile::readFile(const std::string &fn) {
    BinaryIO *BinIO = new BinaryIO(fn, BinaryIO::READ);

    CvrStgFile *file = NULL;
    switch (guessff(BinIO)) {
    case UNKNOWN: {
        throw UnSupFileFormat(BinIO);
        break;
    }

    case BMP: {
        file = new BmpFile(BinIO);
        break;
    }

    case WAV: {
        file = new WavFile(BinIO);
        break;
    }

    case AU: {
        file = new AuFile(BinIO);
        break;
    }

    case JPEG: {
        file = new JpegFile(BinIO);
        break;
    }

    default: {
        myassert(0);
        break;
    }
    }

    return file;
}
