#ifndef XM_FCINTERFACE_H_

/*
 * $Id: fcinterface_371.h,v 1.2 2005/10/24 03:57:58 jxc Exp $
 *
 * Utilities for converting C/FORTRAN strings
 *
 * Author: DTJ	 5/ 1/92
 *
 */

/* Definitions for the conversion types */

#define F2C_FLAG_LOWERCASE 0x0001
#define F2C_FLAG_TRIM      0x0002
#define F2C_FLAG_APPEND_   0x0004

#define F2C_UNIXFILE	   F2C_FLAG_LOWERCASE | F2C_FLAG_TRIM
#define F2C_TRIM	   F2C_FLAG_TRIM
#define F2C_SUBROUTINE     F2C_FLAG_LOWERCASE | F2C_FLAG_TRIM | \
						F2C_FLAG_APPEND_
#define F2C_LITERAL	   0x0000

#define C2F_FLAG_UPPERCASE 0x0001
#define C2F_FLAG_FILL      0x0002
#define C2F_FILL	   C2F_FLAG_FILL
#define C2F_CLEAN	   C2F_FLAG_FILL | C2F_FLAG_UPPERCASE

#ifdef __cplusplus
extern "C" {
#endif

char *strf2c(const char *fstr, int flen, char *cstr, int clen, int flags);
char *strc2f(const char *cstr, int clen, char *fstr, int flen, int flags);

#ifdef __cplusplus
}

/* NOTE: The includer must already have a C++ class named "string"
   declared with the interface of std::string.  For std::string this
   would look like:
  
   #include <string>
   using namespace std;
*/
#include <string.h>	/* for memset() and memcpy() */

inline string strf2cpp (const char* fstr, string::size_type fcapacity)
{
  int ii = fcapacity;
  while (--ii >= 0 && fstr[ii] <= ' ');
  return string(fstr, ii + 1);
}

inline char *strcpp2f (const string& cppstr, char *fstr, int flen,
		       int flags = C2F_FLAG_FILL)
{
  return strc2f(cppstr.c_str(), cppstr.length(), fstr, flen, flags);
}

#if _VMS
/* On VMS strings are passed in a special structure which we 
   extend here adding constructors to fill it out.
*/
#include <descrip.h>
struct VAXstring : public dsc$descriptor {
  /* Constructors - CAUTION the memory associated with the input
     strings must outlive the VAXstring instance or the VAXstring will
     have an invalid internal pointer value.  Note that for string
     instances the pointer is only valid until a non-const operation
     is performed on the string.
  */
  /* This is the read/write signature */
  inline VAXstring (char* buf, string::size_type len)
  { init_(buf, len); }

  /* The following two constructors are for const arguments, but have
     to have the const-ness of their char* cast away for the
     underlying descriptor structure.  We're trusting the calling
     function to not change the underlying character buffer.
  */
  inline VAXstring (const string& str)
  { init_(const_cast<char*>(str.c_str()), str.length()); }
  inline VAXstring (const char& c)
  { init_(const_cast<char*>(&c), 1); }

  /* Inspectors */
  inline char* buf ()
  { return dsc$a_pointer; }
  inline const char* buf () const
  { return dsc$a_pointer; }
  inline string::size_type capacity () const
  { return dsc$w_length; }

  /* Outcast to string() */
  inline operator string () const
  { return strf2cpp(buf(), capacity()); }

  private:
  inline void init_ (char* ptr, int len)
  {
    dsc$a_pointer = ptr;
    dsc$w_length  = len;
    dsc$b_dtype   = DSC$K_DTYPE_T;
    dsc$b_class   = DSC$K_CLASS_S;
  }
}; /* VAXstring */
#endif /* _VMS */

/* A class to allow fortran strings as output parameters.  It will
   either wrap a string& or can be constructed from an fstring<>
   defined below.  This allows FORTRAN strings in structures to be
   passed all places where an output string is required.
*/
class fstring_buf {
  public:
    string::size_type nchar;
    char* buf;

    /* The following constructor is used by fstring<>'s
       to outcast to fstring_buf.
    */
    fstring_buf (char* cbuf, string::size_type bufsize) :
      nchar(bufsize),
      buf(cbuf),
      str_(0)
    { }

    /* The following constructor will take a string& and
       create an fstring buffer equivalent for the underlying
       wrapper.
    */
    fstring_buf (string& s, string::size_type bufsize = 0) :
      nchar(bufsize),
      buf(0),
      str_(&s)
    { if (bufsize) allocate(bufsize); }

    ~fstring_buf ()
    { if (str_ && buf) { *str_ = strf2cpp(buf, nchar); delete[] buf; } }


    fstring_buf& operator= (const char* c_str)
    {
      int slen = strlen(c_str);
      if (!buf) allocate(slen);
      strc2f(c_str, slen, buf, nchar, C2F_FILL);
      return *this;
    }

    fstring_buf& operator= (char c)
    {
      if (!buf) allocate(1);
      strc2f(&c, 1, buf, nchar, C2F_FILL);
      return *this;
    }

#if _VMS
    inline operator VAXstring ()
    { return VAXstring(buf, nchar); }
#endif

