/*******************************************************************************
 * Copyright (c) 2015-2016 ONERA.
 * Ce logiciel est la propriété de l’ONERA (the French Aerospace Lab).
 * Tous droits réservés.
 *
 * Ce programme et les éléments qui l'accompagnent sont mis à disposition
 * aux conditions définies par le contrat de licence logiciel CeCILL-C soumise
 * au droit français et respectant les principes de diffusion des logiciels libres.
 * Vous pouvez utiliser, modifier et/ou redistribuer ce programme
 * sous les conditions de la licence CeCILL-C (http://www.cecill.info).

 * This software is property of ONERA (the French Aerospace Lab).
 * All rights reserved.
 *
 * This program and the accompanying materials are made available under
 * the terms of the CeCILL-C license under French law and
 * abiding by the rules of distribution of free software.
 * You can  use, modify and/ or redistribute the software under the terms of
 * the CeCILL-C license (http://www.cecill.info).
 *
 * Contributeurs/contributors:
 *     Guillaume Infantes (ONERA - Centre de Toulouse) - initial API and implementation
 *
 *******************************************************************************/

#include "MemberAccess.hh"
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <sstream>
#include <boost/tokenizer.hpp>


namespace epoch {
    namespace altarica {

	Ident::~Ident() {
	}

	MemberAccess::MemberAccess(){
	    isGlobal_ = false;
	    doString();
	}




	MemberAccess::MemberAccess(std::string id, std::vector<int> *v) {
	    isGlobal_ = false;
	    Ident* i = new Ident();
	    i->id = id;
	    for (std::vector<int>::iterator j = v->begin(); j != v->end(); ++j)
		i->indexes.push_back(*j);
	    //	    i->indexes = v;
	    path_.push_back(i);
	    doString();
	}

	MemberAccess::MemberAccess(std::string id) {
    isGlobal_ = false;
    std::istringstream iss(id);
    std::string slice;
    bool first = true;
    
    while (std::getline(iss,slice,'.')) {		
      Ident* i = new Ident();
      boost::char_separator<char> sep("[]");
      boost::tokenizer<boost::char_separator<char> > tok(slice, sep);
      i->id = *(tok.begin());
      if (first && i->id.compare("main") == 0)
        isGlobal_ = true;      
      first = false;
      
      //		i->indexes = new std::vector<int>();		
      for (boost::tokenizer<boost::char_separator<char> >::const_iterator j = ++tok.begin();
           j != tok.end(); ++j)
        i->indexes.push_back(atoi((*j).c_str()));
      path_.push_back(i);
    }
    doString();
	}
      


	void MemberAccess::addIdBack(std::string id) {
	    Ident * nap = new Ident();
	    nap->id = id;
	    //	    nap->indexes = new std::vector<int>();
	    path_.push_back(nap);
	    doString();
	}


	void MemberAccess::addIdBack(std::string id, std::vector<int> &iv) {
	    Ident * nap = new Ident();
	    nap->id = id;
	    for (std::vector<int>::iterator i = iv.begin(); i!=iv.end(); ++i)
		nap->indexes.push_back(*i);
	    path_.push_back(nap);	    
	    doString();
	}

	void MemberAccess::addIdBackNoString(std::string id, std::vector<int> &iv) {
	    Ident * nap = new Ident();
	    nap->id = id;
	    for (std::vector<int>::iterator i = iv.begin(); i!=iv.end(); ++i)
		nap->indexes.push_back(*i);
	    //	    nap->indexes = iv;
	    path_.push_back(nap);	    
	}



	void MemberAccess::addIdBack(std::string id, int i) {
	    Ident * nap = new Ident();
	    nap->id = id;
	    //	    nap->indexes = new std::vector<int>();
	    nap->indexes.push_back(i);
	    path_.push_back(nap);	    
	    doString();
	}
	
	void MemberAccess::addIdFront(std::string id) {
	    Ident * nap = new Ident();
	    nap->id = id;
	    //	    nap->indexes = new std::vector<int>();
	    path_.insert(path_.begin(),nap);	    
	    doString();
	}

	void MemberAccess::addIdFront(std::string id, std::vector<int> &iv) {
	    Ident * nap = new Ident();
	    nap->id = id;
	    for (std::vector<int>::iterator i = iv.begin(); i!=iv.end(); ++i)
		nap->indexes.push_back(*i);
	    //	    nap->indexes = iv;
	    path_.insert(path_.begin(),nap);	    
	    doString();
	}

	void MemberAccess::addIdFront(std::string id, int i) {
	    Ident * nap = new Ident();
	    nap->id = id;
	    //	    nap->indexes = new std::vector<int>();
	    nap->indexes.push_back(i);
	    path_.insert(path_.begin(),nap);	    
	    doString();
	}

	void MemberAccess::removeIdFront() {
	    Ident * a= path_[0];	    
	    path_.erase(path_.begin());
	    delete a;
	    doString();
	}

	void MemberAccess::removeIdFrontNoString() {
	    Ident * a= path_[0];	    
	    path_.erase(path_.begin());
	    delete a;
	}


