/* This file is part of Lcrzo.
*/

#if defined LCRZODEF_SYSTEM_Unix
 /* do no need these functions */
#elif defined LCRZODEF_SYSTEM_Windows

/*-------------------------------------------------------------*/
SNMPAPI SNMP_FUNC_TYPE SnmpUtilOidCpy(OUT AsnObjectIdentifier *DstObjId,
                                      IN  AsnObjectIdentifier *SrcObjId)
{
  DstObjId->ids = (UINT *)GlobalAlloc(GMEM_ZEROINIT, SrcObjId->idLength * sizeof(UINT));
  if(!DstObjId->ids){
    SetLastError(1);
    return 0;
  }

  memcpy(DstObjId->ids, SrcObjId->ids, SrcObjId->idLength*sizeof(UINT));
  DstObjId->idLength = SrcObjId->idLength;

  return 1;
}

/*-------------------------------------------------------------*/
VOID SNMP_FUNC_TYPE SnmpUtilOidFree(IN OUT AsnObjectIdentifier *ObjId)
{ GlobalFree(ObjId->ids);
  ObjId->ids = 0;
  ObjId->idLength = 0;
}

/*-------------------------------------------------------------*/
SNMPAPI SNMP_FUNC_TYPE SnmpUtilOidNCmp(IN AsnObjectIdentifier *ObjIdA,
                                       IN AsnObjectIdentifier *ObjIdB,
                                       IN UINT                 Len)
{
  UINT CmpLen;
  UINT i;
  int  res;

  CmpLen = Len;
  if(ObjIdA->idLength < CmpLen)
    CmpLen = ObjIdA->idLength;
  if(ObjIdB->idLength < CmpLen)
    CmpLen = ObjIdB->idLength;

  for(i=0;i<CmpLen;i++){
    res = ObjIdA->ids[i] - ObjIdB->ids[i];
    if(res!=0)
      return res;
  }
  return 0;
}

/*-------------------------------------------------------------*/
VOID SNMP_FUNC_TYPE SnmpUtilVarBindFree(IN OUT RFC1157VarBind *VarBind)
{
  BYTE asnType;
  // free object name
  SnmpUtilOidFree(&VarBind->name);

  asnType = VarBind->value.asnType;

  if(asnType==ASN_OBJECTIDENTIFIER){
    SnmpUtilOidFree(&VarBind->value.asnValue.object);
  }
  else if(
        (asnType==ASN_OCTETSTRING) ||
        (asnType==ASN_RFC1155_IPADDRESS) ||
        (asnType==ASN_RFC1155_OPAQUE) ||
        (asnType==ASN_SEQUENCE)){
    if(VarBind->value.asnValue.string.dynamic){
      GlobalFree(VarBind->value.asnValue.string.stream);
    }
  }

  VarBind->value.asnType = ASN_NULL;

}

/*-------------------------------------------------------------*/
int lcrzo_priv_AsnOid_init(OUT AsnObjectIdentifier *DstObjId, ...)
{ int count, i;
  va_list marker;

  /* count the number of arguments */
  va_start(marker, DstObjId);
  count=0;
  i=va_arg(marker, int);
  while( i != -1 )
  { count++;
    i=va_arg(marker, int);
  }
  va_end(marker);  
  
  /* initialize memory */
  DstObjId->idLength=count;
  DstObjId->ids = (UINT *)GlobalAlloc(GMEM_ZEROINIT, DstObjId->idLength * sizeof(UINT));
  if(!DstObjId->ids){
    SetLastError(1);
    return LCRZO_ERR_FUGLOBALALLOC;
  }

  /* set values */
  va_start(marker, DstObjId);
  for ( i=0 ; i<count ; i++ )
  { DstObjId->ids[i]=va_arg(marker, int);
  }
  va_end(marker);  

  return LCRZO_ERR_OK;
}

