/*
 * This program 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.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 */
%define DOCSTRING
"SWIG wrapper and helper around libavrdude

The module defines all things necessary to perform the
same operations as the CLI AVRDUDE program, and exports
it to Python.

Invoke like

import swig_avrdude as ad

The following global variables are available in `ad.cvar`:

`ad.cvar.version`  - str, read-only
`ad.cvar.progname` - str, for messages
`ad.cvar.verbose`  - int, message verbosity (0 ... 5)
`ad.cvar.quell_progress` - int, message supression (0 ... 2)
`ad.cvar.ovsigck`  - int, override signature and some other checks (0 ... 1)"
%enddef
%module (docstring=DOCSTRING) swig_avrdude
%feature("autodoc", "1");
%{
#include <ac_cfg.h>
#include "libavrdude.h"

// global variables referenced by library
char * version  = AVRDUDE_FULL_VERSION;
char * progname = "avrdude";
int verbose;
int quell_progress;
int ovsigck;
const char *partdesc = "";
const char *pgmid = "";
libavrdude_context *cx;

static PyObject *msg_cb = NULL;
static PyObject *progress_cb = NULL;
static void swig_progress(int percent, double etime, const char *hdr, int finish);

void set_msg_callback(PyObject *PyFunc) {
  if (PyFunc == Py_None) {
    if (msg_cb)
      Py_XDECREF(msg_cb);       // Remove reference to previous callback
    msg_cb = NULL;
  } else {
    if (msg_cb)
      Py_XDECREF(msg_cb);       // Remove reference to previous callback
    Py_XINCREF(PyFunc);         // Add reference to new callback
    msg_cb = PyFunc;
  }
}

void set_progress_callback(PyObject *PyFunc) {
  if (PyFunc == Py_None) {
    if (progress_cb)
      Py_XDECREF(progress_cb);  // Remove reference to previous callback
    update_progress = NULL;
    progress_cb = NULL;
  } else {
    if (progress_cb)
      Py_XDECREF(progress_cb);  // Remove reference to previous callback
    progress_cb = PyFunc;
    Py_XINCREF(PyFunc);         // Add reference to new callback
    update_progress = swig_progress;
  }
}

// We cannot pass va_args to Python, so pre-process the message here
// in C code.
int avrdude_message2(FILE *fp, int lno, const char *file,
                     const char *func, int msgmode, int msglvl,
                     const char *format, ...)
{
    int rc = 0;
    va_list ap;
    PyObject *backslash_v = Py_False;

    const char *target = fp == stderr? "stderr": "stdout";

    if (msglvl <= MSG_ERROR)     // Serious error? Free progress bars (if any)
      report_progress(1, -1, NULL);

    if (msgmode & MSG2_FLUSH) {
      // ignored
    }

    // Vertical tab at start of format string is a conditional new line.
    // We pass this information down to the callee.
    if (*format == '\v') {
      backslash_v = Py_True;
      format++;
    }

    // Determine required size first
    va_start(ap, format);
    rc = vsnprintf(NULL, 0, format, ap);
    va_end(ap);

    if (rc < 0)                 // Some errror?
      return 0;

    rc++;                       // Accommodate terminating nul
    char *p = cfg_malloc(__func__, rc);
    va_start(ap, format);
    rc = vsnprintf(p, rc, format, ap);
    va_end(ap);

    if (rc < 0) {
      free(p);
      return 0;
    }

    if (*p) {
      if (msg_cb) {
        PyObject *result =
          PyObject_CallFunction(msg_cb, "(sissiisO)",
                                target, lno, file, func, msgmode, msglvl, p, backslash_v);
        Py_XDECREF(result);
      }
      free(p);
    }

    return rc;
}

static void swig_progress(int percent, double etime, const char *hdr, int finish)
{
  if (progress_cb) {
    PyObject *result =
      PyObject_CallFunction(progress_cb, "(idsi)", percent, etime, hdr, finish);
    Py_XDECREF(result);
  }
}

PROGRAMMER *ldata_programmer(LNODEID p) {
  return (PROGRAMMER *)ldata(p);
}

AVRPART *ldata_avrpart(LNODEID p) {
  return (AVRPART *)ldata(p);
}

AVRMEM *ldata_avrmem(LNODEID p) {
  return (AVRMEM *)ldata(p);
}

AVRMEM_ALIAS *ldata_avrmem_alias(LNODEID p) {
  return (AVRMEM_ALIAS *)ldata(p);
}

const char *ldata_string(LNODEID p) {
  return (const char *)ldata(p);
}

PyObject *get_config_table(const char *name) {
  int idx = upidxname(name);
  if (idx == -1)
    return Py_None;

  const Avrintel *up = uP_table + idx;
  int nitems = up->nconfigs;

  // found: construct a list object
  PyObject *list = PyList_New(nitems);

  if (list == NULL) {
    return NULL;
  }

  // now, add list elements
  const Configitem *cfg = up->cfgtable;

  for (int i = 0; i < nitems; i++) {
    PyObject* dict = PyDict_New();
    if (dict == NULL) {
      return NULL;
    }
    PyDict_SetItem(dict, PyUnicode_FromString("name"),
                   PyUnicode_FromString(cfg->name));
    int nvalues = cfg->nvalues;
    PyObject *vlist = PyList_New(nvalues);
    if (vlist == NULL) {
      return NULL;
    }
    for (int j = 0; j < nvalues; j++) {
      PyObject* vdict = PyDict_New();
      if (vdict == NULL) {
        return NULL;
      }
      PyDict_SetItem(vdict, PyUnicode_FromString("value"),
                     PyLong_FromLong(cfg->vlist[j].value));
      PyDict_SetItem(vdict, PyUnicode_FromString("label"),
                     PyUnicode_FromString(cfg->vlist[j].label));
      PyDict_SetItem(vdict, PyUnicode_FromString("vcomment"),
                     PyUnicode_FromString(cfg->vlist[j].vcomment));

      PyList_SetItem(vlist, j, vdict);
    }
    PyDict_SetItem(dict, PyUnicode_FromString("vlist"),
                   vlist);
    PyDict_SetItem(dict, PyUnicode_FromString("memstr"),
                   PyUnicode_FromString(cfg->memstr));
    PyDict_SetItem(dict, PyUnicode_FromString("memoffset"),
                   PyLong_FromLong(cfg->memoffset));
    PyDict_SetItem(dict, PyUnicode_FromString("mask"),
                   PyLong_FromLong(cfg->mask));
    PyDict_SetItem(dict, PyUnicode_FromString("lsh"),
                   PyLong_FromLong(cfg->lsh));
    PyDict_SetItem(dict, PyUnicode_FromString("initval"),
                   PyLong_FromLong(cfg->initval));
    PyDict_SetItem(dict, PyUnicode_FromString("ccomment"),
                   PyUnicode_FromString(cfg->ccomment));

    PyList_SetItem(list, i, dict);

    cfg++;
  }

  return list;
}

%}

