/* -*- C++ -*- */
/*
 * snmpkit: primary snmpkit header file
 *
 * This library 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 library 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 library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 *
 * See the AUTHORS file for a list of people who have hacked on 
 * this code. 
 * See the ChangeLog file for a list of changes.
 *
 */

#ifndef __SNMPKIT_HEADER__
#define __SNMPKIT_HEADER__

#include <netdb.h>
#include <pthread.h>

#include <string>
#include <list>
#include <functional>

#include "snmpkit_tags"
#include "snmpkit_except"

class OidSeq;
class BerSequence;

class SNMP_session {
  static char need_newline; 
  static SNMP_session *lastprint;
  static pthread_mutex_t lastprint_m;

  std::string community;
  hostent *he;
  int ipidx;
  std::string hostname;

  OidSeq *do_req(Tags tag, OidSeq *oids)
    throw(SNMPPacketNotSequenceException,SNMPRespNotSequenceException,
	  SNMPNotResponseTagException,SNMPSeqnoNotIntException,
	  SNMPStateNotIntException,SNMPFaultOidNotIntException,
	  OidSeqBadLayoutException,SNMPBadOidException,std::bad_alloc,
	  SocketSendShortExecption,BerSequenceTagException,BerLengthException,
	  BerIntTagException,BerIntLengthExecption,BerCounterTagException,
	  BerCounterLengthExecption,BerStringTagException,BerNullTagException,
	  BerNullLengthExecption,BerOidTagException,BerTimeTickTagException,
	  BerTimeTickLengthExecption,BerIPAddrLengthExecption);

  unsigned int flags;
  int debugfile;
public:
  static void end();

  SNMP_session(const std::string &host,
	       void *(*start_routine)(SNMP_session*), 
	       const std::string &community="public")
    throw(std::bad_alloc,SocketNoUDPException,SocketCreateFailException,
	  ReceiverCreateException,SessionHostNotFoundException,
	  JoinerCreateException,SessionWorkerCreateException);
  ~SNMP_session();

  /* these take one oid sequence and then return a new oidseq with
     the values filled in. The oidseq passed in will not be 
     touched and the oidseq that is returned will need to be 
     freed. There are two reasons for not filling in the original 
     oidseq. 1) it is more work. 2) that way you can use the
     same sequence to ask multiple hosts the same questions. */
  inline OidSeq *__get(OidSeq *oids)
    throw(SNMPPacketNotSequenceException,SNMPRespNotSequenceException,
	  SNMPNotResponseTagException,SNMPSeqnoNotIntException,
	  SNMPStateNotIntException,SNMPFaultOidNotIntException,
	  OidSeqBadLayoutException,SNMPBadOidException,std::bad_alloc,
	  SocketSendShortExecption,BerSequenceTagException,BerLengthException,
	  BerIntTagException,BerIntLengthExecption,BerCounterTagException,
	  BerCounterLengthExecption,BerStringTagException,BerNullTagException,
	  BerNullLengthExecption,BerOidTagException,BerTimeTickTagException,
	  BerTimeTickLengthExecption,BerIPAddrLengthExecption){
    return do_req(GET_REQ_TAG,oids);}
  inline OidSeq *__get_next(OidSeq *oids)
    throw(SNMPPacketNotSequenceException,SNMPRespNotSequenceException,
	  SNMPNotResponseTagException,SNMPSeqnoNotIntException,
	  SNMPStateNotIntException,SNMPFaultOidNotIntException,
	  OidSeqBadLayoutException,SNMPBadOidException,std::bad_alloc,
	  SocketSendShortExecption,BerSequenceTagException,BerLengthException,
	  BerIntTagException,BerIntLengthExecption,BerCounterTagException,
	  BerCounterLengthExecption,BerStringTagException,BerNullTagException,
	  BerNullLengthExecption,BerOidTagException,BerTimeTickTagException,
	  BerTimeTickLengthExecption,BerIPAddrLengthExecption){
    return do_req(GET_NEXT_TAG,oids);}
  inline OidSeq *__set(OidSeq *oids)
    throw(SNMPPacketNotSequenceException,SNMPRespNotSequenceException,
	  SNMPNotResponseTagException,SNMPSeqnoNotIntException,
	  SNMPStateNotIntException,SNMPFaultOidNotIntException,
	  OidSeqBadLayoutException,SNMPBadOidException,std::bad_alloc,
	  SocketSendShortExecption,BerSequenceTagException,BerLengthException,
	  BerIntTagException,BerIntLengthExecption,BerCounterTagException,
	  BerCounterLengthExecption,BerStringTagException,BerNullTagException,
	  BerNullLengthExecption,BerOidTagException,BerTimeTickTagException,
	  BerTimeTickLengthExecption,BerIPAddrLengthExecption){
    return do_req(SET_REQ_TAG,oids);}
  inline char *ConnHost(){return he->h_addr_list[ipidx];}
  inline const std::string &Hostname(){return hostname;}

  void setDebug()throw(DebugFileOpenException);
  void __write_debug(const std::string &dirstr, BerSequence *packet);
  void __write_debug_bin(const std::basic_string<unsigned char> &str);

  void printstr(char need_newline,char *str,char ck_name_flag);
};

void set_snmpsock_props(int timeout,int retries=5, int port=0);
int SNMP_sessions_done();

std::list<SNMP_session*> &SNMP_sessions(std::list<SNMP_session*> &dest,
					std::string &hostspec,
					void *(*start_routine)(SNMP_session*),
					const std::string &community="public")
  throw(std::bad_alloc,SocketNoUDPException,SocketCreateFailException,
	ReceiverCreateException,SessionHostNotFoundException,
	JoinerCreateException,SessionWorkerCreateException,
	SessionCommunityException,SessionOctetOverflowException,
	SessionBadSubnetException,SessionNetbitsOverflowException,
	SessionBadNetmaskException);
std::list<SNMP_session*> &SNMP_sessions(std::list<SNMP_session*> &dest,
				   std::list<std::string> &hostspecs,
				   void *(*start_routine)(SNMP_session*),
				   const std::string &community="public")
  throw(std::bad_alloc,SocketNoUDPException,SocketCreateFailException,
	ReceiverCreateException,SessionHostNotFoundException,
	JoinerCreateException,SessionWorkerCreateException,
	SessionCommunityException,SessionOctetOverflowException,
	SessionBadSubnetException,SessionNetbitsOverflowException,
	SessionBadNetmaskException);

class SNMP_structFiller{
  class TableEntry{
  public:
    std::string oidstr;
    Tags type;
    void (*fp)();

    inline TableEntry(const std::string &ostr,
		      Tags thetype,void (*funct)()):
      oidstr(ostr),type(thetype),fp(funct){}
  
    inline int operator==(std::string str){ return oidstr==str;}
  };

  class TableEntry_eq:public std::unary_function<TableEntry,bool>{
    std::string s;
  public:
    explicit TableEntry_eq(const std::string &ss):s(ss){}
    bool operator()(const TableEntry &c)const{return c.oidstr==s;}
  };

  std::list<TableEntry> tabdef;
  OidSeq *oidseq;
  OidSeq *retseq;
  SNMP_session &session;

  int fillStruct(OidSeq *data,void *curstruct)
    throw(FillerTypeMismatchException,BerUnexpectedTagException,
	  OidSeqBadLayoutException);
  void append(const std::string &oidstr,Tags tag,void (*fp)())
    throw(std::bad_alloc,BerOidBadSubOidException,BerNoOidsException);
public:
  inline SNMP_structFiller(SNMP_session &sess):
    oidseq(NULL),retseq(NULL),session(sess){}

  ~SNMP_structFiller();

