/**
 * \file pappsomspp/amino_acid/aaBase.cpp
 * \date 7/3/2015
 * \author Olivier Langella
 * \brief private amino acid model
 */

/*******************************************************************************
 * Copyright (c) 2015 Olivier Langella <Olivier.Langella@moulon.inra.fr>.
 *
 * This file is part of the PAPPSOms++ library.
 *
 *     PAPPSOms++ 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.
 *
 *     PAPPSOms++ 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 PAPPSOms++.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 *     Olivier Langella <Olivier.Langella@moulon.inra.fr> - initial API and
 *implementation
 ******************************************************************************/

#include <QDebug>
#include <QObject>

#include "pappsomspp/core/amino_acid/aabase.h"
#include "pappsomspp/core/exception/exceptionnotfound.h"
#include "pappsomspp/core/exception/exceptionnotpossible.h"

using namespace std;

namespace pappso
{

AaBase::AaBase(char aa_letter) : m_aaLetter(aa_letter)
{
  /*
   if (AaBase::_aa_mass_map.empty()) {
   AaBase::static_builder();
   }
   */
  qDebug() << aa_letter;
  if(m_aaMassMap.find(aa_letter) == m_aaMassMap.end())
    {
      qDebug() << "error";
      if((int)aa_letter == 0)
        {
          throw ExceptionNotPossible(QObject::tr("amino acid letter is void"));
        }
      else
        {
          throw ExceptionNotFound(QObject::tr("amino acid %1 not found").arg(aa_letter));
        }
    }
  qDebug();
}

AaBase::AaBase(Enums::AminoAcidChar aa_char) : m_aaLetter((char)aa_char)
{
}


AaBase::AaBase(const AaBase &aa) : m_aaLetter(aa.m_aaLetter)
{
}

AaBase::~AaBase()
{
  // TODO Auto-generated destructor stub
}

AaBase::AminoAcidCharList AaBase::m_aminoAcidCharList = [] {
  AminoAcidCharList ret = {
    Enums::AminoAcidChar::alanine,        Enums::AminoAcidChar::arginine,
    Enums::AminoAcidChar::cysteine,       Enums::AminoAcidChar::aspartic_acid,
    Enums::AminoAcidChar::glutamic_acid,  Enums::AminoAcidChar::phenylalanine,
    Enums::AminoAcidChar::glycine,        Enums::AminoAcidChar::histidine,
    Enums::AminoAcidChar::isoleucine,     Enums::AminoAcidChar::lysine,
    Enums::AminoAcidChar::leucine,        Enums::AminoAcidChar::methionine,
    Enums::AminoAcidChar::asparagine,     Enums::AminoAcidChar::proline,
    Enums::AminoAcidChar::glutamine,      Enums::AminoAcidChar::serine,
    Enums::AminoAcidChar::threonine,      Enums::AminoAcidChar::valine,
    Enums::AminoAcidChar::tryptophan,     Enums::AminoAcidChar::tyrosine,
    Enums::AminoAcidChar::selenocysteine, Enums::AminoAcidChar::pyrrolysine,
  };


  return ret;
}();

AaBase::AaMassMap AaBase::m_aaMassMap = [] {
  AaMassMap ret;
  // populate ret
  // http://education.expasy.org/student_projects/isotopident/htdocs/aa-list.html

  // 	C11H12N2O2
  ret.insert(std::pair<char, pappso_double>(
    'W', pappso_double(MASSCARBON * 11 + MPROTIUM * 10 + MASSNITROGEN * 2 + MASSOXYGEN * 1)));
  // C2H5NO2
  ret.insert(std::pair<char, pappso_double>(
    'G', pappso_double(MASSCARBON * 2 + MPROTIUM * 3 + MASSNITROGEN * 1 + MASSOXYGEN * 1)));

  // C3H7NO2
  ret.insert(std::pair<char, pappso_double>(
    'A', pappso_double(MASSCARBON * 3 + MPROTIUM * 5 + MASSNITROGEN * 1 + MASSOXYGEN * 1)));
  // C3H7NO3
  ret.insert(std::pair<char, pappso_double>(
    'S', pappso_double(MASSCARBON * 3 + MPROTIUM * 5 + MASSNITROGEN * 1 + MASSOXYGEN * 2)));
  // C5H9NO2
  ret.insert(std::pair<char, pappso_double>(
    'P', pappso_double(MASSCARBON * 5 + MPROTIUM * 7 + MASSNITROGEN * 1 + MASSOXYGEN * 1)));
  // C5H11NO2
  ret.insert(std::pair<char, pappso_double>(
    'V', pappso_double(MASSCARBON * 5 + MPROTIUM * 9 + MASSNITROGEN * 1 + MASSOXYGEN * 1)));
  // C4H9NO3
  ret.insert(std::pair<char, pappso_double>(
    'T', pappso_double(MASSCARBON * 4 + MPROTIUM * 7 + MASSNITROGEN * 1 + MASSOXYGEN * 2)));
  // C6H13NO2
  ret.insert(std::pair<char, pappso_double>(
    'L', pappso_double(MASSCARBON * 6 + MPROTIUM * 11 + MASSNITROGEN * 1 + MASSOXYGEN * 1)));
  // C6H13NO2
  ret.insert(std::pair<char, pappso_double>(
    'I', pappso_double(MASSCARBON * 6 + MPROTIUM * 11 + MASSNITROGEN * 1 + MASSOXYGEN * 1)));
  // C4H8N2O3
  ret.insert(std::pair<char, pappso_double>(
    'N', pappso_double(MASSCARBON * 4 + MPROTIUM * 6 + MASSNITROGEN * 2 + MASSOXYGEN * 2)));
  // C4H7NO4
  ret.insert(std::pair<char, pappso_double>(
    'D', pappso_double(MASSCARBON * 4 + MPROTIUM * 5 + MASSNITROGEN * 1 + MASSOXYGEN * 3)));
  // C6H14N2O2
  ret.insert(std::pair<char, pappso_double>(
    'K', pappso_double(MASSCARBON * 6 + MPROTIUM * 12 + MASSNITROGEN * 2 + MASSOXYGEN * 1)));
  // C5H10N2O3
  ret.insert(std::pair<char, pappso_double>(
    'Q', pappso_double(MASSCARBON * 5 + MPROTIUM * 8 + MASSNITROGEN * 2 + MASSOXYGEN * 2)));
  // C5H9NO4
  ret.insert(std::pair<char, pappso_double>(
    'E', pappso_double(MASSCARBON * 5 + MPROTIUM * 7 + MASSNITROGEN * 1 + MASSOXYGEN * 3)));

  // C5H11NO2S
  ret.insert(std::pair<char, pappso_double>(
    'M',
    pappso_double(MASSCARBON * 5 + MPROTIUM * 9 + MASSNITROGEN * 1 + MASSOXYGEN * 1 + MASSSULFUR)));
  // $arrret['m'] = 147.04; #METHIONINE OXIDEE (+16)
  // case 'm':
  //	mass = (float) 131.0404;
  //	addModification((float) 15.994915);
  // C6H9N3O2
  ret.insert(std::pair<char, pappso_double>(
    'H', pappso_double(MASSCARBON * 6 + MPROTIUM * 7 + MASSNITROGEN * 3 + MASSOXYGEN * 1)));
  // C9H11NO2
  ret.insert(std::pair<char, pappso_double>(
    'F', pappso_double(MASSCARBON * 9 + MPROTIUM * 9 + MASSNITROGEN * 1 + MASSOXYGEN * 1)));
  // C6H14N4O2
  ret.insert(std::pair<char, pappso_double>(
    'R', pappso_double(MASSCARBON * 6 + MPROTIUM * 12 + MASSNITROGEN * 4 + MASSOXYGEN * 1)));
  // C3H7NO2S
  ret.insert(std::pair<char, pappso_double>(
    'C',
    pappso_double(MASSCARBON * 3 + MPROTIUM * 5 + MASSNITROGEN * 1 + MASSOXYGEN * 1 + MASSSULFUR)));
  // mass = (float) 161.01; // CYSTEINE CARBAMIDOMETHYLE
  // addModification((float) 57.021464);
  // C9H11NO3
  ret.insert(std::pair<char, pappso_double>(
    'Y', pappso_double(MASSCARBON * 9 + MPROTIUM * 9 + MASSNITROGEN * 1 + MASSOXYGEN * 2)));

  // Pyrrolysine C12H21N3O3
  ret.insert(std::pair<char, pappso_double>(
    'O', pappso_double(MASSCARBON * 12 + MPROTIUM * 21 + MASSNITROGEN * 3 + MASSOXYGEN * 3)));

  // Selenocysteine	C3H7NO2Se
  ret.insert(
    std::pair<char, pappso_double>('U',
                                   pappso_double(MASSCARBON * 3 + MPROTIUM * 7 + MASSNITROGEN * 1 +
                                                 MASSOXYGEN * 2 + MASSSELENIUM)));
  // 168.018678469607
  // ret.insert(std::pair<char, pappso_double>('U', pappso_double(168.964203)));
  //_aa_mass_map.insert(
  //		std::pair<char, pappso_double>('X', pappso_double(103.00919)));
  return ret;
}();

AaBase::AaIntMap AaBase::m_aaNumberOfCarbonMap = [] {
  AaIntMap ret;
  // populate ret

  ret.insert(std::pair<char, unsigned int>('W', 11));
  ret.insert(std::pair<char, unsigned int>('G', 2));
  ret.insert(std::pair<char, unsigned int>('A', 3));
  ret.insert(std::pair<char, unsigned int>('S', 3));
  ret.insert(std::pair<char, unsigned int>('P', 5));
  ret.insert(std::pair<char, unsigned int>('V', 5));
  ret.insert(std::pair<char, unsigned int>('T', 4));
  ret.insert(std::pair<char, unsigned int>('L', 6));
  ret.insert(std::pair<char, unsigned int>('I', 6));
  ret.insert(std::pair<char, unsigned int>('N', 4));
  ret.insert(std::pair<char, unsigned int>('D', 4));
  ret.insert(std::pair<char, unsigned int>('K', 6));
  ret.insert(std::pair<char, unsigned int>('Q', 5));
  ret.insert(std::pair<char, unsigned int>('E', 5));
  ret.insert(std::pair<char, unsigned int>('M', 5));
  ret.insert(std::pair<char, unsigned int>('H', 6));
  ret.insert(std::pair<char, unsigned int>('F', 9));
  ret.insert(std::pair<char, unsigned int>('R', 6));
  ret.insert(std::pair<char, unsigned int>('C', 3));
  ret.insert(std::pair<char, unsigned int>('Y', 9));
  // Selenocysteine	C3H7NO2Se
  ret.insert(std::pair<char, unsigned int>('U', 3));
  // Pyrrolysine C12H21N3O3
  ret.insert(std::pair<char, unsigned int>('O', 12));
  return ret;
}();


AaBase::AaIntMap AaBase::m_aaNumberOfHydrogenMap = [] {
  AaIntMap ret;
  // populate ret

  ret.insert(std::pair<char, unsigned int>('A', 5));
  ret.insert(std::pair<char, unsigned int>('C', 5));
  ret.insert(std::pair<char, unsigned int>('D', 5));
  ret.insert(std::pair<char, unsigned int>('E', 7));
  ret.insert(std::pair<char, unsigned int>('F', 9));
  ret.insert(std::pair<char, unsigned int>('G', 3));
  ret.insert(std::pair<char, unsigned int>('H', 7));
  ret.insert(std::pair<char, unsigned int>('I', 11));
  ret.insert(std::pair<char, unsigned int>('K', 12));
  ret.insert(std::pair<char, unsigned int>('L', 11));
  ret.insert(std::pair<char, unsigned int>('M', 9));
  ret.insert(std::pair<char, unsigned int>('N', 6));
  ret.insert(std::pair<char, unsigned int>('P', 7));
  ret.insert(std::pair<char, unsigned int>('Q', 8));
  ret.insert(std::pair<char, unsigned int>('R', 12));
  ret.insert(std::pair<char, unsigned int>('S', 5));
  ret.insert(std::pair<char, unsigned int>('T', 7));
  ret.insert(std::pair<char, unsigned int>('V', 9));
  ret.insert(std::pair<char, unsigned int>('W', 10));
  ret.insert(std::pair<char, unsigned int>('Y', 9));
  // Selenocysteine	C3H7NO2Se
  ret.insert(std::pair<char, unsigned int>('U', 7));
  // Pyrrolysine C12H21N3O3
  ret.insert(std::pair<char, unsigned int>('O', 21));
  return ret;
}();


AaBase::AaIntMap AaBase::m_aaNumberOfNitrogenMap = [] {
  AaIntMap ret;
  // populate ret

  ret.insert(std::pair<char, unsigned int>('A', 1));
  ret.insert(std::pair<char, unsigned int>('C', 1));
  ret.insert(std::pair<char, unsigned int>('D', 1));
  ret.insert(std::pair<char, unsigned int>('E', 1));
  ret.insert(std::pair<char, unsigned int>('F', 1));
  ret.insert(std::pair<char, unsigned int>('G', 1));
  ret.insert(std::pair<char, unsigned int>('H', 3));
  ret.insert(std::pair<char, unsigned int>('I', 1));
  ret.insert(std::pair<char, unsigned int>('K', 2));
  ret.insert(std::pair<char, unsigned int>('L', 1));
  ret.insert(std::pair<char, unsigned int>('M', 1));
  ret.insert(std::pair<char, unsigned int>('N', 2));
  ret.insert(std::pair<char, unsigned int>('P', 1));
  ret.insert(std::pair<char, unsigned int>('Q', 2));
  ret.insert(std::pair<char, unsigned int>('R', 4));
  ret.insert(std::pair<char, unsigned int>('S', 1));
  ret.insert(std::pair<char, unsigned int>('T', 1));
  ret.insert(std::pair<char, unsigned int>('V', 1));
  ret.insert(std::pair<char, unsigned int>('W', 2));
  ret.insert(std::pair<char, unsigned int>('Y', 1));
  // Selenocysteine	C3H7NO2Se
  ret.insert(std::pair<char, unsigned int>('U', 1));
  // Pyrrolysine C12H21N3O3
  ret.insert(std::pair<char, unsigned int>('O', 3));
  return ret;
}();

AaBase::AaIntMap AaBase::m_aaNumberOfOxygenMap = [] {
  AaIntMap ret;
  // populate ret

  ret.insert(std::pair<char, unsigned int>('A', 1));
  ret.insert(std::pair<char, unsigned int>('C', 1));
  ret.insert(std::pair<char, unsigned int>('D', 3));
  ret.insert(std::pair<char, unsigned int>('E', 3));
  ret.insert(std::pair<char, unsigned int>('F', 1));
  ret.insert(std::pair<char, unsigned int>('G', 1));
  ret.insert(std::pair<char, unsigned int>('H', 1));
  ret.insert(std::pair<char, unsigned int>('I', 1));
  ret.insert(std::pair<char, unsigned int>('K', 1));
  ret.insert(std::pair<char, unsigned int>('L', 1));
  ret.insert(std::pair<char, unsigned int>('M', 1));
  ret.insert(std::pair<char, unsigned int>('N', 2));
  ret.insert(std::pair<char, unsigned int>('P', 1));
  ret.insert(std::pair<char, unsigned int>('Q', 2));
  ret.insert(std::pair<char, unsigned int>('R', 1));
  ret.insert(std::pair<char, unsigned int>('S', 2));
  ret.insert(std::pair<char, unsigned int>('T', 2));
  ret.insert(std::pair<char, unsigned int>('V', 1));
  ret.insert(std::pair<char, unsigned int>('W', 1));
  ret.insert(std::pair<char, unsigned int>('Y', 2));
  // Selenocysteine	C3H7NO2Se
  ret.insert(std::pair<char, unsigned int>('U', 2));
  // Pyrrolysine C12H21N3O3
  ret.insert(std::pair<char, unsigned int>('O', 3));
  return ret;
}();

AaBase::AaIntMap AaBase::m_aaNumberOfSulfurMap = [] {
  AaIntMap ret;
  // populate ret

  ret.insert(std::pair<char, unsigned int>('A', 0));
  ret.insert(std::pair<char, unsigned int>('C', 1));
  ret.insert(std::pair<char, unsigned int>('D', 0));
  ret.insert(std::pair<char, unsigned int>('E', 0));
  ret.insert(std::pair<char, unsigned int>('F', 0));
  ret.insert(std::pair<char, unsigned int>('G', 0));
  ret.insert(std::pair<char, unsigned int>('H', 0));
  ret.insert(std::pair<char, unsigned int>('I', 0));
  ret.insert(std::pair<char, unsigned int>('K', 0));
  ret.insert(std::pair<char, unsigned int>('L', 0));
  ret.insert(std::pair<char, unsigned int>('M', 1));
  ret.insert(std::pair<char, unsigned int>('N', 0));
  ret.insert(std::pair<char, unsigned int>('P', 0));
  ret.insert(std::pair<char, unsigned int>('Q', 0));
  ret.insert(std::pair<char, unsigned int>('R', 0));
  ret.insert(std::pair<char, unsigned int>('S', 0));
  ret.insert(std::pair<char, unsigned int>('T', 0));
  ret.insert(std::pair<char, unsigned int>('V', 0));
  ret.insert(std::pair<char, unsigned int>('W', 0));
  ret.insert(std::pair<char, unsigned int>('Y', 0));
  // Selenocysteine	C3H7NO2Se
  ret.insert(std::pair<char, unsigned int>('U', 0));
  // Pyrrolysine C12H21N3O3
  ret.insert(std::pair<char, unsigned int>('O', 0));
  return ret;
}();

pappso_double
AaBase::getAaMass(char aa_letter)
{
  return m_aaMassMap.at(aa_letter);
}


pappso_double
AaBase::getMass() const
{
  return m_aaMassMap.at(m_aaLetter);
}


int
AaBase::getNumberOfAtom(Enums::AtomIsotopeSurvey atom) const
{
  switch(atom)
    {
      case Enums::AtomIsotopeSurvey::C:
        return this->m_aaNumberOfCarbonMap.at(m_aaLetter);
      case Enums::AtomIsotopeSurvey::H:
        return this->m_aaNumberOfHydrogenMap.at(m_aaLetter);
      case Enums::AtomIsotopeSurvey::N:
        return this->m_aaNumberOfNitrogenMap.at(m_aaLetter);
      case Enums::AtomIsotopeSurvey::O:
        return this->m_aaNumberOfOxygenMap.at(m_aaLetter);
      case Enums::AtomIsotopeSurvey::S:
        return this->m_aaNumberOfSulfurMap.at(m_aaLetter);
      default:
        return 0;
    }
  // selenium (U) is not taken into account to compute isotopes
  // it has 5 stable isotopes and the most abundant is 80Se (49,61%)
  qDebug() << "AaBase::getNumberOfAtom(Enums::AtomIsotopeSurvey atom) NOT IMPLEMENTED";
  return 0;
}


void
AaBase::replaceLeucineIsoleucine()
{
  if(m_aaLetter == 'L')
    m_aaLetter = 'I';
}


int
AaBase::getNumberOfIsotope(Enums::Isotope isotope [[maybe_unused]]) const
{
  return 0;
}


const char &
AaBase::getLetter() const
{
  return m_aaLetter;
}

const Enums::AminoAcidChar &
AaBase::getAminoAcidChar() const
{
  return (Enums::AminoAcidChar &)m_aaLetter;
}

const std::vector<Enums::AminoAcidChar> &
AaBase::getAminoAcidCharList()
{
  return m_aminoAcidCharList;
}
} /* namespace pappso */