// globals from above are mapped to Python
%immutable;
char * version;
%mutable;
char * progname;
int verbose;
int quell_progress;
int ovsigck;
%immutable;
const char *partdesc;
const char *pgmid;
%mutable;

typedef void * LNODEID;
typedef void * LISTID;
typedef struct avrmem AVRMEM;
typedef struct avrmem_alias AVRMEM_ALIAS;
typedef struct programmer PROGRAMMER;
typedef void pgm_initpgm(PROGRAMMER*);

enum msglvl {
  MSG_EXT_ERROR = (-3),         // OS-type error, no -v option, can be suppressed with -qqqqq
  MSG_ERROR = (-2),             // Avrdude error, no -v option, can be suppressed with -qqqq
  MSG_WARNING = (-1),           // Warning, no -v option, can be suppressed with -qqq
  MSG_INFO = 0,                 // Commentary, no -v option, can be suppressed with -qq
  MSG_NOTICE = 1,               // Displayed with -v
  MSG_NOTICE2 = 2,              // Displayed with -vv
  MSG_DEBUG = 3,                // Displayed with -vvv
  MSG_TRACE = 4,                // Displayed with -vvvv, show trace communication
  MSG_TRACE2 = 5,               // Displayed with -vvvvv
};

enum msgmode {
  MSG2_PROGNAME = 1,            // Start by printing progname
  MSG2_FUNCTION = 2,            // Print calling function (1st arg) after progname if >= notice
  MSG2_FILELINE = 4,            // Print source file and line number after function if >= debug
  MSG2_TYPE = 8,                // Print message type after function or progname
  MSG2_INDENT1 = 16,            // Start by printing indentation of progname+1 blanks
  MSG2_INDENT2 = 32,            // Start by printing indentation of progname+2 blanks
  MSG2_FLUSH = 64,              // Flush before and after printing
  MSG2_LEFT_MARGIN = 128,       // Print \n unless last character printed was \n
};

