#ifndef XMLRPC_BASE_HPP_INCLUDED
#define XMLRPC_BASE_HPP_INCLUDED

#include <xmlrpc-c/config.h>

#include <climits>
#include <cfloat>
#include <ctime>
#include <vector>
#include <map>
#include <string>
#if XMLRPC_HAVE_TIMEVAL
#include <sys/time.h>
#endif

#include <xmlrpc-c/c_util.h>
#include <xmlrpc-c/base.h>

namespace xmlrpc_c {

class XMLRPC_DLLEXPORT value {
    // This is a handle.  You don't want to create a pointer to this;
    // it is in fact a pointer itself.
public:
    value();
        // This creates a placeholder.  It can't be used for anything, but
        // holds memory.  instantiate() can turn it into a real object.

    value(xmlrpc_c::value const &value);  // copy constructor

    ~value();

    enum type_t {
        // These are designed to be identical to the values for
        // enum xmlrpc_type in the C library.
        TYPE_INT        = 0,
        TYPE_BOOLEAN    = 1,
        TYPE_DOUBLE     = 2,
        TYPE_DATETIME   = 3,
        TYPE_STRING     = 4,
        TYPE_BYTESTRING = 5,
        TYPE_ARRAY      = 6,
        TYPE_STRUCT     = 7,
        TYPE_C_PTR      = 8,
        TYPE_NIL        = 9,
        TYPE_I8         = 10,
        TYPE_DEAD       = 0xDEAD
    };

    type_t type() const;

    xmlrpc_c::value&
    operator=(xmlrpc_c::value const&);

    bool
    isInstantiated() const;

    // The following are not meant to be public to users, but just to
    // other Xmlrpc-c library modules.  If we ever go to a pure C++
    // implementation, not based on C xmlrpc_value objects, this shouldn't
    // be necessary.

    void
    appendToCArray(xmlrpc_value * const arrayP) const;

    void
    addToCStruct(xmlrpc_value * const structP,
                 std::string    const key) const;

    xmlrpc_value *
    cValue() const;
        // Not to be confused with public 'cvalue' method that all the derived
        // classes have.

    value(xmlrpc_value * const valueP);

    void
    instantiate(xmlrpc_value * const valueP);
        // Works only on a placeholder object created by the no-argument
        // constructor.

    xmlrpc_value * cValueP;
        // NULL means this is merely a placeholder object.

protected:
    void
    validateInstantiated() const;
};


std::ostream& operator<<(std::ostream& out,
                         xmlrpc_c::value::type_t const& type);


class XMLRPC_DLLEXPORT value_int : public value {
public:
    value_int(int const cvalue);

    value_int(xmlrpc_c::value const baseValue);

    operator int() const;

    int cvalue() const;
};


class XMLRPC_DLLEXPORT value_boolean : public value {
public:
    value_boolean(bool const cvalue);

    value_boolean(xmlrpc_c::value const baseValue);

    operator bool() const;

    bool cvalue() const;
};


class XMLRPC_DLLEXPORT value_string : public value {
public:
    enum nlCode {nlCode_all, nlCode_lf};

    value_string(std::string const& cppvalue,
                 nlCode      const  nlCode);

    value_string(std::string const& cppvalue);

    value_string(xmlrpc_c::value const baseValue);

    std::string
    crlfValue() const;

    operator std::string() const;

    std::string cvalue() const;
};


class XMLRPC_DLLEXPORT value_double : public value {
public:
    value_double(double const cvalue);

    value_double(xmlrpc_c::value const baseValue);

    operator double() const;

    double cvalue() const;
};


class XMLRPC_DLLEXPORT value_datetime : public value {
public:
    value_datetime(std::string const cvalue);
    value_datetime(time_t const cvalue);
#if XMLRPC_HAVE_TIMEVAL
    value_datetime(struct timeval const& cvalue);
    operator timeval() const;
#endif
#if XMLRPC_HAVE_TIMESPEC
    value_datetime(struct timespec const& cvalue);
    operator timespec() const;
#endif

    value_datetime(xmlrpc_c::value const baseValue);

    operator time_t() const;

    time_t cvalue() const;
};


typedef std::vector<unsigned char> cbytestring;

class XMLRPC_DLLEXPORT value_bytestring : public value {
public:
    value_bytestring(cbytestring const& cvalue);

    value_bytestring(xmlrpc_c::value const baseValue);

    // You can't cast to a vector because the compiler can't tell which
    // constructor to use (complains about ambiguity).  So we have this:
    cbytestring
    vectorUcharValue() const;

    cbytestring cvalue() const;

    size_t
    length() const;
};



typedef std::map<std::string, xmlrpc_c::value> cstruct;

class XMLRPC_DLLEXPORT value_struct : public value {
public:
    value_struct(cstruct const& cvalue);

    value_struct(xmlrpc_c::value const baseValue);

    operator cstruct() const;

