/***************************************************************************
 *   Copyright (C) 2004 by Armando Navarro-Vazquez                         *
 *   uscqoanv@cesga.es                                                     *
 *                                                                         *
 *   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 <fstream>
#include <iostream>

#include "jcampdxparser.h"

#include "tokenizer.h"
#include "nmrspectrum.h"
#include "peaktablespectrum.h"
#include "standardspectrum.h"

JCAMPDXParser::JCAMPDXParser(QString& file)
    : SpectrumParser(file)
{
  BuildMap();
}

void JCAMPDXParser::BuildMap()
{
  m_diffmap['%']=0;
  m_diffmap['A']=1;
  m_diffmap['B']=2;
  m_diffmap['C']=3;
  m_diffmap['D']=4;
  m_diffmap['E']=5;
  m_diffmap['F']=6;
  m_diffmap['G']=7;
  m_diffmap['H']=8;
  m_diffmap['I']=9;
  m_diffmap['%']=0;
  m_diffmap['a']=1;
  m_diffmap['b']=2;
  m_diffmap['c']=3;
  m_diffmap['d']=4;
  m_diffmap['e']=5;
  m_diffmap['f']=6;
  m_diffmap['g']=7;
  m_diffmap['h']=8;
  m_diffmap['i']=9;
  m_diffmap['J']=1;
  m_diffmap['K']=2;
  m_diffmap['L']=3;
  m_diffmap['M']=4;
  m_diffmap['N']=5;
  m_diffmap['O']=6;
  m_diffmap['P']=7;
  m_diffmap['Q']=8;
  m_diffmap['R']=9;
  m_diffmap['j']=-1;
  m_diffmap['k']=-2;
  m_diffmap['l']=-3;
  m_diffmap['m']=-4;
  m_diffmap['n']=-5;
  m_diffmap['o']=-6;
  m_diffmap['p']=-7;
  m_diffmap['q']=-8;
  m_diffmap['r']=-9;
  m_diffmap['S']=1;
  m_diffmap['T']=2;
  m_diffmap['U']=3;
  m_diffmap['V']=4;
  m_diffmap['W']=5;
  m_diffmap['X']=6;
  m_diffmap['Y']=7;
  m_diffmap['Z']=8;
  m_diffmap['s']=9;
}


JCAMPDXParser::~JCAMPDXParser()
{}


Spectrum* JCAMPDXParser::GetSpectrum(std::ifstream& file,std::string& line)
{
  while(std::getline(file,line))
  {
    if (line.find("##DATA TYPE") != std::string::npos)
    {
      if( line.find("NMR") != std::string::npos )
      {
        std::cout << "NMR Spectrum" << std::endl;
        return new NMRSpectrum();
      }
    }

    if (line.find("##PEAK TABLE") != std::string::npos)
    {
      std::cout << "Peak Table Spectrum" << std::endl;
      return new PeakTableSpectrum();
    }

  }
  std::cout << "Standard Spectrum" << std::endl;
  return new StandardSpectrum();

}

Spectrum* JCAMPDXParser::ParseFile()
{

  std::ifstream jcampfile;
  jcampfile.open(m_file.toLatin1());
  if(! jcampfile ) return 0;

  std::string line;
  float firstx,lastx;
  std::streampos datapos;
  size_t npoints;
  float yfactor=1;
  Spectrum* spectrum=GetSpectrum(jcampfile,line);
  jcampfile.clear();
  jcampfile.seekg(0);

  if(!spectrum) return 0;

  fidarray& data=spectrum->Data();

  while(std::getline(jcampfile,line))
  {

    StringTokenizer token(line," =");
    if( token.size() > 1 )
    {
      std::string& uctoken=token.at(0);
      StringTokenizer::toupper(uctoken);

      if(uctoken=="##NPOINTS")
      {
        npoints=std::atoi(token.at(1).c_str());
        //fid.SetPoints(npoints);
        data.resize(npoints);
      }

      if(uctoken=="##FIRSTX") firstx=std::atof(token.at(1).c_str());
      if(uctoken=="##LASTX") lastx=std::atof(token.at(1).c_str());
      if (uctoken=="##YFACTOR") yfactor=std::atof(token.at(1).c_str());
      if (uctoken=="##XUNITS") spectrum->SetXUnits(token.at(1).c_str());
      if (uctoken=="##YUNITS") spectrum->SetYUnits(token.at(1).c_str());

      //OK lets write the scale
      spectrum->SetO1( (firstx-lastx)/2 +lastx );
      spectrum->SetSW( firstx-lastx );
      if(uctoken=="##XYDATA") GetData(*spectrum,jcampfile);
      if(uctoken=="##PEAK" ) GetTable(*spectrum,jcampfile);
      /*if(uctoken=="##$BYTORDA")
        bByteSwap=(bool)atoi(token[1].c_str());
      if(uctoken=="##$DECIM")
        dec=atoi(token[1].c_str());
      if(uctoken=="##$DSPFVS")
        dsp=atoi(token[1].c_str());
      if(uctoken=="##$TD")
        npoints=atoi(token[1].c_str());
      if(uctoken=="##$O1")
        fid.SetO1(atof(token[1].c_str()));
      if(uctoken=="##$SW_H")
        fid.SetSW(atof(token[1].c_str()));
      if(uctoken=="##$SFO1")
        fid.SetField(atof(token[1].c_str()));
      if(uctoken=="##NUC1")
        fid.SetNucleus(token[1].c_str());*/
    }
  }

  /* fid.SetSW(lastx-firstx);
   fid.SetO1((lastx+firstx)/2);
   jcampfile.clear();
   jcampfile.seekg(datapos);
   std::getline(jcampfile,line);
   
   size_t point=0;
   while(std::getline(jcampfile,line))
   {
     StringTokenizer token(line," \t");
     std::cerr << line << std::endl;
     StringTokenizer::iterator it;
     for(it=token.begin()+1;it!=token.end();it++)
     { 
       //std::cerr << (*it);
       if(point >= (npoints -1) ) return true;
       data[point++].re=std::atof((it->c_str()));
     }
     
     
   }*/
  size_t i=0;
  std::cout << data.size() << std::endl;
  std::cout << "yfactor is "  << yfactor;

  for(i=0;i<data.size();i++)
  {
    data[i].real()*=yfactor;
  }

  for(size_t i=0;i<data.size();++i)
  {
    std::cout << "data " << data[i].real() << " " << data[i].imag() << std::endl;
  }
  return spectrum;


}

const std::string delimiters="%ABCDEFGHIabcdefghi%JKLMNOPQRjklmnopqrSTUVWXYZs";


JCAMPDXParser::jctokenizer::jctokenizer(const std::string &rStr, const std::string &rDelimiters)
{
  std::string::size_type lastPos(rStr.find_first_not_of(rDelimiters, 0));
  std::string::size_type pos(rStr.find_first_of(rDelimiters, lastPos+1));
  while (  std::string::npos != lastPos )
  {
    //std::cout << rStr.substr(lastPos, pos - lastPos) << std::endl;
    push_back(rStr.substr(lastPos, pos - lastPos));

    lastPos = rStr.find_first_of(rDelimiters, pos);
    pos = rStr.find_first_of(rDelimiters, lastPos+1);
  }
}

void JCAMPDXParser::GetData(Spectrum& spectrum, std::ifstream& jcampfile)
{
  std::string line;
  bool bfirst=true;
  bool firstline=true;
  bool isdiff=false;
  fidarray& data=spectrum.Data();
  unsigned int counter=0;
  int duplicates;
  float value;
  std::streampos pos=jcampfile.tellg();
  std::getline(jcampfile,line);
  if( line.find_first_of(delimiters) != std::string::npos )
  {
    jcampfile.clear();
    jcampfile.seekg(pos);
    while(std::getline(jcampfile,line))
    {
      std::string::size_type pos=line.find('\r') ;
      if( pos != std::string::npos )
        line=line.substr(0,pos);
      if(line.find("##END") == 0 || counter >= data.size() )
      {
        //std::cout << "counter is " << counter << std::endl;
        return;
      }

      if(firstline)
      {
        if( line.find_first_of(delimiters,0) != std::string::npos)
          isdiff=true;
      }
      jctokenizer tok(line,delimiters);
      jctokenizer::iterator jt;
      for(jt=tok.begin();jt!=tok.end();++jt)
      {
        //std::cout << *jt << " ";
      }
      std::cout << std::endl;
      jctokenizer::iterator it=tok.begin()+1;
      if (firstline)
      {
        data[counter++].real()=GetValue(*it,duplicates,isdiff);
        std::cout << data[counter-1].real() << ",";
      }
      else  //Check the value
      {
        float chkvalue=GetValue(*it,duplicates,isdiff);
        std::cout << "Checkpoint" << chkvalue << std::endl; //"," << data[counter-1].re << std::endl;
        //if ( chkvalue != data[counter].re ) std::cout << "Error during checking" << std::endl;
      }
      for(it=tok.begin()+2;it!=tok.end();++it)
      {
        //std::cout << "getting "  << *it << std::endl;
        int duplicates;
        float lastvalue;
        value=GetValue(*it,duplicates,isdiff);
        if(duplicates)
        {
          //std::cout << "duplicates are" << duplicates << std::endl;

          for(int i=0;i<duplicates-1;++i)
          {
            float previous=data[counter-1].real();

            data[counter++].real()=previous+lastvalue;
            std::cout << data[counter-1].real() << ",";
          }

        }
        else
          if(isdiff)
          {
            float previous=data[counter-1].real();
            data[counter++]=previous+value;
            std::cout << data[counter-1].real() << ",";
          }

        lastvalue=value;

      }
      std::cout << std::endl;
      firstline=false;
    }
  }
  else
  {
    jcampfile.clear();
    jcampfile.seekg(pos);
    int point=0;
    while(std::getline(jcampfile,line))
    {
      StringTokenizer token(line," \t");
      StringTokenizer::iterator it;
      for(it=token.begin()+1;it!=token.end();it++)
      {
        //std::cerr << (*it);
        if(point >= ( data.size() -1) ) return;
        data[point++].real()=std::atof((it->c_str()));
      }
    }

  }
}


const std::string duplicates="STUVWXYZs";
const std::string diffdelimiters="%ABCDEFGHIabcdefghiJKLMNOPQRjklmnopqr";
const std::string numbers="0123456789";
float JCAMPDXParser::GetValue(const std::string& val, int& dup,bool& diff)
{
  //check duplicates
  dup=0;
  diff=false;
  std::string::size_type pos=val.find_first_of(duplicates,0);
  if ( pos != std::string::npos)
  {
    if (val.find_first_of(numbers,0) == 0 )
    {
      dup=m_diffmap[val[pos]]*pow(10.,static_cast<int>(val.size()-1))+atof(val.substr(1,val.size()-1).c_str());
    }
    else
    {
      dup=m_diffmap[val[pos]];
    }
    return 0;
  }

  pos=val.find_first_of(diffdelimiters,0);
  if( pos == 0 )
  {
    diff=true;

    float valor=m_diffmap[val[pos]]*pow(10.,static_cast<int>(val.size()-1));

    if(valor < 0 ) return valor-atof(val.substr(1,val.size()-1).c_str());
    else return valor+atof(val.substr(1,val.size()-1).c_str());

  }

  return atof(val.c_str());

}

void JCAMPDXParser::GetTable(Spectrum& spectrum, std::ifstream& jcampfile)
{

  std::string line;
  fidarray& data=spectrum.Data();
  std::cout << "getting table " << data.size() << std::endl;
  int counter=0;
  while(std::getline(jcampfile,line))
  {
    std::cout << line << std::endl;
    if( line.find("##END") == 0 || ( counter >= data.size() ) ) return;
    std::cout << "Hi" << std::endl;
    StringTokenizer token(line,", \t\n\r");
    StringTokenizer::iterator it;

    //std::cout << line << std::endl;
    for( it=token.begin();it!=token.end();it+=2,++counter)
    {
      std::cout << "counter=" << counter << " " << atof(it->c_str()) << std::endl;
      data[counter].real()=atof((it+1)->c_str());
      data[counter].imag()=atof( it->c_str());

    }
  }

}
