#include "tx.h"
#include <dos.h>

char *FName[MAX_FILES_IN_DIR] ;
int num;

void TxDir(void)
{
  struct ffblk FFind ;
  int i,j,selection,memidx=0,baddrv=0 ;
  int row,col,oldrive,DosDrives,DrivesShown=0 ;
  char *TextInWindow , *TextInLine, NameSel[15], NewPath[MAXPATH] ;
  char *FMem[1+(MAX_FILES_IN_DIR/40)];
  void krqsort (int,int);
  int ShowFiles(void) ;
  void AppendStars(char *) ;   /* apppend *.* to path */
  int StripStars(char *) ;
  int DriveExists[26];
  void TestDrives(int *,int *,int *);
  extern void DrawFrame(int,int,int,int,int);
  
  oldrive = CurrDrive ;
  strcpy(NewPath,Path) ;
  AppendStars(NewPath) ;

  for (i=0;i<26;i++)   /* Initialize */
    DriveExists[i]=0;

  TestDrives(DriveExists,&DosDrives,&DrivesShown);
  --DrivesShown;

   row = wherey() ; col = wherex() ; /* Save cursor position */
  _setcursortype(_NOCURSOR) ;       /* Hide cursor */
  
  TextInWindow = malloc(757);
  gettext(3,3,20,23,TextInWindow);
  TextInLine = malloc(161);
  gettext(1,2,80,2,TextInLine);
  
  window(3,3,20,23) ;              /* L T R B */
  clrscr() ;                       /* Clear text in window area */
  window(1,1,80,25) ;              /* But work in absolute coordinates */
  DrawFrame(3,3,20,23,0) ;         /* Draw window frame (single) */

  do {  /* Start of main loop */

    if ( DrivesShown )
      FName[0]=FMem[0]=malloc(5*DrivesShown);

    for ( i=1 ; i < DrivesShown ; i++ )
      FName[i] = FName[i-1] + 5 ;

    for ( i=0,j=0 ; i < DrivesShown ; i++ ) {
      strcpy(FName[i],"<A:>") ;
      for ( ; (!DriveExists[i+j])||((i+j)==CurrDrive) ; j++) ;
      *(FName[i]+1) = i + j + 'A' ;
    }
    /* Start mem index at 0 if only 1 drive, 1 otherwise */
    memidx = DrivesShown ? 1 : 0 ;
    i = findfirst(NewPath,&FFind,FA_DIREC) ;

    for ( num=DrivesShown ;
	 (i==0) && (num < MAX_FILES_IN_DIR) ; num++ ) {
      if ( ( (num-DrivesShown) % 40) == 0 ) {
	FName[num] = FMem[memidx++] = malloc(600);
	for ( j = 1 ; j < 40 ; j++ )
	  FName[num+j] = FName[num] + (j * 15) ;
      }
      if ( FFind.ff_attrib & FA_DIREC ) {
	*(FName[num]) = '[' ; /* Enclose directories in brackets */
	strcpy(FName[num]+1,FFind.ff_name) ;
	strcat(FName[num],"]") ;
      }
      else
	strcpy(FName[num],FFind.ff_name) ;
      i = findnext(&FFind) ;
    }
   /* num contains number of files in directory + actual logical drives */
   /* memidx contains de number of times malloc has been called */

   /* We now have all names in FName. Drives are enclosed in <>
      and directories in []. Drives are already ordered, so we
      may forget about them. Directories and files will be sorted
      separately, namely, the [ will precede other characters in
      the lexicographic order we use. To help avoiding problems
      in case of a future change, we also make < the character
      with the highest precedence. */

   /* Sorting procedure borrowed from K&R */
    krqsort(DrivesShown,num-1);

    puttext(1,2,80,2,TextInLine) ;
    gotoxy(4,2) ;
    cputs(NewPath) ;
    if ( (selection = ShowFiles()) == -1 )
      break ;
    strcpy(NameSel,FName[selection]) ;
    for ( j=0 ; j < memidx ; j++ )
      free(FMem[j]) ;
    if (!( ( *NameSel == '[' ) || ( *NameSel == '<' )) )
      break ;        /* A file has been selected */
    if (!strcmp(NameSel,"[.]") )  /* current directory */
      continue ; /* reread current directory */
    i = StripStars(NewPath) ;
    if ( !strcmp(NameSel,"[..]") ) { /* parent directory */
      for (; (i-3) && (NewPath[i] != BACKSLASH );i-- ) ;
      NewPath[i] = '\0' ;
      chdir(NewPath) ;
    }
   else if ( *NameSel == '[' ) { /* Another directory */
     if ( i-3 )
       NewPath[i++] = BACKSLASH ;
     for (j=0 ; (NewPath[i]=NameSel[++j])!=']';i++);
     NewPath[i]='\0' ; /* get rid of terminating ] */
     chdir(NewPath) ;
   }
   else {   /* Now  NameSel[0] equals '<', so we change drive */
     CurrDrive = ( (int) NameSel[1] ) - 'A' ;
     setdisk(CurrDrive) ; /* set disk */
     NewPath[0]='\0';
     if ( getcwd(NewPath,MAXPATH)==NULL ) {
          baddrv = 1 ;
          selection = -1 ;
          break;
	}
   }
    AppendStars(NewPath) ;
    
  } while (1) ;   /* End of main loop */
  

   /* Restore settings */
  puttext(3,3,20,23,TextInWindow) ;
  free(TextInWindow) ;
  puttext(1,2,80,2,TextInLine) ;
  free(TextInLine) ;
  _setcursortype(_NORMALCURSOR) ;      /* show cursor */
  gotoxy(col,row) ;                    /* restore cursor position */
  
  if (selection == -1 ) { /* Leave everything as before */
    if ( !baddrv )
      for ( j=0 ; j < memidx ; j++ )
	free(FMem[j]) ;
    setdisk(oldrive) ;
    CurrDrive = oldrive ;
    chdir(Path) ;
    return ;
  }
  
  i=StripStars(NewPath) ;
  strcpy(Path,NewPath) ;
  if ( Path[--i] != BACKSLASH )
    Path[++i] = BACKSLASH;
  for (j=0 ; ( Path[++i] = NameSel[j++] ) ; );
  SplitPath();
}