/*-------------------------------------------------------------*/
int lcrzo_priv_AsnOid_close(OUT AsnObjectIdentifier *ObjId)
{ 
  SnmpUtilOidFree(ObjId);
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_AsnOid_print(OUT AsnObjectIdentifier *ObjId)
{ unsigned int i;

  for ( i=0 ; i<ObjId->idLength-1 ; i++ )
  { printf("%d.", ObjId->ids[i]);
  }
  printf("%d", ObjId->ids[ObjId->idLength-1]);

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
typedef BOOL(WINAPI * pSnmpExtensionInit) (
        IN DWORD dwTimeZeroReference,
        OUT HANDLE * hPollForTrapEvent,
        OUT AsnObjectIdentifier * supportedView);

/*-------------------------------------------------------------*/
int lcrzo_priv_snmpdll_init(lcrzo_priv_snmpdll *psnmpdll)
{ pSnmpExtensionInit snmpe_init;
  HANDLE PollForTrapEvent;
  AsnObjectIdentifier SupportedView;
  int ret;

  /* load the DLL */
  psnmpdll->hinstance=LoadLibrary("inetmib1.dll");
  if ( psnmpdll->hinstance==NULL )
  { return(LCRZO_ERR_FULOADLIBRARY);
  }

  /* init query function */
  psnmpdll->snmpqueryfunction=(pSnmpExtensionQuery)GetProcAddress(psnmpdll->hinstance, "SnmpExtensionQuery");
  if ( psnmpdll->snmpqueryfunction==NULL )
  { FreeLibrary(psnmpdll->hinstance);
    return(LCRZO_ERR_FUGETPROCADDRESS);
  }

  /* initialize the extension agent */
  snmpe_init=(pSnmpExtensionInit)GetProcAddress(psnmpdll->hinstance, "SnmpExtensionInit");
  if ( snmpe_init==NULL )
  { FreeLibrary(psnmpdll->hinstance);
    return(LCRZO_ERR_FUGETPROCADDRESS);
  }
  ret=snmpe_init(GetTickCount(), &PollForTrapEvent, &SupportedView);
  if (!ret)
  { FreeLibrary(psnmpdll->hinstance);
    return(LCRZO_ERR_FUSNMPEXTENSIONINIT);
  }
  /*lcrzo_priv_AsnOid_print(&SupportedView); puts("");*/
  /* PollForTrapEvent and SupportedView are automatically closed by the dll */

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_snmpdll_close(lcrzo_priv_snmpdll *psnmpdll)
{ int ret;

  /* unload the DLL */
  ret=FreeLibrary(psnmpdll->hinstance);
  if (!ret) return(LCRZO_ERR_FUFREELIBRARY);

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_snmpdllreq_init(lcrzo_priv_snmpdllreq *psnmpdllreq,
                               AsnObjectIdentifier *poid1,
                               AsnObjectIdentifier *poid2,
                               AsnObjectIdentifier *poid3,
                               AsnObjectIdentifier *poid4,
                               AsnObjectIdentifier *poid5)
{ int ret;

  /* varBind will be used as the array */
  psnmpdllreq->varBindList.list = psnmpdllreq->varBind;

  /* if there is no oid */
  if ( poid1==NULL )
  { psnmpdllreq->varBindList.len=0;
    return(LCRZO_ERR_OK);
  }
  
  /* first oid is set */
  psnmpdllreq->varBindList.len=1;
  ret=SNMP_oidcpy(&(psnmpdllreq->varBindList.list[0].name), poid1);
  if (!ret) return(LCRZO_ERR_FUMALLOC);
  psnmpdllreq->varBindList.list[0].value.asnType=ASN_NULL;
  
  /* second oid is set */
  if ( poid2!=NULL )
  { psnmpdllreq->varBindList.len=2;
    psnmpdllreq->varBindList.list[1].value.asnType=ASN_NULL;
    ret=SNMP_oidcpy(&(psnmpdllreq->varBindList.list[1].name), poid2);
    if (!ret) return(LCRZO_ERR_FUMALLOC);
  }

  /* third oid is set */
  if ( poid3!=NULL )
  { psnmpdllreq->varBindList.len=3;
    psnmpdllreq->varBindList.list[2].value.asnType=ASN_NULL;
    ret=SNMP_oidcpy(&(psnmpdllreq->varBindList.list[2].name), poid3);
    if (!ret) return(LCRZO_ERR_FUMALLOC);
  }

  /* fourth oid is set */
  if ( poid4!=NULL )
  { psnmpdllreq->varBindList.len=4;
    psnmpdllreq->varBindList.list[3].value.asnType=ASN_NULL;
    ret=SNMP_oidcpy(&(psnmpdllreq->varBindList.list[3].name), poid4);
    if (!ret) return(LCRZO_ERR_FUMALLOC);
  }

  /* fifth oid is set */
  if ( poid5!=NULL )
  { psnmpdllreq->varBindList.len=5;
    psnmpdllreq->varBindList.list[4].value.asnType=ASN_NULL;
    ret=SNMP_oidcpy(&(psnmpdllreq->varBindList.list[4].name), poid5);
    if (!ret) return(LCRZO_ERR_FUMALLOC);
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_snmpdllreq_close(lcrzo_priv_snmpdllreq *psnmpdllreq)
{ unsigned int i;

  for ( i=0 ; i<psnmpdllreq->varBindList.len ; i++ )
  { SNMP_FreeVarBind(&(psnmpdllreq->varBindList.list[i]));
  }
  psnmpdllreq->varBindList.len=0;

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_snmpdllreq_print(lcrzo_priv_snmpdllreq *psnmpdllreq)
{ unsigned int i, j;

  printf("varBindList : %d\n", psnmpdllreq->varBindList.len);
  for ( i=0 ; i<psnmpdllreq->varBindList.len ; i++ )
  { /* display name */
    lcrzo_er(lcrzo_priv_AsnOid_print(&(psnmpdllreq->varBindList.list[i].name)));
    /* display value type */
    printf(" Type %d(", psnmpdllreq->varBindList.list[i].value.asnType);
    switch(psnmpdllreq->varBindList.list[i].value.asnType)
    { case ASN_INTEGER: printf("ASN_INTEGER"); break;
      case ASN_OCTETSTRING: printf("ASN_OCTETSTRING"); break;
      case ASN_NULL: printf("ASN_NULL"); break;
      case ASN_OBJECTIDENTIFIER: printf("ASN_OBJECTIDENTIFIER"); break;
      case ASN_SEQUENCE: printf("ASN_SEQUENCE"); break;
      case ASN_RFC1155_IPADDRESS: printf("ASN_RFC1155_IPADDRESS"); break;
      case ASN_RFC1155_COUNTER: printf("ASN_RFC1155_COUNTER"); break;
      case ASN_RFC1155_GAUGE: printf("ASN_RFC1155_GAUGE"); break;
      case ASN_RFC1155_TIMETICKS: printf("ASN_RFC1155_TIMETICKS"); break;
      case ASN_RFC1155_OPAQUE: printf("ASN_RFC1155_OPAQUE"); break;
      default: printf("???"); break;
    }
    printf(")\n");
    /* display the value */
    switch(psnmpdllreq->varBindList.list[i].value.asnType)
    { case ASN_INTEGER:
        printf(" %d\n", psnmpdllreq->varBindList.list[i].value.asnValue.number);
        break;
      case ASN_OCTETSTRING:
        printf("len=%d\n", psnmpdllreq->varBindList.list[i].value.asnValue.string.length);
        lcrzo_data_print(psnmpdllreq->varBindList.list[i].value.asnValue.string.stream,
                         psnmpdllreq->varBindList.list[i].value.asnValue.string.length, LCRZO_PRINTTYPE_DUMP);
        break;
      case ASN_NULL:
        /* nothing to print */
        break;
      case ASN_RFC1155_COUNTER:
        printf(" %lu\n", psnmpdllreq->varBindList.list[i].value.asnValue.counter);
        break;
      case ASN_RFC1155_GAUGE:
        printf(" %lu\n", psnmpdllreq->varBindList.list[i].value.asnValue.gauge);
        break;
      case ASN_RFC1155_TIMETICKS:
        printf(" %lu\n", psnmpdllreq->varBindList.list[i].value.asnValue.ticks);
        break;
      case ASN_OBJECTIDENTIFIER:
        /* display name */
        printf(" ");
        lcrzo_er(lcrzo_priv_AsnOid_print(&(psnmpdllreq->varBindList.list[i].value.asnValue.object)));
        printf("\n");
        break;
      case ASN_RFC1155_IPADDRESS:
        printf(" ");
        for ( j=0 ; j<psnmpdllreq->varBindList.list[i].value.asnValue.address.length ; j++ )
        { printf("%d.", psnmpdllreq->varBindList.list[i].value.asnValue.address.stream[j]); 
        }
        printf("\n");
        break;
      case ASN_SEQUENCE:
      case ASN_RFC1155_OPAQUE:
      default:
        return(LCRZO_ERR_IEUNIMPLEMENTED);
    }
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_snmpdllreq__uint32(lcrzo_priv_snmpdllreq *psnmpdllreq,
                                  lcrzo_uint8 index,
                                  lcrzo_uint32 *pnumber)
{ lcrzo_uint32 number;

  if ( index >= psnmpdllreq->varBindList.len ) return(LCRZO_ERR_POTOOBIG);

  switch(psnmpdllreq->varBindList.list[index].value.asnType)
  { case ASN_INTEGER:
      number=psnmpdllreq->varBindList.list[index].value.asnValue.number;
      break;
    case ASN_RFC1155_COUNTER:
      number=psnmpdllreq->varBindList.list[index].value.asnValue.counter;
      break;
    case ASN_RFC1155_GAUGE:
      number=psnmpdllreq->varBindList.list[index].value.asnValue.gauge;
      break;
    case ASN_RFC1155_TIMETICKS:
      number=psnmpdllreq->varBindList.list[index].value.asnValue.ticks;
      break;
    case ASN_RFC1155_IPADDRESS:
      if (psnmpdllreq->varBindList.list[index].value.asnValue.address.length!=4)
      { number=0;
      }
      else
      { number = (psnmpdllreq->varBindList.list[index].value.asnValue.address.stream[0]<<24) |
                 (psnmpdllreq->varBindList.list[index].value.asnValue.address.stream[1]<<16) |
                 (psnmpdllreq->varBindList.list[index].value.asnValue.address.stream[2]<<8 ) |
                 (psnmpdllreq->varBindList.list[index].value.asnValue.address.stream[3]    ) ;
      }
      break;
    case ASN_OCTETSTRING:
    case ASN_NULL:
    case ASN_OBJECTIDENTIFIER:
    case ASN_SEQUENCE:
    case ASN_RFC1155_OPAQUE:
    default:
      return(LCRZO_ERR_SPSNMPDLLBADTYPE);
  }

  if ( pnumber!=NULL ) *pnumber=number;

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_snmpdllreq__data(lcrzo_priv_snmpdllreq *psnmpdllreq,
                                lcrzo_uint8 index,
                                lcrzo_data *pdata,
                                lcrzo_int32 *pdatasize)
{ 
  if ( index >= psnmpdllreq->varBindList.len ) return(LCRZO_ERR_POTOOBIG);

  switch(psnmpdllreq->varBindList.list[index].value.asnType)
  { case ASN_OCTETSTRING:
      lcrzo_er(lcrzo_data_initm_data(psnmpdllreq->varBindList.list[index].value.asnValue.string.stream,
                                     psnmpdllreq->varBindList.list[index].value.asnValue.string.length,
                                     pdata, pdatasize));
      break;
    case ASN_RFC1155_IPADDRESS:
      lcrzo_er(lcrzo_data_initm_data(psnmpdllreq->varBindList.list[index].value.asnValue.address.stream,
                                     psnmpdllreq->varBindList.list[index].value.asnValue.address.length,
                                     pdata, pdatasize));
      break;
    case ASN_NULL:
    case ASN_INTEGER:
    case ASN_SEQUENCE:
    case ASN_RFC1155_OPAQUE:
    case ASN_RFC1155_COUNTER:
    case ASN_RFC1155_GAUGE:
    case ASN_RFC1155_TIMETICKS:
    default:
      return(LCRZO_ERR_SPSNMPDLLBADTYPE);
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_snmpdllreq__etha(lcrzo_priv_snmpdllreq *psnmpdllreq,
                                lcrzo_uint8 index,
                                lcrzo_etha etha)
{ 
  if ( index >= psnmpdllreq->varBindList.len ) return(LCRZO_ERR_POTOOBIG);

  switch(psnmpdllreq->varBindList.list[index].value.asnType)
  { case ASN_OCTETSTRING:
      if (psnmpdllreq->varBindList.list[index].value.asnValue.string.length==LCRZO_ETHA_MAXBYTES)
      { lcrzo_er(lcrzo_etha_init(psnmpdllreq->varBindList.list[index].value.asnValue.string.stream[0], 
                                 psnmpdllreq->varBindList.list[index].value.asnValue.string.stream[1], 
                                 psnmpdllreq->varBindList.list[index].value.asnValue.string.stream[2], 
                                 psnmpdllreq->varBindList.list[index].value.asnValue.string.stream[3], 
                                 psnmpdllreq->varBindList.list[index].value.asnValue.string.stream[4], 
                                 psnmpdllreq->varBindList.list[index].value.asnValue.string.stream[5], 
                                 etha));
      }
      else
      { lcrzo_er(lcrzo_etha_initdefault(etha));
      }
      break;
    case ASN_RFC1155_IPADDRESS:
    case ASN_NULL:
    case ASN_INTEGER:
    case ASN_SEQUENCE:
    case ASN_RFC1155_OPAQUE:
    case ASN_RFC1155_COUNTER:
    case ASN_RFC1155_GAUGE:
    case ASN_RFC1155_TIMETICKS:
    default:
      return(LCRZO_ERR_SPSNMPDLLBADTYPE);
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_snmpdllreq_getnext(lcrzo_priv_snmpdll *psnmpdll,
                                  lcrzo_priv_snmpdllreq *psnmpdllreq)
{ AsnInteger errorStatus;
  AsnInteger errorIndex;
  int ret;

  ret = (psnmpdll->snmpqueryfunction)(ASN_RFC1157_GETNEXTREQUEST, &(psnmpdllreq->varBindList), &errorStatus, &errorIndex);
  if ( !ret ) return(LCRZO_ERR_FUSNMPEXTENSIONQUERY);
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_snmpdll_printall()
{ lcrzo_priv_snmpdll snmpdll;
  lcrzo_priv_snmpdllreq snmpdllreq;
  AsnObjectIdentifier oid0;
  
  /* load the DLL, and init functions */
  lcrzo_er(lcrzo_priv_snmpdll_init(&snmpdll));

  lcrzo_er(lcrzo_priv_AsnOid_init(&oid0, 1, 3, 6, 1, 2, 1, -1));
  lcrzo_er(lcrzo_priv_snmpdllreq_init(&snmpdllreq, &oid0, NULL, NULL, NULL, NULL));
  lcrzo_er(lcrzo_priv_AsnOid_close(&oid0));
  while ( snmpdllreq.varBindList.list[0].name.idLength<7 || snmpdllreq.varBindList.list[0].name.ids[6] < 10 )
  { lcrzo_er(lcrzo_priv_snmpdllreq_getnext(&snmpdll, &snmpdllreq));
    lcrzo_er(lcrzo_priv_snmpdllreq_print(&snmpdllreq));
  }
  lcrzo_er(lcrzo_priv_snmpdllreq_close(&snmpdllreq));

  /* end */
  lcrzo_er(lcrzo_priv_snmpdll_close(&snmpdll));
  
  return(LCRZO_ERR_OK);
}

#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif
