//////////////////////////////////////////////////////////////////////////////
// $Id: MAPITL.h,v 1.1.1.4 2005/01/26 13:42:20 nthiery Exp $
// Name:	MAPITL.h
// Purpose:	Template Library for the
//		MuPAD Application Programming Interface
//		(User interface)
// Author:	Nicolas M. Thiéry <nthiery@mines.edu>
// Created:	2000/08
// Copyright:	LGPL
//////////////////////////////////////////////////////////////////////////////

#ifndef _MAPITL_H
#define _MAPITL_H

#include<string>
#include<vector>
#include<list>
#include<algorithm>
#include<iterator>
#include<iostream>
#include<sstream>

namespace MAPITL
{

  using std::pair;
  using std::string;
  using std::reverse_iterator;
  using std::random_access_iterator_tag;
  using std::input_iterator_tag;
  using std::iterator_traits;
  using std::distance;
}

#include "MAPITL/CtoM.h"
#include "MAPITL/Reference.h"
#include "MAPITL/Iterator.h"
#include "MAPITL/Container.h"

namespace MAPITL {
  // Copy a string content in a newly allocated char *
  // Stroustrup, Chapter 20, Strings, p. 590
  char* c_string(const string &s) {
    char *p = new char[s.length()+1];
    s.copy(p, string::npos);
    p[s.length()]=0; // add terminator
    return p;
  }
  /// error()
  void error(const string &s) {
    MFerror(c_string(s));
  }
  void error(const std::ostringstream &s) {
    error(s.str());
  }

  //////////////////////////////////////////////////////////////////////////////
  // MuPAD cell wrapper
  // Takes care of:
  //  - Memory management (if any)
  //  - Conversions to MTcell (but not from, because constructors are not inherited)
  //  - Cell accessors (those that do not concern the data stored in the cell)
  //////////////////////////////////////////////////////////////////////////////

  /*long
  MMMTheader::getCount() const
  {
    MMMASSERT(this != NULL);
    return count_;
    }*/

  class SimpleCell {
  protected:
    MTcell cell;

  public:
    // Raw constructor without initialization
    SimpleCell() {}

    // Conversions to/from cells
    explicit SimpleCell(MTcell c) : cell(c) {}
    //operator const MTcell() const { return cell; }
    operator MTcell()             { return cell; }
    MTcell* operator &()          { return &cell;}

    // Memory managment to alleviate the work from the user
    // Does not much for the moment
    SimpleCell& release()         { //if (cell!=0) { MFfree(cell); cell=0; }
                                    return *this; }
    SimpleCell& unique()          { MFcopy(cell); MFchange(&cell); return *this; }

    // TODO: assign, operator =
    SimpleCell& sign()            { MFsig(cell); return *this;   }

    // WARNING!
    // All those functions raise a seg fault if compiled with -O1 or more!!!
    long        getRefs()      const { return cell->getRefs();      }
    long        getCount()     const { return cell->getCount();     }
    // Not available in -DMMMDLEAN mode
    //SimpleCell& setCount(long c) { cell->setCount(c); return *this; }
    long        getSignature() const { return cell->getSignature(); }
    long        getSize()      const { return cell->getSize();      }
    //long        getSize()      const { return cell->size_;      }

    // Accessors

    string dump() const {
      std::ostringstream s;
      s << "address: "   << cell << std::endl;
      s << "refcount: "  << getCount()     << std::endl;
      s << "refs: "      << getRefs()      << std::endl;
      s << "signature: " << getSignature() << std::endl;
      s << "size: "      << getSize()      << std::endl;
      s << "pointers: "  << std::endl;
      return s.str();
    }
  };

  //////////////////////////////////////////////////////////////////////////////
  // Standard MuPAD cell wrapper to make it behave as a standard C++ object
  //////////////////////////////////////////////////////////////////////////////

  class Cell: public SimpleCell {
  public:
             Cell(MTcell c) : SimpleCell(c)           {}

    //////////////////////////////////////////////////////////////////////////
    // Conversions from C++ objects

    // Standard problem: there must be a conversion defined for the
    // integer type of size_t on the current architecture

    explicit Cell()                  : SimpleCell(MFcopy(MVnull))      {}
    explicit Cell(bool   b)          : SimpleCell(MFbool(b))           {}
    explicit Cell(int    l)          : SimpleCell(MFint (l))           {}
    explicit Cell(unsigned int l)    : SimpleCell(MFlong(long(l)))     {}
    explicit Cell(long   l)          : SimpleCell(MFlong(l))           {}
    explicit Cell(float  f)          : SimpleCell(MFfloat (f))         {}
    explicit Cell(double f)          : SimpleCell(MFdouble(f))         {}
    explicit Cell(const string s)    : SimpleCell(MFstring((char *)s.c_str())) {}

    // Warning: the following conversions are not lossless!
    explicit Cell(unsigned long l)   : SimpleCell(MFlong(long(l)))     {}
    explicit Cell(long long int l)   : SimpleCell(MFlong((long int)l)) {}

    // We would need to add the following: char, unsigned char, unsigned int,
    // long unsigned int, if we define a default template conversion
    // But this is probably not a good idea anyway
    template<class S, class T>
    explicit Cell(std::pair<S,T> p);

    //////////////////////////////////////////////////////////////////////////
    // Conversions to C++ objects

    template<class T>
    T to	           () const;

    //////////////////////////////////////////////////////////////////////////
    // Container accessors / constructors
    //
    // TODO: merge with the functionnalities of Container<T>
    // by extracting an intermediate class ContainerCell<T>, with this
    // class inheriting from ContainerCell<Cell>

    typedef	  Cell	                     value_type;
    typedef	  long                       size_type;
    typedef	  value_type                 *pointer;
    typedef const value_type                 *const_pointer;
    // Note: reference is a class, for implementing copy-on-write with []
    typedef       Reference<Cell>            reference;
    typedef const reference                  const_reference;

    typedef       Iterator<Cell>             iterator;
    typedef const iterator                   const_iterator;
      //typedef reverse_iterator<const_iterator> const_reverse_iterator;
      //typedef reverse_iterator<iterator>	     reverse_iterator;

    // Standard container accessors

    bool      empty    () const;
    size_type size     () const;
    size_type capacity () const;

    // Iterators & Reverse iterators

    template<class T>
    Iterator<T>       	   begin  ();
    Iterator<Cell>    	   begin  ()       { return begin<Cell>(); }

    template<class T>
    Iterator<T>       	   end    ();
    Iterator<Cell>    	   end    ()       { return end<Cell>(); }

    template<class T>
    const Iterator<T> 	   begin  () const;
    const Iterator<Cell>   begin  () const { return begin<Cell>(); }

    template<class T>
    const Iterator<T> 	   end    () const;
    const Iterator<Cell>   end    () const { return end<Cell>(); }

    template<class T>
    reverse_iterator<Iterator<T> >   	rbegin () { return reverse_iterator<Iterator<T>    >(end  <T>()); }
    reverse_iterator<Iterator<Cell> >	rbegin () { return reverse_iterator<Iterator<Cell> >(end     ()); }

    template<class T>
    reverse_iterator<Iterator<T> >   	rend   () { return reverse_iterator<Iterator<T>    >(begin<T>()); }
    reverse_iterator<Iterator<Cell> >	rend   () { return reverse_iterator<Iterator<Cell> >(begin   ()); }

    template<class T>
    reverse_iterator<const Iterator<T> >    rbegin () const {
	return reverse_iterator<Iterator<T>    >(end  <T>()); }
    reverse_iterator<const Iterator<Cell> > rbegin () const {
	return reverse_iterator<Iterator<Cell> >(end  <Cell>()); }

    template<class T>
    reverse_iterator<const Iterator<T> >    rend   () const {
	return reverse_iterator<Iterator<T>    >(begin<T>   ()); }
    reverse_iterator<const Iterator<Cell> > rend   () const {
	return reverse_iterator<Iterator<Cell> >(begin<Cell>()); }

    // Standard random access operators
    value_type operator [] (size_type) const;
    //template<class T>
    //T operator [] (size_type) const;
    reference  operator [] (size_type);

    template<class T>
    T at	           (size_type  ) const;
    value_type at  	   (size_type i) const { return at<Cell>(i); }
    template<class T>
    Reference<T>    at     (size_type  );
    Reference<Cell> at 	   (size_type i)       { return at<Cell>(i); }

    template<class T>
    Cell&      push_back  (T);

    // Miscellaneous

    void       check       (size_type) const;
    // Throws exception out_of_range if the index is out of range

    size_type size()                           { return MFnops(cell); }
    void resize(size_type s)                   { MFnops(&cell, s); }
    value_type op (size_type i) {
      return value_type(MFop(cell, i));
    }

    template<typename InputIterator>
    Cell(InputIterator first, InputIterator last) :
      SimpleCell(CtoM(first, last)) {}

    template<class InputIterator>
    Cell(InputIterator first, InputIterator last, size_t len) :
      SimpleCell(CtoM(first, last, len)) {}

    template<class T>
    Cell(std::vector<T> v) : SimpleCell(CtoM(v)) {}

    // Maybe there is already something in the STL like
    // makeArray(i.begin(), i.end())
    // If yes, it would be better to just provide "typed" iterators
    template<typename V>
    V* toArray() {
      size_type n = size();
      V* res = new V[n];
      for(int i=0; i<n; i++) {
	res[i] = MtoC<V>(MFgetList(&cell,i));
      }
      return res;
    }
  };


  // Overload CtoM and MtoC
  MTcell CtoM(SimpleCell v) { return MTcell(v); }
  MTcell CtoM(Cell v) { return MTcell(v); }
  // We probably need to repeat MtoC for all subclasses of SimpleCell,
  // unless we define extra down conversions
  SimpleCell MtoC(MTcell c, MAPITL::Type<SimpleCell>) {
    return SimpleCell(c);
  }
  Cell MtoC(MTcell c, MAPITL::Type<Cell>) {
    return Cell(c);
  }

  //////////////////////////////////////////////////////////////////////////////
  // MuPAD Cell representing a domain element with things of type T
  // stored in the data section
  //////////////////////////////////////////////////////////////////////////////

  /* Notes:
     - Hardcoding the domain does not allow for inheritance.
     - Most of the time, this class will be used inside a module
       function. So a domain can possibly be fetched(?) from the
       DOM_EXEC environment.

     So it would be better to get rid of the string template, and ask
     explicitely for the domain in the constructor; other functions
     creating copies should get the domain from their arguments.
  */

  template<typename T, string* DomainName>
  class ThingInCell : public SimpleCell {
  private:
    //static MTcell domain;
  public:

    static MTcell getDomain() {
      //if (domain!=0) return domain;
      MTcell domain = MFeval(MFtext2expr((char *)DomainName->c_str()));
      if (!MFisDomain(domain)) {
	MFerror((*DomainName+" is not a domain").c_str());
      }
      return domain;
    };

    typedef	  T	      value_type;
    typedef	  size_t      size_type;
    typedef	  value_type *pointer;
    typedef const value_type *const_pointer;
    typedef       T	     &reference;
    typedef const value_type &const_reference;

    ThingInCell () { }

    // Explicit conversions from MuPAD standard MTcell's
    // TODO: type checking on the domtype of c
    explicit ThingInCell(MTcell c) : SimpleCell(c)   {}

    // FIXME: is it possible to have those methods return
    // a Bla when inherited from a subclasse Bla?

    ThingInCell& resize(size_type size) { // reserve?
      // Optional argument to fill the new space as in string?
      MFsize(&cell, sizeof(MTRE_cat_type)+size*sizeof(T));
      return *this;
    }

    ThingInCell& initCell(size_type size) { // proper name?
      cell = MFnewExt(getDomain(),0);
      resize(size);
      return *this;
    }

    const_pointer getPointer() const {
      return (T *) MFmemGet(cell, sizeof(MTRE_cat_type));
    }

    pointer getPointer() {
      return (T *) MFmemSet(cell, sizeof(MTRE_cat_type));
    }

    size_type size () { return (MFsize(cell)- sizeof(MTRE_cat_type)) / sizeof(T); };
    bool      empty() { return  MFsize(cell)==sizeof(MTRE_cat_type);              };
  };

  //////////////////////////////////////////////////////////////////////////////
  // Wrapper around static C++ array of objects of type T
  // stored in the data section of a MuPAD Cell
  //////////////////////////////////////////////////////////////////////////////
  template<typename T, string* DomainName>
  class ArrayInCell: public ThingInCell<T, DomainName> {
  public:

    // Required by G++ >= 3.4; see http://gcc.gnu.org/gcc-3.4/changes.html
    using ThingInCell<T, DomainName>::sign;
    using ThingInCell<T, DomainName>::release;
    using ThingInCell<T, DomainName>::initCell;
    using ThingInCell<T, DomainName>::getPointer;
    using ThingInCell<T, DomainName>::size;
    // and all the others ...

    ArrayInCell() { }

    explicit ArrayInCell(MTcell c) : ThingInCell<T, DomainName>(c)   {}
    explicit ArrayInCell(int size) {
      initCell(size);
      sign();
    }

    typedef	  T value_type;
    typedef	  size_t size_type;
    typedef       value_type *iterator;
    typedef const value_type *const_iterator;
    typedef reverse_iterator<const_iterator> const_reverse_iterator;
    typedef reverse_iterator<iterator>	     reverse_iterator;

    //empty, capacity, begin, end, rbegin, rend, and const variations

    value_type op (size_type i) const {
      return *(getPointer()+i-1);
    }

    value_type operator [] (size_type i) { // Doesn't work for some reason
      return *(getPointer()+i-1);
    }

    value_type operator [] (size_type i) const {
      return *(getPointer()+i-1);
    }

    iterator begin () { return getPointer(); };
    iterator end   () { return getPointer()+size(); };

    template<typename InputIterator>
    ArrayInCell(InputIterator first, InputIterator last) {
      init(first, last, distance(first,last));
    }

    template<typename InputIterator>
    ArrayInCell(InputIterator first, InputIterator last, size_type distance) {
      init(first, last, distance);
    }

    template<typename InputIterator>
    ArrayInCell& init(InputIterator first, InputIterator last, size_type distance) {
      release();
      initCell(distance);
      std::uninitialized_copy(first, last, begin());
      sign();
      return *this;
    }

    ArrayInCell& fromExpr (Cell c) {
      release(); // release the former cell
      // Should be a constructor ???
      // Return a ref to myself
      initCell(c.size());
      // FIXME: use a standard STL algorithm
      T* i = begin();
      for (Cell::size_type j=0; j < c.size(); j++) {
	*i = MtoC<T>(MTcell(c.op(j)));
	i++;
      };
      sign();
      return *this;
    }

    Cell expr() {
      return Cell(begin(), end());
    }
  };

  //////////////////////////////////////////////////////////////////////////////
  // Wrapper around a static C++ object of type T
  // stored in the data section of a MuPAD Cell
  //////////////////////////////////////////////////////////////////////////////

  template<typename T, string* DomainName>
  class ObjectInCell : public ThingInCell<T, DomainName> {
    // Required by G++ >= 3.4; see http://gcc.gnu.org/gcc-3.4/changes.html
    using ThingInCell<T, DomainName>::sign;
    using ThingInCell<T, DomainName>::release;
    using ThingInCell<T, DomainName>::initCell;
    using ThingInCell<T, DomainName>::getPointer;
    using ThingInCell<T, DomainName>::size;
  public:

    typedef T              value_type;
    typedef value_type    *pointer;
    typedef const pointer  const_pointer;
    typedef size_t         size_type;

    ObjectInCell() {
      initCell(1);
      sign();
    }

    explicit ObjectInCell(MTcell c) : ThingInCell<T, DomainName>(c)   {}

    explicit ObjectInCell(value_type value) {
      initCell(1);
      *getPointer() = value;
      sign();
    }

    operator value_type ()             { return *getPointer(); }
    // TODO: test those!
          pointer operator -> ()       { return  getPointer(); }
    const_pointer operator -> () const { return  getPointer(); }
  };

} // End of namespace MAPITL, public part

//////////////////////////////////////////////////////////////////////////////
//
//			 End of user interface
//
//////////////////////////////////////////////////////////////////////////////

// Below is the definition of what is inlined for efficiency.
// It ought to be considered implementation detail.

namespace MAPITL {

   template<class RandomAccessIterator>
   MTcell CtoM(RandomAccessIterator first, RandomAccessIterator last,
	       random_access_iterator_tag);

   template<class InputIterator>
   MTcell CtoM(InputIterator first, InputIterator last,
	       input_iterator_tag);

} // End of namespace MAPITL, private part

#endif	/* _MAPITL_H */

// Local Variables: 
// mode: c++
// End:
