/*
 * Hugs98 Registry reset app - get rid off the
 * user-specific Hugs98 options settings from the Registry.
 *
 * The reason why this util is needed is that Hugs doesn't 
 * perform any validity checks when persisting the :set'able
 * options to the Registry. As a result, the user might end
 * up inadvertently zapping the search path, causing Hugs 
 * to be unable to locate the Prelude the next time it starts
 * up. Which is undesirable, since it'll then exit before
 * the useris able to get to get to the interpreter prompt
 * & repair the damage done via another :set !!
 *
 * The only way to exit from this unhappy state of affairs
 * is to either be Registry-savy and do the mods 'regedit',
 * or reinstall. So, clearly some room for improvement in
 * usability here.
 *
 * This util is a stop-gap measure which lets the user rid
 * him/herself of the *user-specific* option settings that
 * Hugs will have saved in the Registry. By deleting the
 * user-specific settings, the next time Hugs starts up,
 * it'll default back to using the machine-wide default
 * options. i.e., the user may end up having to go in 
 * and tweak these settings again, but at least Hugs has
 * been brought back into a useable state.
 *
 * To compile this utility, you'll need a Win32 C compiler;
 * here are the options to use with the three compilers I've
 * tested it with:
 *
 *  MSVC:       cl /o restorer restore.c advapi32.lib
 *  gcc:        gcc -mno-cygwin -o restorer restore.c
 *  lcc-win32:  lc restore.c -o main.exe
 * 
 * 7/01 -- sof
 */
#include <windows.h>
#include <malloc.h>
#include <stdio.h>

#define MAX(x,y) ( x > y ? x : y)

#define HUGS_HCU_ROOT "Software\\Haskell\\Hugs"

/* This util inspects the Registry looking for
   existing Hugs installations in the current
   user hive.

   Store each one of the installs found in a table,
   so that we can later on go in and selectively
   reset each one of them.
*/

typedef struct {
  TCHAR*  keyName;
  HKEY    hKey;
} HugsInstall;

typedef struct {
  unsigned int size;
  unsigned int idx;
  HugsInstall* tab;
} InstallTable;

/*
 * Function:  initInstallTable ( unsigned int sz )
 *
 * Purpose:   Allocate a table (of 'sz' elements) that will
 *            hold information about available Hugs installation.
 *
 * Returns:   empty Hugs InstallTable of size 'sz' elements.
 */
InstallTable*
initInstallTable(unsigned int size)
{
  HugsInstall*  tab;
  InstallTable* table;
  
  table = (InstallTable*)malloc(sizeof(InstallTable));
  
  if (table == NULL) {
    return table;
  }

  tab = (HugsInstall*)malloc(size * sizeof(HugsInstall));
  
  if (tab == NULL) {
    free(table);
    return NULL;
  }
  
  table->size = size;
  table->idx  = 0;
  table->tab  = tab;
  
  return table;
}

void
DestroyTable(InstallTable* table)
{
  int i;
  if (!table) 
    return;

  for ( i=0; i < (int)table->idx ; i++ ) {
    free(table->tab[i].keyName);
    RegCloseKey(table->tab[i].hKey);
  }
  
  free(table->tab);
  free(table);
}  

/*
 * Function:  expandTable(InstallTable*)
 *
 * Purpose:   expand the installation table by 50%.
 *            Helper routine used by addInstall().
 *
 * Returns:   boolean indicating whether the expansion
 *            was successful or not.
 */
BOOL
expandTable(InstallTable* table)
{
  HugsInstall* tab;
  unsigned int newSz;
  if (!table) {
    return FALSE;
  }
  
  newSz = table->size + MAX(1, (table -> size / 2));
  tab = (HugsInstall*)realloc(table->tab, sizeof(HugsInstall) * newSz);

  if (tab == NULL) {
    return FALSE;
  }

  table->size = newSz;
  table->tab  = tab;

  return TRUE;
}

BOOL
addHugsInstall(InstallTable* table, char* dirName, HKEY hKey)
{
  char* tmp;
  if (!table)
    return FALSE;

  /* idx points to the next available slot */
  if ( table->idx == table->size ) {
    if ( !expandTable(table) ) {
      return FALSE;
    }
  }
  
  tmp = (char*)malloc(sizeof(char) * (strlen(dirName) + 1));
  
  if (!tmp) 
    return FALSE;
  
  strcpy(tmp, dirName);
  table->tab[table->idx].keyName = tmp;
  table->tab[table->idx].hKey    = hKey;
  
  table->idx += 1;
  
  return TRUE;
}