    /* When wrapping a string&, provide a max buf-length if one
       was not provided.  If this is NOT called buf will be NULL.
    */
    void allocate (string::size_type bufsize)
    {
      if (str_ && !buf) {
	buf = new char[nchar = bufsize];
        strcpp2f(*str_, buf, nchar);
      }
    }

  private:

    string* str_;
};


/* A class to wrap a raw char buffer with methods so that it behaves
   like a FORTRAN string.  Note that the size of this class is the
   size of the char buffer it contains and can thus take the place of
   a raw char buffer in a struct without adding any overhead.  This is
   how the X-Midas libraries share char buffers in structures between
   FORTRAN and C++.
  
   FORTRAN: character*80 name
         C: char name[80];
       C++: fstring<80> name;
  
   Note: according to the standard (sec 10.4.12) a class with a
   constructor, destructor or copy operation cannot be the type of a
   union member.  We purposely do NOT define those member functions
   for this class so that it CAN be part of a union or part of a
   structure that is part of a union.
*/
template <string::size_type nchar> struct fstring {

  char buf[nchar];

  /* Methods */

  inline string substr (string::size_type start = 0,
			string::size_type len = string::npos) const
  { return str().substr(start, len); }

  inline string str () const { return string(buf, length()); }
  inline const char* data () const { return buf; }

  inline string::size_type length () const
  { int i = nchar; while (--i >= 0 && buf[i] <= ' '); return i + 1; }

  inline bool empty () const
  { int i = 0; while (i < nchar && buf[i] <= ' ') ++i; return i == nchar; }

  inline string::size_type capacity () const { return nchar; }
  inline string::size_type max_size () const { return nchar; }

  inline void clear () { memset(buf, ' ', nchar); }

  inline fstring<nchar>& erase (string::size_type pos = 0,
				string::size_type num_chars = string::npos)
  {
    if (num_chars < nchar) {
      string s(str());
      *this = s.erase(pos, num_chars);
    } else if (pos < nchar) {
      memset(buf + pos, ' ', nchar - pos);
    }
    return *this;
  }

  inline fstring<nchar>& insert (string::size_type pos, const char* s)
  { string temp = str(); return (*this = temp.insert(pos, s)); }

  inline fstring<nchar>& insert (string::size_type pos, const string& s)
  { string temp = str(); return (*this = temp.insert(pos, s)); }

  /* For the STL */

  inline string::size_type size () const { return length(); }

  inline char* begin () { return buf; }
  inline char* end () { return buf + size(); }

  inline const char* begin () const { return buf; }
  inline const char* end () const { return buf + size(); }


  /* Operators */

  inline bool operator== (const char* s) const
  { string::size_type len = strlen(s);
    return len == length() && !strncmp(buf, s, len); }

  inline bool operator== (const string& s) const
  { return s.length() == length() && !strncmp(buf, s.c_str(), s.length()); }

  inline bool operator== (const fstring<nchar>& fstr) const
  { string::size_type len = length();
    return len == fstr.length() && !strncmp(buf, fstr.buf, len); }

  inline bool operator!= (const char* s) const
  { return !(*this == s); }

  inline bool operator!= (const string& s) const
  { return !(*this == s); }

  inline bool operator!= (const fstring<nchar>& fstr) const
  { return !(*this == fstr); }

  inline fstring& operator= (const char* s)
  { strc2f(s, strlen(s), buf, nchar, C2F_FLAG_FILL); return *this;  }

  inline fstring& operator= (const string& s)
  { strc2f(s.c_str(), s.length(), buf, nchar, C2F_FLAG_FILL); return *this;}

  inline fstring& operator+= (const char* c2)
  { return *this = str() + c2; }

  inline fstring& operator+= (const string& s)
  { return *this = str() + s; }

  inline operator string () const
  { return str(); }

  inline operator fstring_buf ()
  { return fstring_buf(buf, nchar); }

#if _VMS
  inline operator VAXstring ()
  { return VAXstring(buf, nchar); }
#endif

  inline char operator[] (int subscript) const
  { return buf[subscript]; }

  inline char& operator[] (int subscript)
  { return buf[subscript]; }

};

template <string::size_type nchar>
inline ostream& operator<< (ostream& out, const fstring<nchar>& fin)
{ out << fin.str(); return out; }

template <string::size_type nchar>
inline string operator+ (const fstring<nchar>& s1, const string& s2)
{ return s1.str() + s2; }

template <string::size_type nchar>
inline string operator+ (const string& s1, const fstring<nchar>& s2)
{ return s1 + s2.str(); }

#define FSTRING(NAME, LEN) fstring<LEN> NAME
#define FSTRING_ARRAY(NAME, LEN, ELEMS) fstring<LEN> NAME[ELEMS]

#else

#define FSTRING(NAME, LEN) char NAME[LEN]
#define FSTRING_ARRAY(NAME, LEN, ELEMS) char NAME[ELEMS][LEN]

#endif /* __cplusplus */

#define XM_FCINTERFACE_H_
#endif /* XM_FCINTERFACE_H_ */
