/* guile-xapian --- Guile bindings for Xapian
 * Copyright © 2020, 2023–2024 Arun Isaac <arunisaac@systemreboot.net>
 * Copyright © 2021, 2022 Bob131 <bob@bob131.so>
 *
 * This file is part of guile-xapian.
 *
 * guile-xapian is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 2 of the
 * License, or (at your option) any later version.
 *
 * guile-xapian is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with guile-xapian.  If not, see
 * <https://www.gnu.org/licenses/>.
 */

%module wrap
%{
%}

%scheme
%{
;; Try loading the shared library by absolute path. If that fails, try
;; searching for it using LD_LIBRARY_PATH and other mechanisms. We do this
;; so that guile-xapian finds libguilexapian both before and after installation.
(catch #t
  (lambda ()
    (load-extension "@libdir@/libguilexapian-@GUILE_EFFECTIVE_VERSION@" "SWIG_init"))
  (lambda _
    (load-extension "libguilexapian-@GUILE_EFFECTIVE_VERSION@" "SWIG_init")))
%}

%{
  #include <string>

  // We introduce a dummy typedef for std::string without declaring it
  // to SWIG so we can establish a new SWIG typemap applying only to
  // certain redeclarations of existing C++ functions taking or
  // returning strings.
  typedef std::string SWIG_bytevector;

  static inline void typemap_in_SWIG_bytevector (SWIG_bytevector& out, SCM in) {
    out.assign ((char*) SCM_BYTEVECTOR_CONTENTS (in),
                SCM_BYTEVECTOR_LENGTH (in));
  }

  static inline SCM typemap_out_SWIG_bytevector (const SWIG_bytevector& in) {
    SCM result = scm_c_make_bytevector (in.size ());
    memcpy (SCM_BYTEVECTOR_CONTENTS (result), in.data (), in.size ());
    return result;
  }
%}

%typemap(in) SWIG_bytevector {
  if (scm_is_bytevector ($input)) {
    $1 = std::string ();
    typemap_in_SWIG_bytevector ($1, $input);
  } else {
    %argument_fail (SWIG_TypeError, "$type", $symname, $argnum);
  }
}

%typemap(in) SWIG_bytevector&, SWIG_bytevector* {
  if (scm_is_bytevector ($input)) {
    $1 = new std::string ();
    typemap_in_SWIG_bytevector (*$1, $input);
  } else {
    %argument_fail (SWIG_TypeError, "$type", $symname, $argnum);
  }
}

%typemap(out) SWIG_bytevector {
  $result = typemap_out_SWIG_bytevector ($1);
}

%typemap(out) SWIG_bytevector&, SWIG_bytevector* {
  $result = typemap_out_SWIG_bytevector (*$1);
}

%typecheck(SWIG_TYPECHECK_SWIGOBJECT)
  SWIG_bytevector, SWIG_bytevector&, SWIG_bytevector*
{
  $1 = scm_is_bytevector ($input);
}

%include xapian-head.i
%include except.i
%include xapian-headers.i

%extend Xapian::ValueIterator {
  SWIG_bytevector get_value_bytes () {
    return **$self;
  }
}

%extend Xapian::Document {
  SWIG_bytevector get_value_bytes (Xapian::valueno valueno) {
    return $self->get_value (valueno);
  }

  void add_value_bytes (Xapian::valueno valueno, const SWIG_bytevector& bytes) {
    $self->add_value (valueno, bytes);
  }

  SWIG_bytevector get_data_bytes () {
    return $self->get_data ();
  }

  void set_data_bytes (const SWIG_bytevector& bytes) {
    $self->set_data (bytes);
  }
}

%extend Xapian::Database {
  SWIG_bytevector get_metadata_bytes (const std::string& key) {
    return $self->get_metadata (key);
  }
}

%extend Xapian::WritableDatabase {
  void set_metadata_bytes (const std::string& key,
                           const SWIG_bytevector& bytes)
  {
    $self->set_metadata (key, bytes);
  }
}

%extend Xapian::Query {
  Query (op op_, Xapian::valueno slot, const SWIG_bytevector& range_limit) {
    return new Xapian::Query (op_, slot, range_limit);
  }

  Query (op op_, Xapian::valueno slot,
         const SWIG_bytevector& range_lower, const SWIG_bytevector& range_upper)
  {
    return new Xapian::Query (op_, slot, range_lower, range_upper);
  }
}

// Child class of Xapian::RangeProcessor that calls back to a
// user-specified Scheme procedure to process fields.
%{
  class GuileXapianRangeProcessorWrapper
    : public Xapian::RangeProcessor {
  private:
    SCM proc;
  public:
    GuileXapianRangeProcessorWrapper(Xapian::valueno slot, const std::string &str, unsigned flags, SCM _proc)
      : Xapian::RangeProcessor(slot, str, flags) {
      proc = _proc;
    }
    Xapian::Query operator()(const std::string &begin, const std::string &end) {
      void *ptr;
      int res = SWIG_ConvertPtr(scm_call_2(proc,
                                           begin.empty() ? SCM_BOOL_F : scm_from_utf8_string(begin.c_str()),
                                           end.empty() ? SCM_BOOL_F : scm_from_utf8_string(end.c_str())),
                                &ptr,
                                SWIGTYPE_p_Xapian__Query,
                                0);
      return *((Xapian::Query*)ptr);
    }
  };
%}

class GuileXapianRangeProcessorWrapper
: public Xapian::RangeProcessor
{
 public:
  GuileXapianRangeProcessorWrapper(Xapian::valueno, std::string const&, unsigned, SCM);
  ~GuileXapianRangeProcessorWrapper();
  Xapian::Query operator()(std::string const&, std::string const&);
};

// Child class of Xapian::FieldProcessor that calls back to a
// user-specified Scheme procedure to process fields.
%{
  class GuileXapianFieldProcessorWrapper
    : public Xapian::FieldProcessor {
  private:
    SCM proc;
  public:
    GuileXapianFieldProcessorWrapper(SCM _proc) {
      proc = _proc;
    }
    Xapian::Query operator()(const std::string &str) {
      void *ptr;
      int res = SWIG_ConvertPtr(scm_call_1(proc, scm_from_utf8_string(str.c_str())),
                                &ptr,
                                SWIGTYPE_p_Xapian__Query,
                                0);
      return *((Xapian::Query*)ptr);
    }
  };
%}

class GuileXapianFieldProcessorWrapper
: public Xapian::FieldProcessor
{
 public:
  GuileXapianFieldProcessorWrapper(SCM);
  ~GuileXapianFieldProcessorWrapper();
  Xapian::Query operator()(std::string const&);
};