#define HandleRegError(msg, rc) printf("Error calling %s - return code = 0x%x\n", msg, rc)

void
ResetOption(InstallTable* table, int idx)
{
  LONG lRes;
  
  lRes = RegDeleteValue(table->tab[idx].hKey, "Options");
  printf("Finished resetting %s\n\n", table->tab[idx].keyName);
}

void
HandleDeletion(InstallTable* table)
{
  int i, idx;
  int ch;
  int val;
  char line[10];
  BOOL* delVector;
  int*  idxVector;
  
  printf("Hugs98 Repair Tool\n\n");

  printf("By selecting one of the installations below, you'll reset\n");
  printf("your user-specific options that Hugs98 has saved away\n");
  printf("in the Windows Registry (Resetting just gets you back\n");
  printf("to using the default Hugs options).\n\n");
  
  if (!table || table->idx == 0) {
    printf("\n\n No local Hugs installations found. Exiting\n");
    return;
  }
  
  idxVector=(int*)malloc(sizeof(int) * (table->idx));
  delVector=(int*)malloc(sizeof(BOOL) * (table->idx));

  for (i=0;i < (int)table->idx; i++) {
    delVector[i]=FALSE;
    idxVector[i]=-1;
  }
  do {
    idx = 0;
    printf("Local Hugs98 installations: \n");
    for (i=0; i < (int)table->idx ; i++ ) {
      if (delVector[i]) {
	continue;
      }
      idxVector[idx] = i;
      printf("  %i - %s\n", idx+1, table->tab[i].keyName);
      idx++;
    }
  
    printf("\nInstallation to reset (0 == reset all; ENTER to exit) ? ");
    fflush(stdout);
    if (fgets(line,10,stdin) != NULL) {
      if (line[0] == '\n') {
	free(delVector);
	free(idxVector);
	return;
      }
      val = atoi(line);
      if (val != 0) {
	if ( val > idx ) {
	  printf("Selection, %u, out of range\n", val);
	  continue;
	}
	ResetOption(table, idxVector[val-1]);
	delVector[idxVector[val-1]]=TRUE;
      } else {
	for (i=0; i < table->idx ; i++) {
	  if (!delVector[i]) {
	    ResetOption(table, i);
	  }
	}
	free(delVector);
	free(idxVector);
	break;
      }
    }
  } while (1);

}

int
main()
{
  HKEY hHugsKey;
  HKEY hSubKey;
  LONG lRes;
  TCHAR nameBuf [ 511 ];
  TCHAR nameBuf2 [ 2048 ];
  DWORD dwSize, dwIndex;
  InstallTable* table;
  FILETIME ft;

  lRes = 
    RegOpenKeyEx ( HKEY_CURRENT_USER,
		   HUGS_HCU_ROOT,
		   0,
		   KEY_ALL_ACCESS,
		   &hHugsKey);
		   
  if ( lRes != ERROR_SUCCESS ) {
    HandleRegError("RegOpenKey", lRes);
    return 1;
  }
  
  dwIndex = -1;
  
  table = initInstallTable(2);

  do {
    dwSize = sizeof ( nameBuf );
    dwIndex++;
    lRes = 
      RegEnumKeyEx ( hHugsKey,
		     dwIndex,
		     nameBuf,
		     &dwSize,
		     NULL,
		     NULL,
		     NULL,
		     &ft);
    if ( lRes == ERROR_SUCCESS ||
	 lRes == ERROR_MORE_DATA || 
	 lRes == ERROR_NO_MORE_ITEMS ) {
      strcpy(nameBuf2, HUGS_HCU_ROOT);
      strcat(nameBuf2, "\\");
      strcat(nameBuf2, nameBuf);

      RegOpenKeyEx ( HKEY_CURRENT_USER,
		     nameBuf2,
		     0,
		     KEY_ALL_ACCESS,
		     &hSubKey);
      if ( !addHugsInstall(table, nameBuf, hSubKey) ) {
	break;
      }
    }
  } while ( lRes == ERROR_SUCCESS || 
	    lRes == ERROR_MORE_DATA ); 
  
  if ( lRes != ERROR_MORE_DATA && lRes != ERROR_NO_MORE_ITEMS ) {
    HandleRegError("RegEnumKeyEx", lRes);
    return 0;
  }
  
  HandleDeletion(table);
  
  DestroyTable(table);
  RegCloseKey(hHugsKey);
  
  return 0;
}
