#ifndef ALT_HPP
#define ALT_HPP

#include <sstream>
#include <string>
#include <set>
#include <map>

#include "ustate.hpp"

/*struct alt
{
  alt(std::string x):x_(x){}
  
  char& operator[](unsigned int z){return  x_[z];}

  std::string x_;
};
*/

typedef std::string alt;

struct ostate
{
  ostate(const alt&a, const ustate&u)
  {
    if(a.length()!=u.n_) throw "length";
    
    for(int i=0;i<u.n_;++i)
    {
      if(u.numat[i]>0) 
      {
        lets.insert(std::string(1,a[i]));
        for(int j=i+1;j<u.n_;++j)
        {
          if(/*u.numat[i]>0&&*/u.numat[j]>0) obis.insert(std::string(1,a[i])+a[j]);
          if(u.numat[i]+u.numat[j]==4)
          {
            if(j==i+1) cbis.insert(std::string(1,a[i])+a[j]);
            else cbis.insert(std::string(1,a[i])+"_"+a[j]);
          }
        }
      }
    }
    
    if(u.numat[0]>1) cbis.insert(std::string("_")+a[0]);
    if(u.numat[u.n_-1]>1) cbis.insert(std::string(1,a[u.n_-1])+"_");
    
    std::stringstream ss;
    
    std::multiset<std::string>::iterator i=lets.begin(),k=lets.end();
    if(i!=k) ss<<*i++;
    while(i!=k) ss<<","<<*i++;
    ss<<";";
    i=obis.begin(); k=obis.end();
    if(i!=k) ss<<*i++;
    while(i!=k) ss<<","<<*i++;
    ss<<";";
    i=cbis.begin(); k=cbis.end();
    if(i!=k) ss<<*i++;
    while(i!=k) ss<<","<<*i++;
    ss<<";";
    
    sig_=ss.str();
  }
  
  bool operator<(const ostate&other)const
  {
    return sig_<other.sig_;
  }  

  bool operator==(const ostate&other)const
  {
    return sig_==other.sig_;
  }  
  
  std::multiset<std::string> lets,obis,cbis;

  std::string sig_;
};

struct target:alt
{
  target(const char*x=""):alt(std::string(x)){}
  target(const std::string& x):alt(x){}
//  target(alt x):alt(x){}

  static std::map<std::string,std::set<ustate> > mismatch_map;
  
  std::set<ustate>& mismatches(const alt& foil) const
  {
    std::set<ustate>*r;
    std::string spec=*this;
    spec+="/";
    spec+=foil;
    #pragma omp critical(mm)
    {
    std::map<std::string,std::set<ustate> >::iterator i=mismatch_map.find(spec);
    if(i==mismatch_map.end())
    {
      std::set<ustate> z=mismatches_work(foil);
      r=&(mismatch_map[spec]=z);
        
    }
    else r=&i->second;
    }
    return *r;
  }
  
  std::set<ustate> mismatches_work(const alt& foil) const 
  {
    std::set<ustate> r;
    std::set<ostate> fst;
  
    for(ustate_it i(foil.length(),true),k(foil.length(),false);i!=k;++i)
    {
      ustate u=*i;
      ostate temp(foil,u);
//      std::cerr<<temp.sig_<<" :)\n";
      fst.insert(temp);
//      std::cerr<<u.hash()<<"x\n";
    }

//    std::cerr<<fst.size()<<"\n";

    for(ustate_it i(length(),true),k(length(),false);i!=k;++i)
    {
      ustate u=*i;
      ostate temp(*this,u);
//      std::cerr<<temp.sig_<<" \n";
      int z=fst.count(temp);
//      std::cerr<<z<<"\n";
      if(0==z)
      {
        r.insert(u);
//        std::cerr<<"hash"<<"\n";
//        std::cerr<<(u).hash()<<"\n";
      }
    }
    return r;
  }

  double pcor(const alt& foil,const double*ps)
  {
    double p=0.;
    std::set<ustate> mm=mismatches(foil);
//    std::cerr<<mm.size()<<"s\n";
    for(std::set<ustate>::const_iterator i=mm.begin(),k=mm.end();i!=k;++i)
    {
//      std::cerr<<"HASH\n";
//      std::cerr<<(*i).hash()<<"\n";

      double pp=i->prob(ps);
      p+=pp;
//      std::cerr<<ostate(*this,*i).sig_<<"\t"<<pp<<"\n";
    }
    return p;
  }

};


#endif

