/*  Functions for creating and manipulating the list of memos
 *
 *  Functions in this file are :-
 *
 *	struct MinList *LoadData()  \   Not yet included/complete
 *	int SaveData()              /
 *	struct MinNode *MoveBackOne(struct MinNode *p)
 *	struct MinNode *MoveBackBlock(struct MinNode *p)
 *	struct MinNode *MoveEnd(struct MinNode *p)
 *	struct MinNode *MoveFwdOne(struct MinNode *p)
 *	struct MinNode *MoveFwdBlock(struct MinNode *p)
 *	void DisplayBlock()
 *	void Toggle(int t_gadget)
 *
 *	    (to the memo currently top of the display)
 *  The pointer ^ is generally held as a pointer to a MinNode structure since
 *    this makes the code clearer and more efficient(?) since it does not
 *    need to be cast into this form from the Memo_In_Mem form all over the
 *    place.
 *
 *  Use them like so:-
 *    struct MinNode *x,*DisplayFirst;
 *    x=MoveFwdBlock(DisplayFirst);
 *    if (x != DisplayFirst)
 *	{
 *	DisplayFirst=x;
 *	DisplayBlock(DisplayFirst);
 *	}
 */

#include <string.h>
#include <lists.h>
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>

#include <clib/alib_protos.h>

#include "mm2.h"
#include <stdio.h>

/* Prototypes */

Prototype struct MinList *LoadData(void);
Prototype int SaveData(void);
Prototype struct MinNode *MoveBackOne(struct MinNode *);
Prototype struct MinNode *MoveBackBlock(struct MinNode *);
Prototype struct MinNode *MoveFwdOne(struct MinNode *);
Prototype struct MinNode *MoveFwdBlock(struct MinNode *);
Prototype void Display_Block(struct MinNode *);
Prototype void Toggle(int);
Prototype struct MinNode *MoveEnd(struct MinNode *);

extern struct MinNode *DisplayFirst;
extern struct Remember *RK;
extern struct MinList *MemListPtr;

/* ==========================================================================
 * LoadData() - version using exec list handling
 */

struct MinList *LoadData()
  {
  FILE *fp;
  struct MinList *mlp;
  struct Memo_Item mi;
  struct MI_Mem *p;
  int OK;

  mlp=(struct MinList *)AllocRemember(&RK,sizeof(struct MinList),MEMF_PUBLIC);
  if (mlp == NULL) return mlp;  /* Failed. No memory to start list  */
  NewList((struct List *)mlp);
  fp=fopen("PROGDIR:memodata.dat","r");

  if (fp == NULL ) return mlp;  /* File isn't there. Return empty list */
  OK=fread((char *)&mi,sizeof(struct Memo_Item),1,fp);
  while (OK)
    {
    p=(struct MI_Mem *)AllocRemember(&RK,sizeof(struct MI_Mem),MEMF_PUBLIC);
    if (p == NULL)
      {
      /* Take some ABORT!!!! type action here due to loss of memory */
      }
    memcpy((char *)&(p->mim_MI),(char *)&mi,sizeof(struct Memo_Item) );
    p->mim_Select = 0;	/*  All memos start off deselected  */
    p->mim_MI.mi_Text[60]='\0';
    AddTail((struct List *)mlp,(struct Node *)p);
    OK=fread((char *)&mi,sizeof(struct Memo_Item),1,fp);
    }
  fclose(fp);
  return mlp;
  }

/*=====================================================================
 * SaveData()
 *
 * Writes structures in list to file memodata.dat . Previous version of
 *   memodata.dat is renamed .bak
 *
 * !!!!!!! Currently no error checking !!!!!!!
 */
SaveData()
  {
  FILE *fp;
  int Save_Status=1;	 /* Assume success */
  int errr;
  struct MinNode *n;
  struct MI_Mem *m;

  errr=remove("PROGDIR:memodata.bak");
  errr=rename("PROGDIR:memodata.dat","PROGDIR:memodata.bak");
  if (LISTEMPTY) return Save_Status;
  fp=fopen("PROGDIR:memodata.dat","w");
  n=MemListPtr->mlh_Head;
  while (n->mln_Succ)
    {
    m=(struct MI_Mem *)n;
    errr=fwrite((char *)&(m->mim_MI),sizeof(struct Memo_Item),1,fp);
    n = n->mln_Succ;
    }
  fclose(fp);
  return Save_Status;
  }