// Functions to record a callback
%typemap(in) PyObject *PyFunc {
  // calling with None removes previous callback
  if ($input != Py_None && !PyCallable_Check($input)) {
      PyErr_SetString(PyExc_TypeError, "Need a callable object!");
      return NULL;
  }
  $1 = $input;
}
void set_msg_callback(PyObject *PyFunc);
void set_progress_callback(PyObject *PyFunc);

// These things are read from config file(s), and must be considered
// read-only by any program. Most internals are only relevant for
// libavrdude itself, so only map things to the Python level that are
// needed there.

%typemap(out) unsigned char signature[3] {
  $result = PyBytes_FromStringAndSize((const char *)$1, 3);
 }

%immutable;
typedef struct avrpart {
  const char  * desc;           /* long part name */
  const char  * id;             /* short part name */
  LISTID        variants;       /* String with variant name and chip properties */
  const char  * parent_id;      /* Used by developer options */
  const char  * family_id;      /* family id in the SIB (avr8x) */
  int           prog_modes;     /* Programming interfaces, see #define PM_... */
  unsigned char signature[3];   /* expected value of signature bytes */
  unsigned short usbpid;        /* USB DFU product ID (0 = none) */
  LISTID        mem;            /* avr memory definitions */
  LISTID        mem_alias;      /* memory alias definitions */
  const char  * config_file;    /* config file where defined */
  int           lineno;         /* config file line number */
} AVRPART;
%mutable;

typedef unsigned int Memtype;
typedef struct avrmem {
  %immutable;
  const char *desc;             /* memory description ("flash", "eeprom", etc) */
  Memtype type;                 /* internally used type, cannot be set in conf files */
  bool paged;                   /* 16-bit page addressed, e.g., ATmega flash but not EEPROM */
  int size;                     /* total memory size in bytes */
  int page_size;                /* size of memory page (if page addressed) */
  int num_pages;                /* number of pages (if page addressed) */
  int initval;                  /* factory setting of fuses and lock bits */
  int bitmask;                  /* bits used in fuses and lock bits */
  %mutable;
  unsigned char * buf;          /* pointer to memory buffer */
} AVRMEM;

%immutable;
typedef struct avrmem_alias {
  const char *desc;             /* alias name ("syscfg0" etc.) */
  AVRMEM *aliased_mem;
} AVRMEM_ALIAS;
%mutable;

%extend avrmem {
%feature("autodoc", "m.get(len: int, offset: int = 0) => return bytes; Read from memory buffer") get;
  PyObject *get(unsigned int len, unsigned int offset = 0) {
    if ($self->buf == NULL)
      // missing avr_initmem()?
      return Py_None;
    if (offset >= (unsigned)$self->size)
      return Py_None;
    if (offset + len > (unsigned)$self->size)
      len = $self->size - offset;
    return PyBytes_FromStringAndSize((char *)($self->buf + offset), len);
  }
}

int avr_initmem(const AVRPART *p);

%extend avrmem {
  %typemap(in) (unsigned char *in, unsigned int len) {
    Py_ssize_t len;
    PyBytes_AsStringAndSize($input, (char **)&$1, &len);
    $2 = len;
  }
%feature("autodoc", "m.put(in: bytes, len: int, offset: int = 0) => return len; Copy to memory buffer, set ALLOCATED tag") put;
  int put(unsigned char *in, unsigned int len, unsigned int offset = 0) {
    if ($self->buf == NULL)
      // missing avr_initmem()?
      return 0;
    if (offset >= (unsigned)$self->size)
      return 0;
    if (offset + len > (unsigned)$self->size)
      len = $self->size - offset;
    memcpy($self->buf + offset, in, len);
    memset($self->tags + offset, TAG_ALLOCATED, len);
    return len;
  }
}