int ShowFiles(void)
{
  int i,j,init=0,last ;
  int previous=0, current = 0 ;
  
  while ( previous != -1 ) {
    previous = 0 ;
    last = ( init + 19 < num ) ? init + 18 : num - 1 ;
    window(5,4,18,22) ;
    clrscr() ;
    window(1,1,80,25) ;
    
    for ( i = init ; i <= last ; i++ ) {
      gotoxy(5,4+(i-init)) ;
      cputs(FName[i]) ;
    }
    
    while ( previous >= 0 )  {
      while (1) { /* Highlight currently selected entry */
	textattr(color[(previous == current)]) ;
	gotoxy(5,4+previous) ;
	cputs(FName[init+previous]) ;
	j = 14 - strlen(FName[init+previous]) ;
	while ( j-- )
	  putch(' ') ;
	if ( previous == current ) break ;
	previous = current ;
      }
      textattr(color[0]) ;
      i = getch();        /* Read character from keyboard */
      if ( i == 0 ) {     /* If extended character */
	i = getch() ;  /* Get character */
	i *= -1 ;      /* Make it different from regular ascii */
      }
      switch ( i ) {
      case CR :
	previous = -1 ;  /* Choose current entry */
	break ;
      case HOMEKEY :
	if ( init > 0 ) {
	  init = 0 ;
	  previous = -2 ;
	}
	current = 0 ;  /* Go to first entry */
	break ;
      case ENDKEY :
	last = num-1 ; /* Go to last entry */
	previous = -2 ;
	init = ( num > 18 ) ? last - 18 : 0 ;
	current = last - init ;
	break ;
      case UPKEY :
      case LEFTKEY :
	if ( current )
	  --current ;  /* Move up one position */
	else if (init) {
	  --init ;   /* Scroll back one position */
	  previous = -2 ;
	}
	break ;
      case DOWNKEY :
      case RIGHTKEY :
	if ( init+current < last )
	  ++current ;  /* Move down one position */
	else if ( last+1 < num ) {
	  ++init ;        /* Scroll forward one position */
	  previous = -2 ;
	}
	break ;
      case PGUPKEY :
	if (init==0)
	  current = 0;
	else if (init > 17 ) {
	  init -= 18 ;
	  /* current = 18 ; Leave current as it is now */
	  previous = -2 ;
	}
	else {
	  current = init ;
	  init = 0 ;
	  previous = -2 ;
	}
	break ;
      case PGDNKEY :
	if ( last+1 >= num )
	  current = last - init ;
	else {
	  init = last ;
	  if ( last + current > num - 1 )
	    current = num - (last+1) ; 
	  previous = -2 ;
	}
	break ;
      default :
	previous = -1 ;
	current = -1 ;
	break ;
      }  /* switch (i) */
    }    /* while (previous >= 0) */
  } /* while (previous != -1) */
  if ( current == -1 )
    return -1 ;
  else
    return init+current ;
}


