/* This file is part of Lcrzo.
*/

#ifdef LCRZODEF_SYSTEM_Unix
  /* this function is not needed*/

#elif defined LCRZODEF_SYSTEM_Windows

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_etha_init_device_ndis3(lcrzo_conststring device,
                                      lcrzo_uint32 oidcode,
                                      lcrzo_data *pdata,
                                      lcrzo_int32 *pdatasize);
int lcrzo_priv_conf_etha_init_device_ndis3(lcrzo_conststring device,
                                      lcrzo_uint32 oidcode,
                                      lcrzo_data *pdata,
                                      lcrzo_int32 *pdatasize)
{ lcrzo_string devicename;
  HANDLE thehandle;
  DWORD BytesReturned;
  BOOL err;
  int ret;

  /* create a handle associated with the device */
  lcrzo_er(lcrzo_string_initm_text("\\\\.\\", &devicename));
  lcrzo_er(lcrzo_string_appendm_text(device, &devicename));
  thehandle=CreateFile(devicename, 0, FILE_SHARE_READ,
                       NULL, OPEN_EXISTING, 0, NULL);
  lcrzo_er(lcrzo_string_free(devicename));
  if ( thehandle == INVALID_HANDLE_VALUE ) return(LCRZO_ERR_FUCREATEFILE);

  /* send the request */
  ret=lcrzo_data_alloc(1000, pdata);
  if (ret!=LCRZO_ERR_OK)
  { CloseHandle(thehandle);
    return(ret);
  }
  BytesReturned=0;
  err=DeviceIoControl(thehandle, IOCTL_NDIS_QUERY_GLOBAL_STATS,
                      &oidcode, sizeof(oidcode),
                      *pdata, 999,
                      &BytesReturned, NULL);
  CloseHandle(thehandle);
  if (!err) return(LCRZO_ERR_FUDEVICEIOCONTROL);
  *pdatasize=BytesReturned;

  return( ERROR_SUCCESS );
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_etha_init_device_ndis2(lcrzo_conststring device,
                                      lcrzo_etha etha);
int lcrzo_priv_conf_etha_init_device_ndis2(lcrzo_conststring device,
                                      lcrzo_etha etha)
{ NDIS_MEDIUM mediumtype;
  lcrzo_data data;
  lcrzo_int32 datasize;
  int ret;
   
  /* get medium type */
  ret=lcrzo_priv_conf_etha_init_device_ndis3(device, OID_GEN_MEDIA_IN_USE,
                                        &data, &datasize);
  if (ret!=LCRZO_ERR_OK) return(ret);
  mediumtype = *((PNDIS_MEDIUM)data);
  lcrzo_data_free(data);
  if ( mediumtype != NdisMedium802_3 ) return(LCRZO_ERR_OKUNRESOLVED);

  /* get MAC */
  ret=lcrzo_priv_conf_etha_init_device_ndis3(device, OID_802_3_CURRENT_ADDRESS,
                                        &data, &datasize);
  if (ret!=LCRZO_ERR_OK) return(ret);
  if (datasize!=LCRZO_ETHA_MAXBYTES) return(LCRZO_ERR_OKUNRESOLVED);

  memcpy(etha, data, LCRZO_ETHA_MAXBYTES);
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_etha_init_device_ndis(lcrzo_conststring device,
                                     lcrzo_etha etha);
int lcrzo_priv_conf_etha_init_device_ndis(lcrzo_conststring device,
                                     lcrzo_etha etha)
{ lcrzo_priv_globalvars *ptrglo;
  lcrzo_device dev, dev2;
  int ret;

  /* special case for loopback */
  if (!strcmp(device, "Loopback"))
  { return(lcrzo_etha_initdefault(etha));
  }

  /* only supported on NT, 2k, XP */
  lcrzo_er(lcrzo_priv_globalvars_ptr_get(&ptrglo));
  switch(ptrglo->windows_systemversion)
  { case LCRZO_WINDOWS_SYSTEMVERSION_31:
    case LCRZO_WINDOWS_SYSTEMVERSION_95:
    case LCRZO_WINDOWS_SYSTEMVERSION_98:
    case LCRZO_WINDOWS_SYSTEMVERSION_me:
      return(LCRZO_ERR_OKUNRESOLVED);
  }

  ret=lcrzo_priv_ascii_init_device(device, dev);
  if (ret==LCRZO_ERR_OK)
  { if (strlen(dev)<15) return(LCRZO_ERR_OKUNRESOLVED);
    if (strncmp(dev, "\\Device\\Packet_", 15)) return(LCRZO_ERR_OKUNRESOLVED);
    ret=lcrzo_device_init_noalias(dev+15, dev2);
    if (ret==LCRZO_ERR_OK)
    { ret=lcrzo_priv_conf_etha_init_device_ndis2(dev2, etha);
    }
  }

  return(ret);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_etha_init_device_packetdll2(LPADAPTER padapter,
                                                lcrzo_uint32 oidcode,
                                                lcrzo_data *pdata,
                                                lcrzo_int32 *pdatasize);
int lcrzo_priv_conf_etha_init_device_packetdll2(LPADAPTER padapter,
                                                lcrzo_uint32 oidcode,
                                                lcrzo_data *pdata,
                                                lcrzo_int32 *pdatasize)
{ BOOL err;
  PPACKET_OID_DATA OidData;

  OidData=GlobalAlloc(GMEM_ZEROINIT, sizeof(PACKET_OID_DATA)+1000);
  if (OidData==NULL) return(LCRZO_ERR_FUGLOBALALLOC);
  OidData->Oid=oidcode;
  OidData->Length=1000;
  err=PacketRequest(padapter, FALSE, OidData);
  if (!err)
  { GlobalFree(OidData);
    return(LCRZO_ERR_FUPACKETREQUEST);
  }

  lcrzo_er(lcrzo_data_initm_data(OidData->Data, OidData->Length, pdata, pdatasize));
  GlobalFree(OidData);
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_etha_init_device_packetdll(lcrzo_conststring device,
                                               lcrzo_etha etha);
int lcrzo_priv_conf_etha_init_device_packetdll(lcrzo_conststring device,
                                               lcrzo_etha etha)
{ lcrzo_device dev, dev2;
  lcrzo_data data;
  lcrzo_int32 datasize;
  NDIS_MEDIUM mediumtype;
  LPADAPTER padapter;
  int ret;

  /* special case for loopback */
  if (!strcmp(device, "Loopback"))
  { return(lcrzo_etha_initdefault(etha));
  }

  /* open the adapter */
  lcrzo_er(lcrzo_priv_ascii_init_device(device, dev));
  lcrzo_er(lcrzo_device_init_noalias(dev, dev2));
  padapter=lcrzo_priv_cs_PacketOpenAdapter(dev2);
  if (padapter==NULL) return(LCRZO_ERR_FUPACKETOPENADAPTER);

  /* get medium type */
  ret=lcrzo_priv_conf_etha_init_device_packetdll2(padapter, OID_GEN_MEDIA_IN_USE,
                                                  &data, &datasize);
  if (ret!=LCRZO_ERR_OK)
  { lcrzo_priv_cs_PacketCloseAdapter(padapter);
    return(ret);
  }
  mediumtype = *((PNDIS_MEDIUM)data);
  lcrzo_data_free(data);
  if ( mediumtype != NdisMedium802_3 )
  { lcrzo_priv_cs_PacketCloseAdapter(padapter);
    return(LCRZO_ERR_OKUNRESOLVED);
  }

  /* get MAC */
  ret=lcrzo_priv_conf_etha_init_device_packetdll2(padapter, OID_802_3_CURRENT_ADDRESS,
                                                  &data, &datasize);
  if (ret!=LCRZO_ERR_OK)
  { lcrzo_priv_cs_PacketCloseAdapter(padapter);
    return(ret);
  }
  /* can not check size because Oid->Length is not set by PaquetRequest under Windows 9x
  if (datasize!=LCRZO_ETHA_MAXBYTES)
  { lcrzo_priv_cs_PacketCloseAdapter(padapter);
    return(LCRZO_ERR_OKUNRESOLVED);
  }
  */
  memcpy(etha, data, LCRZO_ETHA_MAXBYTES);

  lcrzo_priv_cs_PacketCloseAdapter(padapter);
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_etha_init_cardnumber_netbios(lcrzo_int32 devicenumber,
                                            lcrzo_int32 cardnumber,
                                            lcrzo_etha etha);
int lcrzo_priv_conf_etha_init_cardnumber_netbios(lcrzo_int32 devicenumber,
                                            lcrzo_int32 cardnumber,
                                            lcrzo_etha etha)
{ LANA_ENUM AdapterList;
  NCB Ncb;
  int ret;
  struct ASTAT {
    ADAPTER_STATUS adapt;
    NAME_BUFFER NameBuff[30];
  } Adapter;
  lcrzo_priv_globalvars *ptrglo;
  lcrzo_int32 number;

  /* signification depends on systems */
  lcrzo_er(lcrzo_priv_globalvars_ptr_get(&ptrglo));
  switch(ptrglo->windows_systemversion)
  { case LCRZO_WINDOWS_SYSTEMVERSION_95:
    case LCRZO_WINDOWS_SYSTEMVERSION_98:
    case LCRZO_WINDOWS_SYSTEMVERSION_me:
      number=devicenumber;
      break;
    case LCRZO_WINDOWS_SYSTEMVERSION_nt4:
    case LCRZO_WINDOWS_SYSTEMVERSION_2k:
    case LCRZO_WINDOWS_SYSTEMVERSION_xp:
    case LCRZO_WINDOWS_SYSTEMVERSION_net:
      number=cardnumber;
      break;
    default:
      number=cardnumber;
      break;
  }

  /* check if the number is ok */
  memset(&Ncb, 0, sizeof(NCB));
  Ncb.ncb_command = NCBENUM;
  Ncb.ncb_buffer = (unsigned char *)&AdapterList;
  Ncb.ncb_length = sizeof(AdapterList);
  ret=Netbios(&Ncb);
  if ( ret != NRC_GOODRET ) return(LCRZO_ERR_OKUNRESOLVED);
  if ( number > AdapterList.length ) return(LCRZO_ERR_OKUNRESOLVED);
  number=AdapterList.lana[number-1];
  /* number now contains the wanted interface number */

  /* reset the adapter (must be done in order to query it) */
  memset(&Ncb, 0, sizeof(Ncb));
  Ncb.ncb_command = NCBRESET;
  Ncb.ncb_lana_num = (lcrzo_uint8)number;
  ret=Netbios(&Ncb);
  if ( ret != NRC_GOODRET ) return(LCRZO_ERR_OKUNRESOLVED);

  /* get the address */
  memset(&Ncb, 0, sizeof(Ncb));
  Ncb.ncb_command = NCBASTAT;
  Ncb.ncb_lana_num = (lcrzo_uint8)number;
  strcpy((char *) Ncb.ncb_callname, "*");
  memset(&Adapter, 0, sizeof(Adapter));
  Ncb.ncb_buffer = (unsigned char *)&Adapter;
  Ncb.ncb_length = sizeof(Adapter);
  ret=Netbios(&Ncb);
  if ( ret != NRC_GOODRET ) return(LCRZO_ERR_OKUNRESOLVED);

  /* set etha */
  lcrzo_etha_init(Adapter.adapt.adapter_address[0], Adapter.adapt.adapter_address[1],
                  Adapter.adapt.adapter_address[2], Adapter.adapt.adapter_address[3],
                  Adapter.adapt.adapter_address[4], Adapter.adapt.adapter_address[5],
                  etha);
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_etha_init_cardnumber_uuidcreate(lcrzo_int32 number,
                                               lcrzo_etha etha);
int lcrzo_priv_conf_etha_init_cardnumber_uuidcreate(lcrzo_int32 number,
                                               lcrzo_etha etha)
{ UUID uuid;
  RPC_STATUS err;
  lcrzo_priv_globalvars *ptrglo;
  lcrzo_data uuidstr;
  lcrzo_eths eths;
  int ret, i, j;

  ret=LCRZO_ERR_OKUNRESOLVED;
  if ( number == 1 )
  { lcrzo_er(lcrzo_priv_globalvars_ptr_get(&ptrglo));
    switch(ptrglo->windows_systemversion)
    { case LCRZO_WINDOWS_SYSTEMVERSION_2k:
      case LCRZO_WINDOWS_SYSTEMVERSION_xp:
      case LCRZO_WINDOWS_SYSTEMVERSION_net:
        /* we should use UuidCreateSequential, but this function is not
           available in standard libraries of Visual C++ 6 */
        return(LCRZO_ERR_OKUNRESOLVED);
        break;
      case LCRZO_WINDOWS_SYSTEMVERSION_me:
        /* on this system, id created is random */
        return(LCRZO_ERR_OKUNRESOLVED);
        break;
      case LCRZO_WINDOWS_SYSTEMVERSION_95:
      case LCRZO_WINDOWS_SYSTEMVERSION_98:
      default:
        err=UuidCreate(&uuid);
    }
    if (err==RPC_S_OK)
    { err=UuidToString(&uuid, &uuidstr);
      if (err==RPC_S_OK)
      { /*printf("uu=%s\n", uuidstr);*/
        if ( strlen((lcrzo_string)uuidstr) >= 12 )
        { i=strlen((lcrzo_string)uuidstr)-12;
          j=0;
          eths[j++]=uuidstr[i++]; eths[j++]=uuidstr[i++]; eths[j++]=':';
          eths[j++]=uuidstr[i++]; eths[j++]=uuidstr[i++]; eths[j++]=':';
          eths[j++]=uuidstr[i++]; eths[j++]=uuidstr[i++]; eths[j++]=':';
          eths[j++]=uuidstr[i++]; eths[j++]=uuidstr[i++]; eths[j++]=':';
          eths[j++]=uuidstr[i++]; eths[j++]=uuidstr[i++]; eths[j++]=':';
          eths[j++]=uuidstr[i++]; eths[j++]=uuidstr[i++]; eths[j++]='\0';
          /*lcrzo_eths_print("eths=", eths, "\n");*/
          ret=lcrzo_etha_init_eths(eths, etha);
        }
        RpcStringFree(&uuidstr);
      }
    }
  }

  return(ret);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_etha_init_cardnumber_winreg(lcrzo_int32 number,
                                           lcrzo_etha etha);
int lcrzo_priv_conf_etha_init_cardnumber_winreg(lcrzo_int32 number,
                                           lcrzo_etha etha)
{ lcrzo_data ethaddress;
  lcrzo_int32 ethaddresssize;
  lcrzo_priv_globalvars *ptrglo;
  int ret;

  /* only supported on NT */
  lcrzo_er(lcrzo_priv_globalvars_ptr_get(&ptrglo));
  if (ptrglo->windows_systemversion!=LCRZO_WINDOWS_SYSTEMVERSION_nt4)
  { return(LCRZO_ERR_OKUNRESOLVED);
  }

  if ( number == 1 )
  { ret=lcrzo_priv_winreg_data_get(HKEY_LOCAL_MACHINE, "SOFTWARE\\Description\\Microsoft\\Rpc\\UuidTemporaryData", "NetworkAddress", &ethaddress, &ethaddresssize);
    if (ret==LCRZO_ERR_OK)
    { if ( ethaddresssize==6 )
      { lcrzo_etha_init(ethaddress[0], ethaddress[1], ethaddress[2], ethaddress[3], ethaddress[4], ethaddress[5], etha);
      }
      lcrzo_data_free(ethaddress);
      return(LCRZO_ERR_OK);
    }
  }
  return(LCRZO_ERR_OKUNRESOLVED);
}

/*-------------------------------------------------------------*/
/*
 Example :
   device,  devicenumber,  cardnumber,  isanalias
   a        1              1            LCRZO_FALSE
   b        2              2            LCRZO_FALSE
   b:1      3              2            LCRZO_TRUE
   b:2      4              2            LCRZO_TRUE
   c        5              3            LCRZO_FALSE
*/
int lcrzo_priv_conf_etha_init_cardnumberdevice(lcrzo_device device,
                                          lcrzo_int32 devicenumber,
                                          lcrzo_int32 cardnumber,
                                          lcrzo_bool isanalias,
                                          lcrzo_etha etha);
int lcrzo_priv_conf_etha_init_cardnumberdevice(lcrzo_device device,
                                          lcrzo_int32 devicenumber,
                                          lcrzo_int32 cardnumber,
                                          lcrzo_bool isanalias,
                                          lcrzo_etha etha)
{ int ret;

  /* do not try with SNMP because suppose SNMP was previously tried without success */
  /* do not try with IPHLPAPI because suppose it was previously tried without success */
  /* try with packet.dll */
  ret=lcrzo_priv_conf_etha_init_device_packetdll(device, etha);
  if (ret==LCRZO_ERR_OK) return(LCRZO_ERR_OK);
  /* try with NDIS */
  ret=lcrzo_priv_conf_etha_init_device_ndis(device, etha);
  if (ret==LCRZO_ERR_OK) return(LCRZO_ERR_OK);
  /* try with NetBIOS */
  ret=lcrzo_priv_conf_etha_init_cardnumber_netbios(devicenumber, cardnumber, etha);
  if (ret==LCRZO_ERR_OK) return(LCRZO_ERR_OK);
  /* try with UUIDCreate */
  ret=lcrzo_priv_conf_etha_init_cardnumber_uuidcreate(cardnumber, etha);
  if (ret==LCRZO_ERR_OK) return(LCRZO_ERR_OK);
  /* try with Windows registery RPC value */
  ret=lcrzo_priv_conf_etha_init_cardnumber_winreg(cardnumber, etha);
  if (ret==LCRZO_ERR_OK) return(LCRZO_ERR_OK);

  if (ret!=LCRZO_ERR_OK)
  { lcrzo_etha_initdefault(etha);
  }

  return(LCRZO_ERR_OKUNRESOLVED);
  isanalias=isanalias;
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_etha_init_cardnumber_debug_fprint(LCRZOFILE *pf);
int lcrzo_priv_conf_etha_init_cardnumber_debug_fprint(LCRZOFILE *pf)
{ lcrzo_int32 i;
  lcrzo_etha etha;
  int ret;

  fprintf(pf, "\n\nDisplay etha of devices 0 to 10 with lcrzo_priv_conf_etha_init_cardnumberdevice_netbios\n");
  for ( i=0 ; i<=10 ; i++ )
  { ret=lcrzo_priv_conf_etha_init_cardnumber_netbios(i, 0, etha);
    fprintf(pf, "i=%d\t", i);
    if (ret==LCRZO_ERR_OK)
    { lcrzo_etha_fprint(pf, "etha=", etha, "\n");
    }
    else
    { lcrzo_err_fprint(pf, ret);
    }
  }

  fprintf(pf, "\n\nDisplay etha of cards 0 to 10 with lcrzo_priv_conf_etha_init_cardnumberdevice_netbios\n");
  for ( i=0 ; i<=10 ; i++ )
  { ret=lcrzo_priv_conf_etha_init_cardnumber_netbios(0, i, etha);
    fprintf(pf, "i=%d\t", i);
    if (ret==LCRZO_ERR_OK)
    { lcrzo_etha_fprint(pf, "etha=", etha, "\n");
    }
    else
    { lcrzo_err_fprint(pf, ret);
    }
  }

  fprintf(pf, "\n\nDisplay etha of cards 0 to 3 with lcrzo_priv_conf_etha_init_cardnumberdevice_uuidcreate\n");
  for ( i=0 ; i<=3 ; i++ )
  { ret=lcrzo_priv_conf_etha_init_cardnumber_uuidcreate(i, etha);
    fprintf(pf, "i=%d\t", i);
    if (ret==LCRZO_ERR_OK)
    { lcrzo_etha_fprint(pf, "etha=", etha, "\n");
    }
    else
    { lcrzo_err_fprint(pf, ret);
    }
  }

  fprintf(pf, "\n\nDisplay etha of cards 0 to 3 with lcrzo_priv_conf_etha_init_cardnumberdevice_winreg\n");
  for ( i=0 ; i<=3 ; i++ )
  { ret=lcrzo_priv_conf_etha_init_cardnumber_winreg(i, etha);
    fprintf(pf, "i=%d\t", i);
    if (ret==LCRZO_ERR_OK)
    { lcrzo_etha_fprint(pf, "etha=", etha, "\n");
    }
    else
    { lcrzo_err_fprint(pf, ret);
    }
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_conf_etha_init_device_debug_fprint(LCRZOFILE *pf);
int lcrzo_priv_conf_etha_init_device_debug_fprint(LCRZOFILE *pf)
{ lcrzo_priv_confvars *ptrcv;
  lcrzo_priv_conf_devices cd;
  lcrzo_int32 count, i;
  lcrzo_etha etha;
  int ret;

  fprintf(pf, "\n\nFor each device in this table, obtain etha with \nlcrzo_priv_conf_etha_init_device_ndis\n");
  lcrzo_er(lcrzo_priv_confvars_ptr_get(&ptrcv));
  lcrzo_er(lcrzo_list_count(&ptrcv->list_system_devices, &count));
  for ( i=1 ; i<=count ; i++ )
  { lcrzo_er(lcrzo_list_value_pos(&ptrcv->list_system_devices, i, &cd));
    ret=lcrzo_priv_conf_etha_init_device_ndis(cd.device, etha);
    lcrzo_device_fprint(pf, "device=", cd.device, "\t");
    if (ret==LCRZO_ERR_OK)
    { lcrzo_etha_fprint(pf, "etha=", etha, "\n");
    }
    else
    { lcrzo_err_fprint(pf, ret);
    }
  }

  fprintf(pf, "\n\nFor each device in this table, obtain etha with \nlcrzo_priv_conf_etha_init_device_packetdll\n");
  lcrzo_er(lcrzo_priv_confvars_ptr_get(&ptrcv));
  lcrzo_er(lcrzo_list_count(&ptrcv->list_system_devices, &count));
  for ( i=1 ; i<=count ; i++ )
  { lcrzo_er(lcrzo_list_value_pos(&ptrcv->list_system_devices, i, &cd));
    ret=lcrzo_priv_conf_etha_init_device_packetdll(cd.device, etha);
    lcrzo_device_fprint(pf, "device=", cd.device, "\t");
    if (ret==LCRZO_ERR_OK)
    { lcrzo_etha_fprint(pf, "etha=", etha, "\n");
    }
    else
    { lcrzo_err_fprint(pf, ret);
    }
  }

  return(LCRZO_ERR_OK);
}

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