/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA 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 MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <iostream>
#include <fstream>
#include <stdexcept>

#include <mia/core/probmap.hh>

NS_MIA_BEGIN
using namespace std;

static const string pipe_console("-");
static const string map_signature("#probability-map");
static const string label_map_signature("#label-map");


CProbabilityVector::CProbabilityVector()
{
}

CProbabilityVector::CProbabilityVector(size_t nClass, size_t nElm):
       std::vector<double_vector>(nClass, double_vector(nElm))
{
}

CProbabilityVector::CProbabilityVector(const std::string& in_fname)
{
       if (in_fname == pipe_console) {
              do_load(cin);
       } else {
              ifstream instr(in_fname.c_str());
              do_load(instr);
       }
}

bool CProbabilityVector::save(const std::string& out_fname) const
{
       if (out_fname == pipe_console) {
              return do_save(cout);
       } else {
              ofstream outstr(out_fname.c_str());
              return do_save(outstr);
       }
}

bool CProbabilityVector::do_save(ostream& os) const
{
       os << map_signature << "\n";
       os << size() << " " << (*this)[0].size() << '\n';

       for (size_t i = 0; i < (*this)[0].size(); ++i) {
              for (size_t k = 0; k < size(); ++k) {
                     os << (*this)[k][i] << " ";
              }

              os << '\n';
       }

       return os.good();
}

void CProbabilityVector::do_load(istream& is)
{
       string buf;
       is >> buf;
       size_t hsize, nclasses;

       if (buf != map_signature) {
              throw invalid_argument(string("input file is not a probability map: signature:'") + buf + string("'"));
       }

       is >> nclasses >> hsize;

       if (!is.good())
              throw runtime_error("error reading from input file");

       resize(nclasses);

       for (size_t k = 0; k < size(); ++k) {
              (*this)[k] = double_vector(hsize);
       }

       /* hsize is first used to allocate a vector. If it is too big
          a std.:bad_alloc exception will be thrown. This is not worse
          than a check for size that would restrict hsize to an abitrary
          value */
       // coverity[tainted_scalar]
       for (size_t i = 0; i < hsize; ++i) {
              for (size_t k = 0; k < size(); ++k) {
                     is >> (*this)[k][i];
              }
       }

       if (!is.good())
              throw runtime_error("error reading from input file");
}

EXPORT_CORE bool operator == (const CProbabilityVector& a, const CProbabilityVector& b)
{
       if ( a.size() != b.size() )
              return false;

       auto ai = a.begin();
       auto ae = a.end();
       auto bi = b.begin();

       while (ai != ae)  {
              if (*ai++ != *bi++)
                     return false;
       }

       return true;
}


CLabelMap::CLabelMap()
{
}

CLabelMap::CLabelMap(const std::string& in_fname)
{
       if (in_fname == pipe_console) {
              do_load(cin);
       } else {
              ifstream instr(in_fname.c_str());
              do_load(instr);
       }
}

bool CLabelMap::save(const std::string& out_fname) const
{
       if (out_fname == pipe_console) {
              return do_save(cout);
       } else {
              ofstream outstr(out_fname.c_str());
              return do_save(outstr);
       }
}


bool CLabelMap::do_save(ostream& os) const
{
       os << label_map_signature << "\n";
       os << size() << '\n';

       for (auto im = begin(), em = end();
            im != em; ++im) {
              os << im->first << ' ' << im->second << '\n';
       }

       return os.good();
}

void CLabelMap::do_load(istream& is)
{
       string buf;
       is >> buf;
       size_t hsize;

       if (buf != label_map_signature) {
              throw invalid_argument(string("input file is not a label map: signature:'") + buf + string("'"));
       }

       is >> hsize;

       if (!is.good())
              throw runtime_error("error reading from input file");

       size_t src, trgt;

       /* hsize is first used to allocate a vector. If it is too big
          a std.:bad_alloc exception will be thrown. This is not worse
          than a check for size that would restrict hsize to an abitrary
          value */
       // coverity[tainted_scalar]
       for (size_t i = 0; i < hsize; ++i) {
              is >> src >> trgt;
              (*this)[src] = trgt;
       }

       if (!is.good())
              throw runtime_error("error reading from input file");
}

EXPORT_CORE bool operator == (const CLabelMap& a, const CLabelMap& b)
{
       if (a.size() != b.size())
              return false;

       auto ai = a.begin();
       auto bi = b.begin();
       auto ae = a.end();

       while (ai != ae) {
              if (ai->first != bi->first ||
                  ai->second != bi->second)
                     return false;

              ++ai;
              ++bi;
       }

       return true;
}


NS_MIA_END