%extend avrmem {
%feature("autodoc", "m.clear(len, offset, value=0xFF) => return len; Clear memory buffer range, and ALLOCATED tag") clear;
  int clear(unsigned int len, unsigned int offset = 0, unsigned char value = 0xFF) {
    if ($self->buf == NULL)
      // missing avr_initmem()?
      return 0;
    if (offset >= (unsigned)$self->size)
      return 0;
    if (offset + len > (unsigned)$self->size)
      len = $self->size - offset;
    memset($self->buf + offset, value, len);
    memset($self->tags + offset, 0, len);
    return len;
  }
}

%feature("autodoc", "1");

typedef enum {
  CONNTYPE_PARALLEL,
  CONNTYPE_SERIAL,
  CONNTYPE_USB,
  CONNTYPE_SPI,
  CONNTYPE_LINUXGPIO
} Conntype;

// https://stackoverflow.com/questions/11023940/how-do-i-get-swig-to-automatically-wrap-an-emulated-this-pointer-to-a-c-struct/11029809#11029809
// Make sure the wrapped function doesn't expect an input for this:
%typemap(in, numinputs=0) struct programmer *pgm "$1=NULL;"
// Slightly abuse check typemap, but it needs to happen after the rest of the arguments have been set:
%typemap(check) struct programmer *pgm {
  $1 = arg1;
}

%typemap(in, numinputs=0) char *sib (char temp[AVR_SIBLEN + 1]) {
  $1 = temp;
 }
%typemap(argout) char *sib {
  if ($result == NULL)
    $result = Py_None;
  else if (PyLong_Check($result) && PyLong_AsLong($result) != 0)
    $result = Py_None;
  else
    $result = PyBytes_FromStringAndSize((const char *)$1, AVR_SIBLEN);
 }
%typemap(in, numinputs=0) (double *vtarg_out) (double temp) {
  $1 = &temp;
 }
%typemap(argout) (double *vtarg_out) {
  if ($result == NULL)
    $result = Py_None;
  else if (PyLong_Check($result) && PyLong_AsLong($result) != 0)
    $result = Py_None;
  else
    $result = PyFloat_FromDouble(*$1);
 }
%typemap(in, numinputs=0) (double *varef_out) (double temp) {
  $1 = &temp;
 }
%typemap(argout) (double *varef_out) {
  if ($result == NULL)
    $result = Py_None;
  else if (PyLong_Check($result) && PyLong_AsLong($result) != 0)
    $result = Py_None;
  else
    $result = PyFloat_FromDouble(*$1);
 }
%typemap(in, numinputs=0) (double *fosc_out) (double temp) {
  $1 = &temp;
 }
%typemap(argout) (double *fosc_out) {
  if ($result == NULL)
    $result = Py_None;
  else if (PyLong_Check($result) && PyLong_AsLong($result) != 0)
    $result = Py_None;
  else
    $result = PyFloat_FromDouble(*$1);
 }
%typemap(in, numinputs=0) (double *sck_out) (double temp) {
  $1 = &temp;
 }
%typemap(argout) (double *sck_out) {
  if ($result == NULL)
    $result = Py_None;
  else if (PyLong_Check($result) && PyLong_AsLong($result) != 0)
    $result = Py_None;
  else
    $result = PyFloat_FromDouble(*$1);
 }
// abuse check typemap to check for methods not being NULL
// it must be ensured that each argument type/name applies to just one method
%typemap(check) (char *sib) {
  if ((arg1)->read_sib == NULL) {
    SWIG_exception_fail(SWIG_RuntimeError, "pgm->read_sib is NULL");
  }
}
%typemap(check) (FILE *f_parms) {
  if ((arg1)->print_parms == NULL) {
    SWIG_exception_fail(SWIG_RuntimeError, "pgm->print_parms is NULL");
  }
}
%typemap(check) (double vtarg_in) {
  if ((arg1)->set_vtarget == NULL) {
    SWIG_exception_fail(SWIG_RuntimeError, "pgm->set_vtarget is NULL");
  }
}
%typemap(check) (double *vtarg_out) {
  if ((arg1)->get_vtarget == NULL) {
    SWIG_exception_fail(SWIG_RuntimeError, "pgm->get_vtarget is NULL");
  }
}
%typemap(check) (double varef_in) {
  if ((arg1)->set_varef == NULL) {
    SWIG_exception_fail(SWIG_RuntimeError, "pgm->set_varef is NULL");
  }
}
%typemap(check) (double *varef_out) {
  if ((arg1)->get_varef == NULL) {
    SWIG_exception_fail(SWIG_RuntimeError, "pgm->get_varef is NULL");
  }
}
%typemap(check) (double fosc_in) {
  if ((arg1)->set_fosc == NULL) {
    SWIG_exception_fail(SWIG_RuntimeError, "pgm->set_fosc is NULL");
  }
}
%typemap(check) (double *fosc_out) {
  if ((arg1)->get_fosc == NULL) {
    SWIG_exception_fail(SWIG_RuntimeError, "pgm->get_fosc is NULL");
  }
}
%typemap(check) (double sck_in) {
  if ((arg1)->set_sck_period == NULL) {
    SWIG_exception_fail(SWIG_RuntimeError, "pgm->set_sck_period is NULL");
  }
}
%typemap(check) (double *sck_out) {
  if ((arg1)->get_sck_period == NULL) {
    SWIG_exception_fail(SWIG_RuntimeError, "pgm->get_sck_period is NULL");
  }
}
%typemap(check) (struct programmer *pgm_setup) {
  if ((arg1)->setup == NULL) {
    SWIG_exception_fail(SWIG_RuntimeError, "pgm->setup is NULL");
  }
}
%typemap(check) (struct programmer *pgm_teardown) {
  if ((arg1)->teardown == NULL) {
    SWIG_exception_fail(SWIG_RuntimeError, "pgm->teardown is NULL");
  }
}

%immutable;
typedef struct programmer {
  LISTID id;
  const char *desc;
  int prog_modes;               // Programming interfaces, see #define PM_...
  Conntype conntype;
  int baudrate;
  int usbvid;
  LISTID usbpid;
  int ispdelay;                 // ISP clock delay
  double bitclock;              // JTAG ICE clock period in microseconds
  char type[PGM_TYPELEN];

  // methods; they must *not* be declares as pointers
  void initpgm        (struct programmer *pgm); // Sets up the AVRDUDE programmer
  int  initialize     (const struct programmer *pgm, const AVRPART *p); // Sets up the physical programmer
  void setup          (struct programmer *pgm);
  void teardown       (struct programmer *pgm);
  int  parseextparams (const struct programmer *pgm, const LISTID xparams);
  int  parseexitspecs (struct programmer *pgm, const char *s);
  int  open           (struct programmer *pgm, const char *port);
  void close          (struct programmer *pgm);
  void enable         (struct programmer *pgm, const AVRPART *p);
  void disable        (const struct programmer *pgm);
  int  read_sib       (const struct programmer *pgm, const AVRPART *p, char *sib);
  void powerup        (const struct programmer *pgm);
  void powerdown      (const struct programmer *pgm);

  int  chip_erase     (const struct programmer *pgm, const AVRPART *p);
  int  term_keep_alive(const struct programmer *pgm, const AVRPART *p);
  int  end_programming(const struct programmer *pgm, const AVRPART *p);

  void print_parms    (const struct programmer *pgm, FILE *f_parms);
  int  set_vtarget    (const struct programmer *pgm, double vtarg_in);
  int  get_vtarget    (const struct programmer *pgm, double *vtarg_out);
  int  set_varef      (const struct programmer *pgm, unsigned int chan, double varef_in);
  int  get_varef      (const struct programmer *pgm, unsigned int chan, double *varef_out);
  int  set_fosc       (const struct programmer *pgm, double fosc_in);
  int  get_fosc       (const struct programmer *pgm, double *fosc_out);
  int  set_sck_period (const struct programmer *pgm, double sck_in);
  int  get_sck_period (const struct programmer *pgm, double *sck_out);
  // Cached r/w API for terminal reads/writes
  int write_byte_cached(const struct programmer *pgm, const AVRPART *p, const AVRMEM *m,
                        unsigned long addr, unsigned char value);
  int read_byte_cached(const struct programmer *pgm, const AVRPART *p, const AVRMEM *m,
                        unsigned long addr, unsigned char *value);
  int chip_erase_cached(const struct programmer *pgm, const AVRPART *p);
  int page_erase_cached(const struct programmer *pgm, const AVRPART *p, const AVRMEM *m,
                        unsigned int baseaddr);
  int readonly        (const struct programmer *pgm, const AVRPART *p, const AVRMEM *m,
                        unsigned int addr);
  int flush_cache     (const struct programmer *pgm, const AVRPART *p);
  int reset_cache     (const struct programmer *pgm, const AVRPART *p);

} PROGRAMMER;
%mutable;
%clear struct programmer *pgm;