void krqsort (int krleft, int krright)
{
  int kri,krlast;
  void krswap(int,int) ;
  int krcomp(char *,char *);
  
  if (krleft >= krright )
    return;
  krswap(krleft,(krleft+krright)/2);
  krlast=krleft;
  for (kri=krleft+1;kri<=krright;kri++)
    if (krcomp(FName[kri],FName[krleft])<0)
      krswap(++krlast,kri);
  krswap(krleft,krlast);
  krqsort(krleft,krlast-1);
  krqsort(krlast+1,krright);
}

void krswap(int ki,int kj)
{
  char *temp;
  temp=FName[ki]; FName[ki]=FName[kj];FName[kj]=temp;
}

/* Lexicographical comparison, but giving priority to
   the drive symbols "<" and to the directory symbols "[". */

int krcomp(char *a, char *b)
{
  for ( ; *a == *b ; a++, b++ )
    if ( *a == '\0' )
      return 0 ;
  if ( *a == '<' ) return -1 ;
  else if ( *b == '<' ) return 1 ;
  else if ( *a == '[' ) return -1 ;
  else if ( *b == '[' ) return 1 ;
  else return *a - *b ;
}


void TestDrives(int *DriveExists,int *DosDrives, int *ValidDrives)
{
  int i;
  union REGS r;

  *DosDrives=setdisk(CurrDrive);
  *ValidDrives=0;
  
  for ( i=0; i< *DosDrives; i++) {
    setdisk(i);
    DriveExists[i] = ( i == getdisk() ) ;
    if (DriveExists[i]) {
      r.x.ax = 0x440E;
      r.x.bx = 0;
      intdos(&r,&r);
      if ((!( r.x.flags & 1 )) && (r.x.ax & 0xFF)&&
	  (r.x.ax & 0xFF) != i+1 )
	DriveExists[i]= 0; /* Drive already referenced */
    }
    *ValidDrives += DriveExists[i];
  }
  setdisk(CurrDrive) ; /* Restore current disk after tests */
}


void AppendStars(char *Pname)
{
  for (; *Pname ; Pname++) ; /* go to end of string */
  if (*--Pname != BACKSLASH ) /* make sure last char is a backslash */
    *++Pname = BACKSLASH ;
  *++Pname = '*' ;
  *++Pname = '.' ;
  *++Pname = '*' ;
  *++Pname = '\0' ;
}

int StripStars(char *Pname)
{   
  int i;
  for (i=0; *Pname!='*' ; i++,Pname++) ;
  --Pname; /* Point to backslash */
  if (*--Pname == ':' && ++i )
    ++Pname;
  *++Pname = '\0' ;
  return --i;
}