	bool MemberAccess::removeIdFront(std::string id) {
	    if (id.compare(path_[0]->id)!= 0)
		return false;
	    removeIdFront();
	    return true;
	    doString();
	}

	bool MemberAccess::removeIdFront(std::string id, std::vector<int> *iv) {
	    Ident * a = path_[0];
	    if ((!sameIndexes(iv, &a->indexes)) || (id.compare(a->id)!= 0)) 
		return false;
	    removeIdFront();
	    doString();
	    return true;
	}

	bool MemberAccess::removeIdFrontNoString(std::string id, std::vector<int> *iv) {
	    Ident * a = path_[0];
	    if ((!sameIndexes(iv, &a->indexes)) || (id.compare(a->id)!= 0)) 
		return false;
	    removeIdFrontNoString();
	    return true;
	}


	
	void MemberAccess::addIndexBack(int i) {
	    path_[path_.size()-1]->indexes.push_back(i);
	    doString();
	}

	void MemberAccess::addIndexesBack(std::vector<int> *iv){
	    for (std::vector<int>::iterator i = iv->begin();
		 i != iv->end(); ++i)
		addIndexBack(*i);
	    doString();
	}
	
	void MemberAccess::addIndexFront(int i) {
	    path_[0]->indexes.push_back(i);	    
	    doString();
	}

	void MemberAccess::addIndexesFront(std::vector<int> *iv) {
	    for (std::vector<int>::iterator i = iv->begin();
		 i != iv->end(); ++i)
		addIndexFront(*i);
	    doString();
	}
	
	void MemberAccess::addIndex(std::string id, int i) {
	    for (std::vector<Ident*>::iterator pi = path_.begin();
		 pi != path_.end(); ++pi) {
		if ((*pi)->id.compare(id)==0) {
		    (*pi)->indexes.push_back(i);
		    return;
		}		
	    }		
	    doString();
	}

	void MemberAccess::addIndexes(std::string id, std::vector<int> *v) {
	    for (std::vector<Ident*>::iterator pi = path_.begin();
		 pi != path_.end(); ++pi) {
		if ((*pi)->id.compare(id)==0) {
		    for (std::vector<int>::iterator vi = v->begin(); vi != v->end(); ++vi)
			(*pi)->indexes.push_back(*vi);
		    return;
		}		
	    }		
	    doString();	    
	}

	void MemberAccess::addIndexSecond(int i) {
	    if (path_[0]->indexes.size() != 0)
		BOOST_THROW_EXCEPTION(std::runtime_error(std::string("wrong logic in member access name while flattening array domain")));
	    path_[0]->indexes.push_back(i);
	    doString();
	}

	void MemberAccess::addIdSecond(std::string s) {
	    Ident * nap = new Ident();
	    nap->id = s;
	    //	    nap->indexes = new std::vector<int>();
	    path_.insert(++path_.begin(), nap);
	    doString();
	}

		    

	void MemberAccess::setGlobal(bool b) {
	    isGlobal_ = b;
	}

	bool MemberAccess::isGlobal() const {
	    return isGlobal_;
	}

	MemberAccess* MemberAccess::clone() const{
	    MemberAccess * n = new MemberAccess();
	    for (std::vector<Ident*>::const_iterator i = path_.begin(); i!= path_.end(); ++i)
		n->addIdBackNoString(std::string((*i)->id), (*i)->indexes);
	    n->setGlobal(isGlobal_);
	    n->doString();
	    return n;
	}
	
	const std::vector<Ident*> * MemberAccess::getAccesses() const {
	    return &path_;
	}
	
	void MemberAccess::addMemberAccessBack(MemberAccess *ma) {
	    for (std::vector<Ident*>::const_iterator i = ma->getAccesses()->begin(); 
		 i!= ma->getAccesses()->end(); ++i)
		addIdBack((*i)->id, (*i)->indexes);
	    doString();
	}

	void MemberAccess::addMemberAccessFront(MemberAccess *ma) {
	    for (std::vector<Ident*>::const_iterator i = ma->getAccesses()->begin(); 
		 i!= ma->getAccesses()->end(); ++i)
		addIdFront((*i)->id, (*i)->indexes);
	    doString();
	}

	bool MemberAccess::sameIndexes(const std::vector<int> * ind1, const std::vector<int> * ind2) {
	    if (ind1 == NULL)
		return ind2 == NULL;
	    else if (ind2 == NULL)
		return false;
	    // if (ind1->size() == 0)
	    // 	return 	(ind2->size() == 0);
	    if (ind1->size() != ind2->size())
		return false;
	    std::vector<int>::const_iterator i2 = ind2->begin();	    
	    for (std::vector<int>::const_iterator i1 = ind1->begin(); i1 != ind1->end(); ++i1) {
		if (*i1 != *i2)
		    return false;
		++i2;
	    }		
	    return true;
	}

	bool MemberAccess::equals(const MemberAccess* ma) const {
	    return (s_.compare(ma->toString()) == 0);
	    // const std::vector<Ident*> * otherPath = ma->getAccesses();
	    // if (otherPath->size() != path_.size())
	    //   	return false;
	    // std::vector<Ident*>::iterator i2 = path_.begin();
	    // for (std::vector<Ident*>::const_iterator i1 = otherPath->begin(); 	 i1 != otherPath->end(); ++i1, ++i2) {
	    // 	if ((*i1)->id.compare((*i2)->id) != 0)
	    // 	    return false;
	    // 	if (!sameIndexes((*i1)->indexes, (*i2)->indexes))
	    // 	    return false;
	    // }
	    // return true;
	}




	MemberAccess * MemberAccess::buildSuffix(const MemberAccess *ma) const {
	    // MemberAccess * nma = clone();
	    // nma->setGlobal(false);
	    // const std::vector<Ident*> * frontPath = ma->getAccesses();
	    // for (std::vector<Ident*>::const_iterator i =  frontPath->begin();
	    // 	 i != frontPath->end(); ++i)
	    // 	if (!nma->removeIdFrontNoString((*i)->id, (*i)->indexes)) {
	    // 	    delete(nma);
	    // 	    return NULL;
	    // 	}
	    // nma->doString();
	    // return nma;

	    const std::vector<Ident*> * frontPath = ma->getAccesses();
	    int j = 0;
	    for (std::vector<Ident*>::const_iterator i =  frontPath->begin(); i != frontPath->end(); ++i) {
	    	Ident *ij = path_[j];
	    	if ((!sameIndexes(&ij->indexes, &(*i)->indexes)) || (ij->id.compare((*i)->id)!= 0)) 
	    	    return NULL;
	    	++j;
	    }
	    MemberAccess *nma = new MemberAccess();
	    for (; j<path_.size(); ++j) {
	    	nma->addIdBackNoString(std::string(path_[j]->id),path_[j]->indexes);
	    }
	    nma->doString();
	    return nma;
	}

	
	void MemberAccess::addSimpleCombinationsRec(std::vector<int>* idx,  std::vector<int>* maxidxs,  
						    std::vector<std::vector<int>*>* iidx) {
	    if (maxidxs->size() == 0) {
		iidx->push_back(new std::vector<int>(*idx));
	    } else {
		int m = maxidxs->front();
		maxidxs->erase(maxidxs->begin());
		for (int i = 0; i<m; ++i) {
		    idx->push_back(i);
		    addSimpleCombinationsRec(idx,  maxidxs, iidx);
		    idx->pop_back();
		}	       
	    }		
	}
	
	 std::vector<std::vector<int>*>* MemberAccess::buildSimpleCombinations(std::vector<int>* idxs) {
	    std::vector<int> * idx = new std::vector<int>();
	    std::vector<std::vector<int>*>* iidx = new std::vector<std::vector<int>*>();
	    addSimpleCombinationsRec(idx, idxs, iidx);
	    delete(idx);
	    return iidx;
	}



	std::vector<MemberAccess*> * MemberAccess::buildCombinations() {
	    std::vector<MemberAccess*> * ret = new std::vector<MemberAccess*>();
	    ret->push_back(new MemberAccess());

	    for (std::vector<Ident*>::iterator api = path_.begin(); api!= path_.end(); ++api) {
	     	Ident* ap =  (*api);
		std::vector<std::vector<int>* > * comb = buildSimpleCombinations ( &ap->indexes );
		int ncomb = comb->size();
		int nma = ret->size();
		
		
		for (int i = 1; i<ncomb; ++i) {
		    for (int j = 0; j<nma; ++j) {
			ret->push_back( ((*ret)[j])->clone());
		    } 
		}
		for (int i = 0; i< ncomb; ++i) {
		    for (int j= 0; j<nma; ++j) {
			(*ret)[i*nma+j]->addIdBack(ap->id, *((*comb)[i]));			
		    }		
		    delete (*comb)[i];
		}
		delete comb;
	    }
	    
	    return ret;
	}

	const std::string  MemberAccess::getSymbol() const {
	    return path_[0]->id;
	}


	void MemberAccess::toString_()  {
	    s_.clear();

	    for (std::vector<Ident*>::iterator ii = path_.begin(); ii != path_.end();) {
		s_+=  (*ii)->id ;
		if ((*ii)->indexes.size() != 0)
		    for (std::vector<int>::iterator iii = (*ii)->indexes.begin(); 
			 iii != (*ii)->indexes.end(); ++iii) {
			s_+= "[";
			s_+= (*iii);
			s_+= "]";
		    }
		if (++ii != path_.end())
		    s_+= ".";
	    } 
	}

	void MemberAccess::headToString_()  {
	    h_.clear();
	    if (path_.size() == 0)
		return;
	    h_+=  path_[0]->id ;
	    if (path_[0]->indexes.size() != 0)
		for (std::vector<int>::iterator iii = path_[0]->indexes.begin(); 
		     iii != path_[0]->indexes.end(); ++iii) {
		    h_+= "[";
		    h_+= (*iii);
		    h_+= "]";
		}	    
	}


	MemberAccess::~MemberAccess() {
	    for (std::vector<Ident*>::iterator i = path_.begin();
		 i!= path_.end(); ++i)
		delete(*i);
	    h_.clear();
	    s_.clear();
	}

	void MemberAccess::doString() {
	    toString_();
	    headToString_();
	}
    }
}