void init_cx(PROGRAMMER *pgm = NULL);

// Config file handling
int init_config(void);

void cleanup_config(void);

int read_config(const char * file);

// Lists must not be manipulated from the Python level, so
// only map access functions.
extern LISTID      part_list;
extern LISTID      programmers;

LNODEID    lfirst ( LISTID  );  /* head of the list */
LNODEID    llast  ( LISTID  );  /* tail of the list */
LNODEID    lnext  ( LNODEID );  /* next item in the list */
LNODEID    lprev  ( LNODEID );  /* previous item in the list */
void     * ldata  ( LNODEID );  /* data at the current position */
int        lsize  ( LISTID  );  /* number of elements in the list */

// Typecast helpers to interpret LNODEID
%feature("autodoc", "LNODEID -> PROGRAMMER*") ldata_programmer;
PROGRAMMER *ldata_programmer(LNODEID);

%feature("autodoc", "LNODEID -> AVRPART*") ldata_avrpart;
AVRPART *ldata_avrpart(LNODEID);

%feature("autodoc", "LNODEID -> AVRMEM*") ldata_avrmem;
AVRMEM *ldata_avrmem(LNODEID);

%feature("autodoc", "LNODEID -> AVRMEM_ALIAS*") ldata_avrmem_alias;
AVRMEM_ALIAS *ldata_avrmem_alias(LNODEID);

%feature("autodoc", "LNODEID -> str") ldata_string;
const char *ldata_string(LNODEID);

// AVRMEM and AVRPART handling
AVRMEM * avr_locate_mem(const AVRPART *p, const char *desc);
AVRPART * locate_part(const LISTID parts, const char *partdesc);
AVRMEM_ALIAS * avr_locate_memalias(const AVRPART *p, const char *desc);
AVRMEM_ALIAS * avr_find_memalias(const AVRPART *p, const AVRMEM *m_orig);

// Programming modes for parts and programmers: ensure it's copied from libavrdude.h
#define PM_SPM                1 // Bootloaders, self-programming with SPM opcodes or NVM Controllers
#define PM_TPI                2 // Tiny Programming Interface (t4, t5, t9, t10, t20, t40, t102, t104)
#define PM_ISP                4 // SPI programming for In-System Programming (almost all classic parts)
#define PM_PDI                8 // Program and Debug Interface (xmega parts)
#define PM_UPDI              16 // Unified Program and Debug Interface
#define PM_HVSP              32 // High Voltage Serial Programming (some classic parts)
#define PM_HVPP              64 // High Voltage Parallel Programming (most non-HVSP classic parts)
#define PM_debugWIRE        128 // Simpler alternative to JTAG (a subset of HVPP/HVSP parts)
#define PM_JTAG             256 // Joint Test Action Group standard (some classic parts)
#define PM_JTAGmkI          512 // Subset of PM_JTAG, older parts, Atmel ICE mkI
#define PM_XMEGAJTAG       1024 // JTAG, some XMEGA parts
#define PM_AVR32JTAG       2048 // JTAG for 32-bit AVRs
#define PM_aWire           4096 // For 32-bit AVRs
#define PM_Classic (PM_TPI | PM_ISP | PM_HVSP | PM_HVPP | PM_debugWIRE | PM_JTAG | PM_JTAGmkI)
#define PM_ALL           0x1fff // All programming interfaces

