/*

* wsman-xml.i
*
* xml structure accessors for openwsman swig bindings
*
*/

/*

* Document-class: XmlNs
* Xml namespace
*
*/

%rename(XmlNs) __WsXmlNs; %nodefault __WsXmlNs; /* part of WsXmlAttr */ struct __WsXmlNs {}; /* without empty struct, the %rename isn’t executed. */ typedef struct __WsXmlNs* WsXmlNsH;

/*

* XmlDoc
*
* Implementation advice
*
* DONT do a %newobject on functions returning WsXmlDoc. Swig will
* free the WsXmlDocH immediately after wrapping !
*
*/

%rename(XmlDoc) _WsXmlDoc; %nodefault _WsXmlDoc; struct _WsXmlDoc {}; typedef struct _WsXmlDoc* WsXmlDocH;

/*

* Document-class: XmlDoc
*
* XmlDoc holds an XML document and thus represents the root of an XML
* tree. XmlDoc is optimized for SOAP type documents, giving accessors
* to the SOAP envelope, header and body.
*
* Instances of the other XML related classes like XmlAttr and XmlNode
* can only be created with an associated XmlDoc instance.
*
* Main properties of the XML document are
* * name of the root element
* * encoding (defaults to _UTF-8_)
*
*/

%extend _WsXmlDoc {

/*
 * Create XmlDoc with node name
 * optionally pass namespace as 2nd arg (defaults to NULL)
 *
 */
_WsXmlDoc(const char *name, const char *ns = NULL) {
  return ws_xml_create_doc(ns, name);
}
/* destructor */
~_WsXmlDoc() {
  ws_xml_destroy_doc( $self );
}
%typemap(newfree) char * "free($1);";

if defined(SWIGRUBY)

%alias string "to_xml";

endif if defined(SWIGPYTHON)

%rename("__str__") string();

endif if defined(SWIGJAVA)

%rename("toString") string();

endif

%newobject string;
/*
 * generic (indented) string representation of the XmlDoc UTF-8 encoded.
 * see encode for setting the encoding.
 *
 * alias: to_xml
 *
 * call-seq:
 *  doc.string -> String
 *  doc.to_xml -> String
 *
 */
char *string() {
  int size;
  char *buf;
  /* force utf-8 encoding since e.g. winrm sends utf-16 */
  ws_xml_dump_memory_node_tree_enc( ws_xml_get_doc_root($self), &buf, &size, "UTF-8" );
  return buf;
}

if defined(SWIGRUBY)

%alias encode "to_s";

endif

%newobject encode;
/*
 * encode document as string with specific encoding
 * (non-indented representation)
 *
 * encoding defaults to 'utf-8'
 *
 * alias: to_s
 *
 * call-seq:
 *  doc.encode -> String
 *  doc.encode("UTF-16") -> String
 *  doc.to_s -> string
 *
 */
char *encode(const char *encoding = "utf-8") {
  int size;
  char *buf;
  ws_xml_dump_memory_enc( $self, &buf, &size, encoding );
  return buf;
}

/*
 * dump document to file
 *
 * call-seq:
 *   doc.dump(IO) -> nil
 *
 */
void dump_file(FILE *fp) {
  ws_xml_dump_doc( fp, $self );
}                           
/*
 * get root node of doc
 * call-seq:
 *  doc.root -> XmlNode
 *
 */
WsXmlNodeH root() {
  return ws_xml_get_doc_root( $self );
}
/*
 * get soap envelope node
 * call-seq:
 *  doc.envelope -> XmlNode
 *
 */
WsXmlNodeH envelope() {
  return ws_xml_get_soap_envelope( $self );
}
/*
 * get soap header node
 * call-seq:
 *  doc.header -> XmlNode
 *
 */
WsXmlNodeH header() {
  return ws_xml_get_soap_header( $self );
}
/*
 * get soap body node
 * call-seq:
 *  doc.body -> XmlNode
 *
 */
WsXmlNodeH body() {
  return ws_xml_get_soap_body( $self );
}
/*
 * get soap element node by name
 * returns nil if no element with the name can be found
 *
 * call-seq:
 *  doc.element(String) -> XmlNode
 *
 */
WsXmlNodeH element(const char *name) {
  return ws_xml_get_soap_element( $self, name );
}
%newobject context;
/*
 * get enumeration context as string
 * return nil if context not present or empty
 *
 * call-seq:
 *  doc.context -> String
 *
 */
const char *context() {
  char *c = wsmc_get_enum_context( $self );
  if (c) {
    if (*c)
      return c;
    u_free(c);
  }
  return NULL;
}
/*
 * Generate fault document based on given status
 *
 * This creates a new XmlDoc instance representing a fault
 *
 * call-seq:
 *  doc.generate_fault(Openwsman::Status) -> XmlDoc
 *
 */
WsXmlDocH generate_fault(WsmanStatus *s) {
  return wsman_generate_fault( $self, s->fault_code, s->fault_detail_code, s->fault_msg);
}

if defined(SWIGRUBY)

%rename("fault?") is_fault();
%typemap(out) int is_fault
  "$result = ($1 != 0) ? Qtrue : Qfalse;";

endif if defined(SWIGJAVA)

%rename("isFault") is_fault();
%typemap(jstype) int is_fault "boolean"
%typemap(javaout) int is_fault {
        return ( $jnicall != 0 ) ? true : false;
}

endif

/*
 * Check if document represents a fault
 *
 * call-seq:
 *  doc.fault?(XmlDoc) -> Boolean
 *
 */
int is_fault() {
  return wsmc_check_for_fault( $self );
}

%newobject fault;
/*
 * retrieve fault data
 *
 * call-seq:
 *  doc.fault(XmlDoc) -> Openwsman::Fault
 *  doc.fault(XmlDoc) -> nil # if XmlDoc is not a fault
 *
 */
WsManFault *fault() {
  WsManFault *f = NULL;
  if (wsmc_check_for_fault($self)) {
    f = (WsManFault *)calloc(1, sizeof(WsManFault));
    wsmc_get_fault_data($self, f);
  }
  return f;
}

/*
 * Generate response envelope document, optionally relating to a
 * specific action.
 *
 * This creates a new XmlDoc instance representing a response.
 *
 * call-seq:
 *  doc.create_response_envelope(String action) -> XmlDoc
 *
 */
WsXmlDocH create_response_envelope(const char *action = NULL) {
  return wsman_create_response_envelope($self, action);
}

if defined(SWIGRUBY)

%rename("end_of_sequence?") is_end_of_sequence();
%typemap(out) int is_end_of_sequence
  "$result = ($1 != 0) ? Qtrue : Qfalse;";

endif

/*
 * Check if document represents an end of sequence (last enumeration item)
 *
 * call-seq:
 *  doc.is_end_of_sequence() -> Boolean
 *
 */
int is_end_of_sequence() {
  return NULL != ws_xml_find_in_tree( ws_xml_get_soap_body( $self ), XML_NS_ENUMERATION, WSENUM_END_OF_SEQUENCE, 1 );
}

}

/*

* Document-class: XmlNode
*
* XmlNode is a node inside the XML document tree.
* 
* A node has
* * a name
* * a namespace (optional)
* * attributes
* * text (optional)
* * a parent
* * a document (root)
* * children (empty for tail nodes)
*
*/

%rename(XmlNode) __WsXmlNode; %nodefault __WsXmlNode; struct __WsXmlNode {}; /* without empty struct, the %rename isn’t executed. */ typedef struct __WsXmlNode* WsXmlNodeH;

%extend __WsXmlNode {

~__WsXmlNode() {
  ws_xml_unlink_node($self);
}

if defined(SWIGRUBY)

%alias text "to_s";
%alias string "to_xml";

endif if defined(SWIGPYTHON)

%rename("__str__") text();

endif if defined(SWIGJAVA)

%rename("toString") text();
%rename("toXML") string();

endif

%newobject string;
/*
 * dump node as XML string
 *
 * alias: to_xml
 *
 * call-seq:
 *  node.string(XmlNode) -> String
 *
 */
char *string() {
  int size;
  char *buf;
  ws_xml_dump_memory_node_tree( $self, &buf, &size );
  return buf;
}

/*
 * dump node to file
 *
 * call-seq:
 *  node.dump_file(IO) -> nil
 *
 */
void dump_file(FILE *fp) {
  ws_xml_dump_node_tree( fp, $self );
}

if defined(SWIGRUBY)

%alias equal "==";
%typemap(out) int equal
  "$result = ($1 != 0) ? Qtrue : Qfalse;";

endif if defined(SWIGPERL)

int __eq__( WsXmlNodeH n )

else

int equal( WsXmlNodeH n )

endif

/*
 * Test for identity (same object)
 *
 * call-seq:
 *  XmlNode == XmlNode -> Boolean
 *
 */
{ return $self == n; }          

/*
 * get text (without xml tags) of node
 *
 * alias: to_s
 *
 * call-seq:
 *  node.text() -> String
 *
 */
char *text() {
  return ws_xml_get_node_text( $self );
}

if defined(SWIGRUBY)

%rename( "text=" ) set_text( const char *text );

endif

/*
 * Set text of node
 *
 * call-seq:
 *  node.text = String
 *
 */
void set_text( const char *text ) {
  ws_xml_set_node_text( $self, text );
}

/*
 * get XmlDoc to which node belongs
 *
 * call-seq:
 *  node.doc -> XmlDoc
 *
 */
WsXmlDocH doc() {
  return ws_xml_get_node_doc( $self );
}

/*
 * get parent for node
 *
 * call-seq:
 *  node.parent -> XmlNode
 *
 */
WsXmlNodeH parent() {
  return ws_xml_get_node_parent( $self );
}

if defined(SWIGRUBY)

%alias child "first";

endif

/*
 * get first child of node
 *
 * call-seq:
 *  node.child -> XmlNode
 *
 */
WsXmlNodeH child() {
  return xml_parser_get_first_child($self);
}

/*
 * get name for node
 *
 * call-seq:
 *  node.name -> String
 *
 */
char *name() {
  return ws_xml_get_node_local_name( $self );
}

if defined(SWIGRUBY)

%rename("name=") set_name( const char *name);

endif

/*
 * set name of node
 *
 * call-seq:
 *  node.name = String
 *
 */
void set_name( const char *name ) {
  ws_xml_set_node_name( $self, ws_xml_get_node_name_ns( $self ), name );
}

/*
 * get namespace for node
 *
 * call-seq:
 *  node.ns -> String
 *
 */
char *ns() {
  return ws_xml_get_node_name_ns( $self );
}

if defined(SWIGRUBY)

%rename("ns=") set_ns( const char *nsuri );

endif

/*
 * set namespace of node
 *
 * call-seq:
 *  node.ns = String
 *
 */
void set_ns( const char *ns ) {
  ws_xml_set_ns( $self, ns, ws_xml_get_node_name_ns_prefix($self) );
}

/*
 * get prefix of nodes namespace
 *
 * call-seq:
 *  node.prefix -> String
 *
 */
const char *prefix() {
  return ws_xml_get_node_name_ns_prefix($self);
}

if defined(SWIGRUBY)

%rename("lang=") set_lang(const char *lang);

endif

/*
 * set language
 *
 * call-seq:
 *  node.lang = String
 *
 */
void set_lang(const char *lang) {
  ws_xml_set_node_lang($self, lang);
}

/*
 * find node within tree
 * a NULL passed as 'ns' (namespace) is treated as wildcard
 *
 * call-seq:
 *  node.find("namespace", "name") -> XmlNode # recursive
 *  node.find("namespace", "name", 0) -> XmlNode # non-recursive
 *
 */
WsXmlNodeH find( const char *ns, const char *name, int recursive = 1) {
  return ws_xml_find_in_tree( $self, ns, name, recursive );
}

/*
 * iterate over siblings
 *
 * finds next sibling with same namespace and name
 *
 * See also XmlNode#each
 *
 * XmlNode#each iterates over children, XmlNode#next over siblings
 *
 * Example:
 *    <Foo>
 *      <Bar>...
 *      <Bar>...
 *      <Bar>...
 *      <Bar>...
 *      <Other>...
 *      <Other>...
 *    </Foo>
 *
 * node = root.Foo # points to <Foo> node
 *
 *   bar = node.Bar
 *   while bar do
 *     bar = bar.next
 *   end
 *
 * will give you four iterations (all <Bar> nodes)
 *
 *   child = node.Bar
 *   while child do
 *     child = child.next(1)
 *   end
 *
 * will give you six iterations (all children of <Foo>)
 * The latter example is equal to
 *
 *   node.each do |child|
 *     ...
 *   end
 *
 */
WsXmlNodeH next(int all = 0) {
  WsXmlNodeH next_node = xml_parser_get_next_child($self);
  if (next_node && !all) {
    const char *ns_uri = ws_xml_get_node_name_ns($self);
    const char *name = ws_xml_get_node_local_name($self);
    if (ws_xml_is_node_qname(next_node, ns_uri, name) == 0) {
      next_node = NULL;
    }
  }
  return next_node;
}

/*
 * count node children
 * if name given, count children with this name
 * if name + ns given, count children with this namespace and name
 *
 */
int size(const char *name = NULL, const char *ns = NULL) {
  return ws_xml_get_child_count_by_qname($self, ns, name);
}

/*
 * add child (namespace, name, text) to node
 *
 */
WsXmlNodeH add( const char *ns, const char *name, const char *text = NULL ) {
  return ws_xml_add_child( $self, ns, name, text );
}

/*
 * add child (namespace, name, text) before(!) node
 *
 */
WsXmlNodeH add_before( const char *ns, const char *name, const char *text = NULL ) {
  return ws_xml_add_prev_sibling( $self, ns, name, text );
}

if defined(SWIGRUBY)

%alias add "<<";

endif

/*
 * add node as child
 *
 */
WsXmlNodeH add(WsXmlNodeH node) {
  ws_xml_duplicate_tree( $self, node );
  return $self;
}

if defined(SWIGRUBY)

/*
 * iterate over children
 *
 * See also XmlNode#next
 *
 * XmlNode#each iterates over children, XmlNode#next over siblings
 *
 * can be limited to children with specific name (and specific namespace)
 *
 * for array-like constructs, e.g
 *  <Parent>
 *    <Child>..
 *    <Child>..
 *    <Child>..
 *    <OtherChild>..
 *    <OtherChild>..
 *    <OtherChild>..
 *
 *   doc.Parent.each do |child|
 *     ... iterates over all 6 children ...
 *   end
 *
 * use XmlNode#next as in
 *   node = doc.OtherChild
 *   while node do
 *     ... do something with node ...
 *    node = node.next
 *   end
 *
 * call-seq:
 *  node.each { |XmlNode| ... }
 *  node.each("name") { |XmlNode| ... }
 *  node.each("name", "namespace") { |XmlNode| ... }
 *
 */

void each(const char *name = NULL, const char *ns = NULL) {
  int i = 0;
  WsXmlNodeH node = $self;
  int count = ws_xml_get_child_count_by_qname( node, ns, name );
  while ( i < count ) {
    rb_yield( SWIG_NewPointerObj((void*) ws_xml_get_child(node, i, ns, name), SWIGTYPE_p___WsXmlNode, 0));
    ++i;
  }
}

endif

if defined(SWIGPYTHON)

/*
 * iterate over children
 *
 */
%pythoncode %{
  def __iter__(self):
    r = range(0,self.size())
    while r:
      yield self.get(r.pop(0))
%}

endif

if defined(SWIGRUBY)

%alias get "[]";

endif

/*
 * get child by index
 *
 * call-seq:
 *  node.get(42) -> XmlNode
 *  node.get(42, "name") -> XmlNode
 *  node.get(42, "name", "namespace") -> XmlNode
 *
 */
WsXmlNodeH get(int i, const char *name = NULL, const char *ns = NULL) {
  if (i < 0 || i >= ws_xml_get_child_count_by_qname($self,ns,name))
    return NULL;
  return ws_xml_get_child($self, i, ns, name);
}

/*
 * get first child by name (and namespace)
 *
 * call-seq:
 *  node.get("name") -> XmlNode
 *  node.get("name", "namespace") -> XmlNode
 *
 */
WsXmlNodeH get(const char *name, const char *ns = NULL) {
  return ws_xml_get_child($self, 0, ns, name);
}

if defined(SWIGRUBY)

/*
 * get node attribute by index or name
 *
 * call-seq:
 *  node.attr(1) -> XmlAttr
 *  node.attr("name") -> XmlAttr
 *  node.attr("name", "namespace") -> XmlAttr
 *
 */
WsXmlAttrH attr(VALUE index = Qnil, VALUE namespace = Qnil) {
  if (NIL_P(index)) { /* nil */
    return ws_xml_get_node_attr( $self, 0 );
  } else if (FIXNUM_P(index)) { /* numeric */
    return ws_xml_get_node_attr( $self, FIX2INT(index) );      
  } else { /* convert to string */
    const char *ns = NULL;
    const char *name = as_string(index);
    if (!NIL_P(namespace)) {
      ns = as_string(namespace);
    }
    return ws_xml_find_node_attr( $self, ns, name );
  }
}

else

/* get node attribute */
WsXmlAttrH attr(int index = 0) {
  return ws_xml_get_node_attr( $self, index );
}

endif

/*
 * count node attribute
 *
 * call-seq:
 *  node.attr_count -> Integer
 *
 */
int attr_count() {
  return ws_xml_get_node_attr_count( $self );
}
/*
 * find node attribute by name
 *
 * call-seq:
 *  node.attr_find("namespace", "name") -> XmlAttr
 *
 */
WsXmlAttrH attr_find( const char *ns, const char *name ) {
  return ws_xml_find_node_attr( $self, ns, name );
}
/*
 * add attribute to node
 *
 * call-seq:
 *  node.attr_add("namespace", "name", "value") -> XmlAttr
 *
 */
WsXmlAttrH attr_add( const char *ns, const char *name, const char *value ) {
  return ws_xml_add_node_attr( $self, ns, name, value );
}
/*
 * get end point reference
 *
 * call-seq:
 *  node.epr("namespace", "epr_node_name", Integer embedded) -> EndPointReference
 *
 */
epr_t *epr( const char *ns, const char *epr_node_name, int embedded) {
  return epr_deserialize($self, ns, epr_node_name, embedded);
}

if defined(SWIGRUBY)

/*
 * enumerate attributes
 *
 * call-seq:
 *   node.each_attr { |XmlAttr| ... }
 *
 */
void each_attr() {
  int i = 0;
  while ( i < ws_xml_get_node_attr_count( $self ) ) {
    rb_yield( SWIG_NewPointerObj((void*) ws_xml_get_node_attr($self, i), SWIGTYPE_p___WsXmlAttr, 0));
    ++i;
  }
}

endif }

/*

* Document-class: XmlAttr
*
* An XmlAttr is a key/value pair representing an attribute of a node.
*
* An attribute has
* * a name (the key)
* * a namespace (optional)
* * a value
*
* There is no standalone constructor available for XmlAttr, use
* XmlNode.add_attr() to create a new attribute.
*
*/

%rename(XmlAttr) __WsXmlAttr; %nodefault __WsXmlAttr; /* part of WsXmlNode */ struct __WsXmlAttr {}; /* without empty struct, the %rename isn’t executed. */ typedef struct __WsXmlAttr* WsXmlAttrH;

%extend __WsXmlAttr { if defined(SWIGRUBY)

%alias value "to_s";

endif

/*
 * get name for attr
 *
 * call-seq:
 *   attr.name -> String
 *
 */
char *name() {
  return ws_xml_get_attr_name( $self );
}
/*
 * get namespace for attr
 *
 * call-seq:
 *   attr.ns -> String
 *
 */
char *ns() {
  return ws_xml_get_attr_ns( $self );
}
/*
 * get value for attr
 *
 * call-seq:
 *   attr.value -> String
 *
 */
char *value() {
  return ws_xml_get_attr_value( $self );
}
/*
 * remove note attribute
 *
 * call-seq:
 *   attr.remove -> nil
 *
 */
void remove() {
  ws_xml_remove_node_attr( $self );
}

}