//****************
//
// Name : Tree.c
//
//****************


//******** Header Files

//** OS3.1 Definitions
#include <exec/lists.h>
#include <exec/memory.h>
#include <dos/dosextens.h>

//** OS3.1 function Prototypes
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>

//**OS3.1 Inline code
#include <pragmas/dos_pragmas.h>
#include <pragmas/exec_pragmas.h>

//** ANSI
#include <string.h>

//** Application
#include "Librarian.h"
#include "MPMGClass.h"
#include "Prefs.h"
#include "read.h"
#include "tree.h"

#include "TDEBUG.h"


//******* Local Storage

static struct List Root;
ULONG TreeHeight;
#define TITLE(x) (((struct branch *)x)->Title)

//******** Private sort functions

static struct Node *MergeSortAux(struct Node *n) {
  struct Node *a, *b, *c;

  if (0==n->ln_Succ)
    return n;

  // split into two lists
  c=n;
  b=c->ln_Succ;
  for ( ; b->ln_Succ; ) {
    c=c->ln_Succ;
    b=b->ln_Succ;
    if (b->ln_Succ) {
      b=b->ln_Succ;
    }
  }
  b=c->ln_Succ;
  c->ln_Succ=NULL;

  // sort parts
  a=MergeSortAux(n);
  b=MergeSortAux(b);

  // merge
  if (0>strcmp(TITLE(a),TITLE(b))) {
    c=a;
    a=a->ln_Succ;
  } else {
    c=b;
    b=b->ln_Succ;
  }
  n=c;

  while (a && b) {
    if (0>strcmp(TITLE(a),TITLE(b)) ) {
      c->ln_Succ=a;
      c=a;
      a=a->ln_Succ;
    } else {
      c->ln_Succ=b;
      c=b;
      b=b->ln_Succ;
    }
  }
  if (a) {
    c->ln_Succ=a;
    while (c->ln_Succ) c=c->ln_Succ;
  }
  if (b) {
    c->ln_Succ=b;
    while (c->ln_Succ) c=c->ln_Succ;
  }

  return n;
} // MergeSortAux


static void MergeSort(struct List *list) {
  struct Node *n;
  struct Node *a;

  if (!(IsListEmpty(list))) {
    list->lh_TailPred->ln_Succ=0;
    n=MergeSortAux(list->lh_Head);
    a=(struct Node *)list;
    list->lh_Head=n;

    while (n->ln_Succ) {
      n->ln_Pred=a;
      a=n;
      n=n->ln_Succ;
    };
    n->ln_Pred=a;
    list->lh_TailPred=n;
    n->ln_Succ=(struct Node *)&(list->lh_Tail);
/*
    n=MergeSortAux(list->lh_Head);

    list->lh_Head=n;
    n->ln_Pred=0;
    a=n->ln_Succ;
    while (a->ln_Succ) {
      a->ln_Pred=n;
      n=a;
      a=a->ln_Succ;
    } // while
    list->lh_TailPred=a;
    a->ln_Succ=(struct Node *)&(list->lh_Tail);
*/

  }
} // MergeSort


//******** Public functions

void InitTree(void) {
  TreeHeight=0;
  NewList( &Root );
} // InitTree


struct trunk *GetTop(void) {
  return (struct trunk *)Root.lh_Head;
} // GetTop


BOOL AddLevel(BPTR lock) {
  struct trunk *tr;

  tr=(struct trunk *)AllocVec(sizeof(struct trunk),MEMF_PUBLIC | MEMF_CLEAR | MEMF_REVERSE);
  if (tr) {
    AddHead( &Root, (struct Node *)tr );
    NewList( &(tr->list) );
    if (lock) {
      tr->DirLock=DupLock(lock);
    } else {
      tr->DirLock=NULL;
    }
    TreeHeight++;
    return TRUE;
  }

  DPRINT("\nTree: AddLevel(%d): AllocVec() failed\n",TreeHeight);
  return FALSE;
} // AddLevel


STRPTR AddBranch(STRPTR fn, STRPTR dr) {
  struct branch *br;
  struct trunk *tr;
  struct List *li;
  STRPTR s;

  br=(struct branch *)AllocVec(sizeof(struct branch),MEMF_CLEAR | MEMF_REVERSE | MEMF_PUBLIC);
  if (br) {
    tr= (struct trunk *)(Root.lh_Head);
    li= &(tr->list);
    AddTail( li, (struct Node *)br );
    if (fn) {
      s=(STRPTR)AllocVec(strlen(fn)+1,MEMF_PUBLIC|MEMF_PUBLIC|MEMF_REVERSE);
      if (s) {
	br->Title=strcpy(s,fn);
      } else {
	DPRINT("Tree: AddBranch(): AllocVec()#1 failed\n",0);
      }
    } else {
      DPRINT("Tree: AddBranch(): fn is NULL!\n",0);
    }
    if (dr) {
      s=(STRPTR)AllocVec(strlen(dr)+1,MEMF_PUBLIC|MEMF_PUBLIC|MEMF_REVERSE);
      if (s) {
	br->Data=strcpy(s,dr);
      } else {
	DPRINT("Tree: AddBranch(): AllocVec()#2 failed\n",0);
      }
    } else {
//      DPRINT("Tree: AddBranch(): dr is NULL!\n",0);
    }
    return br->Title;
  }
  DPRINT("Tree: AddBranch(): AllocVec()#0 failed\n",0);
  return NULL;
} // AddBranch