    cstruct cvalue() const;
};



typedef std::vector<xmlrpc_c::value> carray;

class XMLRPC_DLLEXPORT value_array : public value {
public:
    value_array(carray const& cvalue);

    value_array(xmlrpc_c::value const baseValue);

    // You can't cast to a vector because the compiler can't tell which
    // constructor to use (complains about ambiguity).  So we have this:
    carray
    vectorValueValue() const;

    carray cvalue() const;

    size_t
    size() const;
};



template<class InputIterator> xmlrpc_c::value_array
arrayValueSlice(InputIterator begin,
                InputIterator end) {
/*----------------------------------------------------------------------------
  convert C++ iterator pair to XML-RPC array
-----------------------------------------------------------------------------*/
    carray ret;
    for (InputIterator p = begin; p != end; ++p) {
        ret.push_back(toValue(*p));
    }
    return xmlrpc_c::value_array(ret);
}

template<class MemberClass> inline xmlrpc_c::value_array
arrayValueArray(const MemberClass * const in,
                size_t              const size) {
/*----------------------------------------------------------------------------
  convert C++ array to XML-RPC array
-----------------------------------------------------------------------------*/
    return arrayValueSlice(in, in + size);
}

class XMLRPC_DLLEXPORT value_nil : public value {
public:
    value_nil();

    value_nil(xmlrpc_c::value const baseValue);

    void * cvalue() const;
};


class XMLRPC_DLLEXPORT value_i8 : public value {
public:
    value_i8(xmlrpc_int64 const cvalue);

    value_i8(xmlrpc_c::value const baseValue);

    operator xmlrpc_int64() const;

    xmlrpc_int64 cvalue() const;
};


inline xmlrpc_c::value_string
toValue(const char * const x) {
    return xmlrpc_c::value_string(x);
}

inline xmlrpc_c::value_string
toValue(std::string const& x) {
    return xmlrpc_c::value_string(x);
}

inline xmlrpc_c::value_int
toValue(int const x) {
    return xmlrpc_c::value_int(x);
}

inline xmlrpc_c::value_boolean
toValue(bool const x) {
    return xmlrpc_c::value_boolean(x);
}

inline xmlrpc_c::value_double
toValue(double const x) {
    return xmlrpc_c::value_double(x);
}

inline xmlrpc_c::value_bytestring
    toValue(cbytestring const& x) {
    return xmlrpc_c::value_bytestring(x);
}

inline const xmlrpc_c::value &
toValue(xmlrpc_c::value const& v) {
/*----------------------------------------------------------------------------
  This does a null conversion; you use it to catch all the XML-RPC types that
  have no usable C++ equivalent, so you can do a toValue() of any XML-RPC
  type at all.  In particular: 'value_datetime', 'value_nil'.
-----------------------------------------------------------------------------*/
    return v;
}

template<class K, class V> xmlrpc_c::value_struct
toValue(std::map<K, V> const& in) {
/*----------------------------------------------------------------------------
  convert C++ map to XML-RPC structure
-----------------------------------------------------------------------------*/
    cstruct ret;
    for (typename std::map<std::string, V>::const_iterator p = in.begin();
         p != in.end();
         ++p) {
        ret[p->first] = toValue(p->second);
    }
    return xmlrpc_c::value_struct(ret);
}

template<class T> inline xmlrpc_c::value_array
toValue(std::vector<T> const& in) {
/*----------------------------------------------------------------------------
  convert C++ vector to XML-RPC array
-----------------------------------------------------------------------------*/
    return arrayValueSlice(in.begin(), in.end());
}

// fromValue() returns via reference argument instead of by return value
// so the compiler can tell which version of it to invoke based on the
// desired output type.

inline void
fromValue(std::string & y, xmlrpc_c::value const& x) {
    y = xmlrpc_c::value_string(x);
}

inline void
fromValue(int & y, xmlrpc_c::value const& x) {
    y = xmlrpc_c::value_int(x);
}

inline void
fromValue(bool & y, xmlrpc_c::value const& x) {
    y = xmlrpc_c::value_boolean(x);
}

inline void
fromValue(double & y, xmlrpc_c::value const& x) {
    y = xmlrpc_c::value_double(x);
}

inline void
fromValue(cbytestring & y, xmlrpc_c::value const& x) {
    y = xmlrpc_c::value_bytestring(x).vectorUcharValue();
}

inline void
fromValue(xmlrpc_c::value & y, xmlrpc_c::value const& x) {
/*----------------------------------------------------------------------------
  This does a null conversion; it's so you can use fromValue() with
  an XML-RPC value or C++ value without having to know which it is.
  One reason you would have an XML-RPC value lying around with C++ values
  is that some XML-RPC values don't have a common C++ equivalent.
-----------------------------------------------------------------------------*/
    y = x;
}

template<class K, class V> inline void
fromValue(std::map<K, V> & y, xmlrpc_c::value const& x) {
/*----------------------------------------------------------------------------
   Convert XML-RPC structure to C++ map.
-----------------------------------------------------------------------------*/
    cstruct m = xmlrpc_c::value_struct(x);
    y.clear();
    for (std::map<std::string, xmlrpc_c::value>::const_iterator p = m.begin();
         p != m.end();
         ++p) {
        fromValue(y[p->first], p->second);
    }
}

template<class T> inline void
fromValue(std::vector<T> & y, xmlrpc_c::value const& x) {
/*----------------------------------------------------------------------------
   Convert XML-RPC array to C++ vector.
-----------------------------------------------------------------------------*/
    carray v = xmlrpc_c::value_array(x).vectorValueValue();
    y.resize(v.size());
    for (unsigned int i = 0; i < v.size(); ++i) {
        fromValue(y[i], v[i]);
    }
}

class XMLRPC_DLLEXPORT fault {
/*----------------------------------------------------------------------------
   This is an XML-RPC fault.

   This object is not intended to be used to represent a fault in the
   execution of XML-RPC client/server software -- just a fault in an
   XML-RPC RPC as described by the XML-RPC spec.

   There is no way to represent "no fault" with this object.  The object is
   meaningful only in the context of some fault.
-----------------------------------------------------------------------------*/
public:
    enum code_t {
        CODE_UNSPECIFIED            =    0,
        CODE_INTERNAL               = -500,
        CODE_TYPE                   = -501,
        CODE_INDEX                  = -502,
        CODE_PARSE                  = -503,
        CODE_NETWORK                = -504,
        CODE_TIMEOUT                = -505,
        CODE_NO_SUCH_METHOD         = -506,
        CODE_REQUEST_REFUSED        = -507,
        CODE_INTROSPECTION_DISABLED = -508,
        CODE_LIMIT_EXCEEDED         = -509,
        CODE_INVALID_UTF8           = -510
    };

