#include "avrdude.h"
#include "programmer.h"
#include "part.h"
#include "update.h"
#include <string>
#include <iostream>

extern "C" {
#if defined(__WINDOWS__)
#include "avrdude/libavrdude.h"
#else
#include "libavrdude.h"
#endif
/*
#include "avrdude/avrdude.h"
#include "avrdude/avr.h"
#include "avrdude/config.h"
#include "avrdude/confwin.h"
#include "avrdude/fileio.h"
#include "avrdude/lists.h"
#include "avrdude/par.h"
#include "avrdude/pindefs.h"
#include "avrdude/term.h"
#include "avrdude/safemode.h"
#include "avrdude/update.h"
#include "avrdude/usbasp.h"
#include "avrdude/pgm.h"
*/
}

class ProgrammerPrivate {
  public:
    ::PROGRAMMER * pgm;
};

Programmer * Programmer::locateProgrammer(const std::string & name) {
    PROGRAMMER * pgm = 0;
    pgm = locate_programmer(programmers, name.c_str());
    if (pgm != NULL) {
        std::cout << "DBG: pgm!==NULL" << std::endl;
        std::cout << "DBG: pgm->desc: " << pgm->desc << std::endl;
        Programmer * prg = new Programmer();
        prg->priv->pgm = pgm;
        return prg;
    }
    return 0;
}


Programmer::Programmer() {
  priv = new ProgrammerPrivate();
}

Programmer::~Programmer() {
  if (priv->pgm->teardown) {
    priv->pgm->teardown(priv->pgm);
  }
  delete priv;
}

void Programmer::setup() {
  if (priv->pgm->setup) {
    priv->pgm->setup(priv->pgm);
  }
}

void Programmer::initpgm() {
    
  if (priv->pgm->initpgm) {
    priv->pgm->initpgm(priv->pgm);
  }
}


bool Programmer::open(const std::string & port) {
  if (priv->pgm->setup) {
    int rc = priv->pgm->open(priv->pgm, (char*) port.c_str());
    return rc>=0;
  }
  return false;
}

#if (LIBAVRDUDE_VER >= 70)
void Programmer::enable(Part & part) {
  priv->pgm->enable(priv->pgm, (struct avrpart *)(part.getPointer()));
}
#else
void Programmer::enable() {
  priv->pgm->enable(priv->pgm);
}
#endif

void Programmer::powerdown() {
  priv->pgm->powerdown(priv->pgm);
}

void Programmer::disable() {
  priv->pgm->disable(priv->pgm);
}

void Programmer::rdy_led(bool on) {
  priv->pgm->rdy_led(priv->pgm, on?ON:OFF);
}

void Programmer::close() {
  priv->pgm->close(priv->pgm);
}

void Programmer::initialize(Part & part) {
  priv->pgm->initialize(priv->pgm, (struct avrpart *)(part.getPointer()));
}

void Programmer::avr_chip_erase(Part & part) {
  ::avr_chip_erase(priv->pgm, (struct avrpart *)(part.getPointer()));
}

bool Programmer::do_update_op(Part & part, avrdude::Update & op, bool nowrite, bool verify) {
  //return ::do_op(priv->pgm, (struct avrpart *)(part.getPointer()), (UPDATE *)(op.getPointer()), nowrite?1:0, verify?1:0)==0;
  enum updateflags flags = (nowrite?UF_NOWRITE:UF_NONE);
  return ::do_op(priv->pgm, (struct avrpart *)(part.getPointer()), (UPDATE *)(op.getPointer()), flags)==0;
}


static char tohexchar(int value) {
  return (value<10)?(value+'0'):(value+'a'-10);
}

static std::string bytetohex(int value) {
  std::string retval;
  retval += tohexchar((value&0xf0)>>4);
  retval += tohexchar((value&0x0f)>>0);
  return retval;
}

std::string Programmer::read_signature(Part & part) {
  std::string retval = "";
  int rc = avr_signature(priv->pgm, (struct avrpart *)(part.getPointer()));
  if (rc != 0) {
    return "";
  }

  AVRMEM * sig = avr_locate_mem((struct avrpart *)(part.getPointer()), (char*)"signature");
  if (sig == NULL) {
    return "";
  }

  for (int i=0; i<sig->size; i++) {
    retval+= bytetohex(sig->buf[i]);
  }
  return retval;
}


static std::vector<ProgrammerInfo> localProgInfo;

static void walkProgCB(const char *name, const char *desc, const char *cfgname, int cfglineno, void *cookie) {
  localProgInfo.push_back(ProgrammerInfo());
  localProgInfo.back().name = name;
  localProgInfo.back().desc = desc;
  localProgInfo.back().cfgname = cfgname;
  localProgInfo.back().cfglineno = cfglineno;
}

std::vector<ProgrammerInfo> Programmer::getAvailableProgrammers() {
  ::walk_programmers(programmers, &walkProgCB , 0);
  return localProgInfo;
}

void Programmer::setBaudrate(unsigned int baudrate) {
    priv->pgm->baudrate=baudrate;
}