void DeleteLevel(void) {
  struct branch *br;
  struct trunk *tr;
  struct List *li;

  if (Root.lh_Head->ln_Succ) {
    tr=(struct trunk *)RemHead(&Root);
    TreeHeight--;
    //** Now free each branch off this level
    li=&(tr->list);
    while (li->lh_Head->ln_Succ) {
      br=(struct branch *)RemHead(li);
      if (br->Title) FreeVec(br->Title);
      if (br->Data ) FreeVec(br->Data );
      FreeVec(br);
    } // while

    if (tr->DirLock) UnLock(tr->DirLock);
    if (tr->Menu) MPMG_DisposeMenus(tr->Menu);
    FreeVec(tr);
  } else {
    DPRINT("Tree: DeleteLevel(%d): Tree is already axed!\n",TreeHeight);
  }
  return;
} // DeleteLevel


STRPTR FindNthBranch(long n) {
  struct Node *myn;
  struct branch *br;

  for ( myn= ((struct trunk *)(Root.lh_Head))->list.lh_Head ; (myn) && (n--) ; myn=myn->ln_Succ );
  if (br=(struct branch *)myn) {
    return (br->Data?br->Data:br->Title);
  } else {
    DPRINT("Tree: FindNthBranch(): Out on a limb by %d!\n",n);
    return NULL;
  }
} // FindNthNode


ULONG MakeMenuFromTree(void) {
  ULONG MyMenus=NULL;
  long NextLeaf=0;
  struct trunk *tr;
  struct List *li;
  struct branch *br;

  tr=(struct trunk *)(Root.lh_Head);
  tr->Menu=MyMenus;
  li=&(tr->list);
  MergeSort(li);


  if (MyMenus=MPMG_MenuBuildNew()) {
    br=(struct branch *)li->lh_Head;
    while (br->node.ln_Succ) {
      MPMG_MenuBuildAdd( (br->type?MPM_PROM:MPM_ITEM), br->Title, NULL, (APTR)NextLeaf++, NULL, ACT_RUNW );
      DPRINT("Adding %s\n", br->Title);
      br=(struct branch *)br->node.ln_Succ;
    } // while

    MPMG_MenuBuildAdd(MPM_END, NULL, NULL, NULL, NULL, NULL );
  } else {
    DPRINT("Tree: MMFT(): BuildNewMenus() failed\n",0);
  }

  return (MyMenus);
} // MakeMenuFromTree


struct trunk *ScanDir(char *st) {
  struct AnchorPath __aligned ua;
  long temprc;
  BPTR oldlock, curlock, dirlock;
  struct trunk *tr=NULL;
  char *st2;
  struct List *li;

  if (Root.lh_Head->ln_Succ) {
    dirlock=((struct trunk *)(Root.lh_Head))->DirLock;
    if (dirlock) dirlock=CurrentDir(dirlock);
  } else {
    dirlock=NULL;
  }

  DPRINT("Tree: ScanDir(%s)\n",st);
  curlock=Lock(st,SHARED_LOCK);
  if (NULL!=curlock) {
    if (TRUE==AddLevel(curlock)) {
      tr=(struct trunk *)(Root.lh_Head);
      li=&(tr->list);

      oldlock=CurrentDir(curlock);
      memset(&ua, 0, sizeof(struct AnchorPath));
      MatchFirst("~(#?.info)", &ua);
      temprc = IoErr();
      while (0==temprc) {
	st2=AddBranch(ua.ap_Info.fib_FileName,NULL);

	if (ua.ap_Info.fib_DirEntryType>0) {
	  ((struct branch *)(li->lh_TailPred))->type=1;
	} else {
	  ((struct branch *)(li->lh_TailPred))->type=0;
	}
	MatchNext(&ua);
	temprc = IoErr();
      } // while temprc==0
      if (temprc != ERROR_NO_MORE_ENTRIES) {
//        DeleteLevel();
	DPRINT("Tree: ScanDir(): Whoa!  temprc\n",0);
      } // if temprc
      MatchEnd(&ua);
      CurrentDir(oldlock);
    } else {
      DPRINT("Tree: ScanDir(): AddLevel() failed\n",0);
    }
    UnLock(curlock);
  } else {
    DPRINT("Tree: ScanDir(): Cannot locked named dir\n",0);
    if (TRUE==AddLevel(NULL)) {
    }
  }
  DPRINT("Tree: ScanDir done\n",0);

  if (dirlock) CurrentDir(dirlock);

  return (struct trunk *)(Root.lh_Head);
} // ScanDir


struct trunk *ScanVol(void) {
  struct trunk *tr;
  struct List *li;
  struct DosList *dl;
  char buf[256];
  long i;
  char *b;

  if (TRUE==AddLevel(NULL)) {
    tr=(struct trunk *)(Root.lh_Head);
    li=&(tr->list);

    dl=LockDosList(LDF_VOLUMES | LDF_READ);
    while (dl) {
      if (dl->dol_Type==DLT_VOLUME) {
	b=(char *)(4*dl->dol_Name);
	i=*b;
	b++;
	strncpy(buf,b,i);
	buf[i]=':';
	buf[++i]=0;
	AddBranch(buf,NULL);
	((struct branch *)(li->lh_TailPred))->type=1;
      } // if
      dl=NextDosEntry(dl,LDF_VOLUMES | LDF_READ);
    } // while
    UnLockDosList(LDF_VOLUMES | LDF_READ);
  } else {
    DPRINT("Tree: ScanVol(): AddLevel() failed\n",0);
  }

  return (struct trunk *)(Root.lh_Head);
} // ScanVol


//******** End of file