    fault();

    fault(std::string             const _faultString,
          xmlrpc_c::fault::code_t const _faultCode 
              = xmlrpc_c::fault::CODE_UNSPECIFIED
        );
    
    xmlrpc_c::fault::code_t getCode() const;

    std::string getDescription() const;

private:
    bool                    valid;
    xmlrpc_c::fault::code_t code;
    std::string             description;
};

class XMLRPC_DLLEXPORT rpcOutcome {
/*----------------------------------------------------------------------------
  The outcome of a validly executed RPC -- either an XML-RPC fault
  or an XML-RPC value of the result.
-----------------------------------------------------------------------------*/
public:
    rpcOutcome();
    rpcOutcome(xmlrpc_c::value const result);
    rpcOutcome(xmlrpc_c::fault const fault);
    bool succeeded() const;
    xmlrpc_c::fault getFault() const;
    xmlrpc_c::value getResult() const;
private:
    bool valid;
        // This is false in a placeholder variable -- i.e. an object you
        // create with the no-argument constructor, which is waiting to be
        // assigned a value.  When false, nothing below is valid.
    bool _succeeded;
    xmlrpc_c::value result;  // valid if 'succeeded'
    xmlrpc_c::fault fault;   // valid if not 'succeeded'
};

class XMLRPC_DLLEXPORT paramList {
/*----------------------------------------------------------------------------
   A parameter list of an XML-RPC call.
-----------------------------------------------------------------------------*/
public:
    paramList(unsigned int const paramCount = 0);

    paramList&
    add(xmlrpc_c::value const param);

    paramList&
    addx(xmlrpc_c::value const param);

    template<class T > paramList& addc(const T & x) {
        xmlrpc_c::paramList::add(toValue(x));
        return *this;
    }

    unsigned int
    size() const;

    xmlrpc_c::value operator[](unsigned int const subscript) const;

    int
    getInt(unsigned int const paramNumber,
           int          const minimum = INT_MIN,
           int          const maximum = INT_MAX) const;

    bool
    getBoolean(unsigned int const paramNumber) const;

    double
    getDouble(unsigned int const paramNumber,
              double       const minimum = -DBL_MAX,
              double       const maximum = DBL_MAX) const;

    enum timeConstraint {TC_ANY, TC_NO_PAST, TC_NO_FUTURE};

    time_t
    getDatetime_sec(unsigned int   const paramNumber,
                    timeConstraint const constraint
                        = paramList::TC_ANY) const;

    std::string
    getString(unsigned int const paramNumber) const;

    cbytestring
    getBytestring(unsigned int const paramNumber) const;

    carray
    getArray(unsigned int const paramNumber,
             unsigned int const minSize = 0,
             unsigned int const maxSize = UINT_MAX) const;

    cstruct
    getStruct(unsigned int const paramNumber) const;

    void
    getNil(unsigned int const paramNumber) const;

    xmlrpc_int64
    getI8(unsigned int const paramNumber,
          xmlrpc_int64 const minimum = XMLRPC_INT64_MIN,
          xmlrpc_int64 const maximum = XMLRPC_INT64_MAX) const;

    void
    verifyEnd(unsigned int const paramNumber) const;

private:
    std::vector<xmlrpc_c::value> paramVector;
};

} // namespace

#endif