// map Python bytes() to sig+sigsize
%typemap(in) (unsigned char *sig, int sigsize) {
  Py_ssize_t len;
  PyBytes_AsStringAndSize($input, (char **)&$1, &len);
  $2 = (int)len;
}
AVRPART * locate_part_by_signature(const LISTID parts, unsigned char *sig, int sigsize);
AVRPART * locate_part_by_signature_pm(const LISTID parts, unsigned char *sig, int sigsize, int prog_modes);

PROGRAMMER *locate_programmer_set(const LISTID programmers, const char *id, const char **setid);
PROGRAMMER *locate_programmer_starts_set(const LISTID programmers, const char *id, const char **setid, AVRPART *prt);
PROGRAMMER *locate_programmer(const LISTID programmers, const char *configid);

// Abuse the check typemaps to inject some code into the wrapper

// avr_read_mem() and avr_write_mem() do not initialize progress
// reporting, since this is left to the caller. The idea is that in
// some situations (e.g. signature readout), no progress reporting is
// desired. Thus, we inject the respective progress reporting
// initialization here into the wrapper functions.
%typemap(check) AVRMEM *mem {
  report_progress(0, 1, "Reading");
}
%feature("autodoc", "avr_read_mem(PROGRAMMER pgm, AVRPART p, AVRMEM mem, AVRPART v=None -> int; v: verify against") avr_read_mem;
int avr_read_mem(const PROGRAMMER * pgm, const AVRPART *p, const AVRMEM *mem, const AVRPART *v = NULL);
%clear AVRMEM *mem;

%typemap(check) AVRMEM *mem {
  report_progress(0, 1, "Writing");
}
%feature("autodoc", "avr_write_mem(PROGRAMMER pgm, AVRPART p, AVRMEM mem, int size, int auto_erase) -> int; write entire memory region from `mem` buffer") avr_write_mem;
int avr_write_mem(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
                  int size, int auto_erase = false);
%clear AVRMEM *mem;

%feature("autodoc", "avr_write_byte(PROGRAMMER pgm, AVRPART p, AVRMEM mem, int addr, byte data) -> int") avr_write_byte;
int avr_write_byte(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
                   unsigned long addr, unsigned char data);
typedef enum {
  FMT_ERROR = -1,
  FMT_AUTO,
  FMT_SREC,
  FMT_IHEX,
  FMT_RBIN,
  FMT_IMM,
  FMT_EEGG,
  FMT_HEX,
  FMT_DEC,
  FMT_OCT,
  FMT_BIN,
  FMT_ELF,
  FMT_IHXC,
} FILEFMT;

typedef enum {
  FIO_READ,
  FIO_WRITE,
  FIO_READ_FOR_VERIFY,
} FIO_OP;


%feature("autodoc", "fileio_format(c: char) -> FILEFMT") fileio_format;
FILEFMT fileio_format(char c);

%feature("autodoc", "fileio_fmtstr(f: FILEFMT) -> str") fileio_fmtstr;
char *fileio_fmtstr(FILEFMT format);

%feature("autodoc", "fileio_fmtchr(f: FILEFMT) -> char") fileio_fmtchr;
int fileio_fmtchr(FILEFMT format);

%feature("autodoc", "fileio_fmt_autodetect(fname: str) -> FILEFMT") fileio_fmt_autodetect;
int fileio_fmt_autodetect(const char *fname);

%feature("autodoc", "fileio(oprwv: FIO_OP, filename: str, format: FILEFMT, p: AVRPART, memstr: str, size: int) -> int [size, or -1 on error]") fileio;
int fileio(int oprwv, const char *filename, FILEFMT format,
  const AVRPART *p, const char *memstr, int size);

// avrintel stuff - configuration values for various AVRs

%feature("autodoc", "get_config_table(name: str) -> list") get_config_table;
PyObject *get_config_table(const char *name);

%feature("autodoc", "1");