/*  =========================================================================
 *  Move the 'current' memo back one
 */
struct MinNode *MoveBackOne(struct MinNode *p)
  {
  struct MinNode *n;

  if (n = GetPred(p))
    return n;
  else
    return p;
  }

/*  =========================================================================
 *Move the 'current' memo back a block
 */

struct MinNode *MoveBackBlock(struct MinNode *p)
  {
    struct MinNode *n;
    int num;

    n = p;
    num = MEMOS_IN_BLOCK-1;
    while (num)
    {
      if (n)
        n = GetPred(n);
      num--;
    }
    if (n)
      return n;
    else
      return GetHead(MemListPtr);
  }

/*=========================================================================
 * Move the 'current' memo forward one. More complicated than at first apparent
 *   because the current pointer is the one at the start of the block and the
 *   end of the list has to be checked relative to the last in the block.
 *   Actually move the current pointer MEMOS_IN_BLOCK+1 forward (or until
 *   the end of the list) and then MEMOS_IN_BLOCK backward (or the number
 *   moved forwards if smaller)  */

struct MinNode *MoveFwdOne(struct MinNode *p)
  {
  int cf;

  cf=0;
  while ((cf<MEMOS_IN_BLOCK+1) &&  (p->mln_Succ))
    {
    p=p->mln_Succ;
    cf++;
    }
  cf=MIN(cf,MEMOS_IN_BLOCK);
  while (cf > 0)
    {
    p=p->mln_Pred;
    cf--;
    }
  return p;
  }

/*=========================================================================
 * Move the 'current' memo forward one block.
 *   Actually move the current pointer MEMOS_IN_BLOCK*2 forward (or until
 *   the end of the list) and then MEMOS_IN_BLOCK backward (or the number
 *   moved forwards if smaller)  */

struct MinNode *MoveFwdBlock(struct MinNode *p)
  {
  int cf;

  cf=0;
  while ((cf < MEMOS_IN_BLOCK+MEMOS_IN_BLOCK) && (p->mln_Succ))
    {
    p=p->mln_Succ;
    cf++;
    }
  cf=MIN(cf,MEMOS_IN_BLOCK);
  while (cf > 0)
    {
    p=p->mln_Pred;
    cf--;
    }
  return p;
  }
/*========================================================================
 *  Makes succesive calls to Display_One() to display a block of memos.
 *    Param is pointer to a node structure attached to first memo
 *    to be displayed. Value of parameter is DisplayFirst which may need to
 *    be reset prior to call to Display_Block(). If param is NULL then list
 *    must be empty so display all blank memos
 =========================================================================*/
void Display_Block(struct MinNode *m)
  {
  int p;

  p=0;
  if (m)
    {
/*    while ( (p<MEMOS_IN_BLOCK) && ( m != NULL ) ) */
    while ( (p<MEMOS_IN_BLOCK) && ( m->mln_Succ ) )
	{
	Display_One( p, (struct MI_mem *)m );
	p++;
	m=m->mln_Succ;
	}
    }

  while ( p<MEMOS_IN_BLOCK )
	{
	Display_Blank( p );
	p++;
	}
  }
/*=========================================================================
 * If memo display gadget clicked on is currently showing a memo, toggle
 *   the memos selected indicator and redisplay.
 */
void Toggle(int t_gadget)
  {
  int g;
  struct MinNode *n;

  n=DisplayFirst;
  for (g=0 ; g < t_gadget ; g++)
    {
    n=n->mln_Succ;
    if (!n) break;
    }
  if (n)
    {
    if ( ((struct MI_Mem *)n)->mim_Select == 0) /* ie if not currently selected */
      ((struct MI_Mem *)n)->mim_Select=1;
    else
      ((struct MI_Mem *)n)->mim_Select=0;
    Display_One(g,(struct MI_Mem *)n);
    }
  }


/*=========================================================================
 * Move the 'current' memo forward to end
 *   Actually move the current pointer forward to
 *   the end of the list) and then MEMOS_IN_BLOCK backward
 */

struct MinNode *MoveEnd(struct MinNode *p)
  {
    struct MinNode *n;
    int b;

    n = GetTail(MemListPtr);
    b = MEMOS_IN_BLOCK-1;
    while (b > 0)
    {
      if (n)
        n = GetPred(n);
      b--;
    }
    if (n)
      return n;
    else
      return GetHead(MemListPtr);
  }