  inline void append_int(const std::string &oidstr,
			 void (*fp)(void*,long))
    throw(std::bad_alloc,BerOidBadSubOidException,BerNoOidsException){
    append(oidstr,INT_TAG,reinterpret_cast<void (*)()>(fp));}
  inline void append_counter(const std::string &oidstr,
			     void (*fp)(void*,long))
    throw(std::bad_alloc,BerOidBadSubOidException,BerNoOidsException){
    append(oidstr,COUNTER_TAG,reinterpret_cast<void (*)()>(fp));}
  inline void append_timetick(const std::string &oidstr,
			      void (*fp)(void*,unsigned long))
    throw(std::bad_alloc,BerOidBadSubOidException,BerNoOidsException){
    append(oidstr,TIME_TICK_TAG,reinterpret_cast<void (*)()>(fp));}
  inline void append_ipaddr(const std::string &oidstr,
			    void (*fp)(void*,const unsigned char *))
    throw(std::bad_alloc,BerOidBadSubOidException,BerNoOidsException){
    append(oidstr,IPADDR_TAG,reinterpret_cast<void (*)()>(fp));}
  inline void append_string(const std::string &oidstr,
			    void (*fp)(void*,const char *))
    throw(std::bad_alloc,BerOidBadSubOidException,BerNoOidsException){
    append(oidstr,STRING_TAG,reinterpret_cast<void (*)()>(fp));}

  void remove(const std::string &oidstr)
    throw(FillerRemoveEmptyException,FillerRemoveNotFoundException,
	  FillerCorruptException,OidSeqRemoveNotFoundException,
	  OidSeqBadLayoutException);

  void *get(void *tobefilled) 
    throw(SNMPNoResponseException,SNMPPacketNotSequenceException,
	  SNMPRespNotSequenceException,SNMPNotResponseTagException,
	  SNMPSeqnoNotIntException,SNMPStateNotIntException,
	  SNMPFaultOidNotIntException,OidSeqBadLayoutException,
	  SNMPBadOidException,std::bad_alloc,SocketSendShortExecption,
	  BerSequenceTagException,BerLengthException,BerIntTagException,
	  BerIntLengthExecption,BerCounterTagException,
	  BerCounterLengthExecption,BerStringTagException,BerNullTagException,
	  BerNullLengthExecption,BerOidTagException,BerTimeTickTagException,
	  BerTimeTickLengthExecption,BerIPAddrLengthExecption);
  void *get_next(void *tobefilled) 
    throw(SNMPNoResponseException,SNMPPacketNotSequenceException,
	  SNMPRespNotSequenceException,SNMPNotResponseTagException,
	  SNMPSeqnoNotIntException,SNMPStateNotIntException,
	  SNMPFaultOidNotIntException,OidSeqBadLayoutException,
	  SNMPBadOidException,std::bad_alloc,SocketSendShortExecption,
	  BerSequenceTagException,BerLengthException,BerIntTagException,
	  BerIntLengthExecption,BerCounterTagException,
	  BerCounterLengthExecption,BerStringTagException,BerNullTagException,
	  BerNullLengthExecption,BerOidTagException,BerTimeTickTagException,
	  BerTimeTickLengthExecption,BerIPAddrLengthExecption);
};

class SNMP_table:public SNMP_structFiller{
  void *(*constructor)();
public:
  inline SNMP_table(SNMP_session &sess, void *(*new_funct)()):
    SNMP_structFiller(sess),constructor(new_funct){}

  void get(std::list<void*> &dest)
    throw(SNMPNoResponseException,SNMPPacketNotSequenceException,
	  SNMPRespNotSequenceException,SNMPNotResponseTagException,
	  SNMPSeqnoNotIntException,SNMPStateNotIntException,
	  SNMPFaultOidNotIntException,OidSeqBadLayoutException,
	  SNMPBadOidException,std::bad_alloc,SocketSendShortExecption,
	  BerSequenceTagException,BerLengthException,BerIntTagException,
	  BerIntLengthExecption,BerCounterTagException,
	  BerCounterLengthExecption,BerStringTagException,BerNullTagException,
	  BerNullLengthExecption,BerOidTagException,BerTimeTickTagException,
	  BerTimeTickLengthExecption,BerIPAddrLengthExecption);
};

#endif
