/*				   Search.c				   */
/*The routines in this module handle the search/replace functions of 'Liner*/

#include "Globals.h"
#include "SearchWdws.h"

#define MAYBE 2
#define NONONO 3

extern void ModSearchWdw();

struct SearchParameters
{
   char Search[80],Replace[80];
   BYTE rep,cse,partial;
};

struct SearchParameters parameters =
{
   "","",FALSE,FALSE,TRUE
};

struct Window *SearchWdw,*ReplAllWdw;
char *SearchStr,*ReplaceStr;
struct Gadget *SearchFor,*ReplaceWith;

GetSRParameters()/*Opens a window to get the parameters for Search/Replace*/
{
   int status;
   struct Gadget junkgad;
   struct Gadget *SearchGadgets,*Cur,*Temp;
   struct StringInfo *si;
   ULONG add;

   NewSearchWindow.Screen=(struct Screen *)Screen;

      /*Get the difference between the screen font point size and the*/
      /*Topaz point size (8).  Used to properly position the screen*/
      /*stuff when a different default font is being used*/
   add=Screen->Font->ta_YSize-Topaz.ta_YSize;

   Temp=Cur=(struct Gadget *)MakeButtonGadgets(&junkgad,SearchButtons,VisInfo,
      &Topaz,SEARCHBUTTONS,add);

   Cur=(struct Gadget *)MakeStringGadgets(Cur,SearchStrings,VisInfo,&Topaz,
       SEARCHSTRINGS,StringData,add);

   /*Get buffers for Search/Replace*/
   si=(SearchFor=(struct Gadget *)Temp->NextGadget)->SpecialInfo;
   SearchStr=si->Buffer;

   si=(ReplaceWith=(struct Gadget *)Temp->NextGadget->NextGadget)
	 ->SpecialInfo;
   ReplaceStr=si->Buffer;

   strcpy(SearchStr,parameters.Search);
   strcpy(ReplaceStr,parameters.Replace);

   Cur=(struct Gadget *)MakeCheckGadgets(Cur,SearchCheck,VisInfo,&Topaz,
       SEARCHCHECK,CheckStates,add);

   NewSearchWindow.FirstGadget=SearchGadgets=
	 junkgad.NextGadget;

      /*If a non-topaz screen font is in use, increase the window height*/
      /*accordingly (to make room for the extra space the title bar will*/
      /*take up*/
   NewSearchWindow.Height+=add;

   SearchWdw=(struct Window *)OpenWindow(&NewSearchWindow);
   if(SearchWdw==NULL)
   {
      Leave(0,"Can't open the search/replace window!");
      return(FALSE);
   }

      /*Draw a bevel (raised 3D) box around OK, CANCEL, WAIT*/
   DrawBevelBoxA(SearchWdw->RPort,9,13+add,193,65,&beveltag[0]);

      /*Around Case sensitive, etc...*/
   DrawBevelBoxA(SearchWdw->RPort,28,81+add,155,42,&beveltag[0]);

      /*And around the text gadgets*/
   DrawBevelBoxA(SearchWdw->RPort,9,126+add,193,17,&beveltag[0]);

   NewSearchWindow.Height-=add; /*Return the height to its original value*/

   ActivateGadget(SearchFor,SearchWdw,NULL);
	 /*Activate the Search for... text gadget*/

   while((status=MonitorButtons())==NONONO); /*Let user enter info*/

   if(status) /*Copy users's choices into parameters structure*/
   {	      /*And store them for future use in Search gadgets*/
      strcpy(parameters.Search,SearchStr);
      strcpy(parameters.Replace,ReplaceStr);
      CheckStates[0]=!parameters.partial;
      CheckStates[1]=parameters.cse;
      CheckStates[2]=parameters.rep;
      if(status==MAYBE)   /*If Wait, then change to Cancel*/
	 status=FALSE;
   }
   else /*Pressed cancel, so restore gadgets*/
   {
      strcpy(SearchStr,parameters.Search);
      strcpy(ReplaceStr,parameters.Replace);
      parameters.rep=CheckStates[2];
      parameters.cse=CheckStates[1];
      parameters.partial=!CheckStates[0];
   }

   CloseWindow(SearchWdw);
   FreeGadgets(SearchGadgets);
   return(status);
}

MonitorButtons() /*Monitor the IDCMP port*/
{
   ULONG Class;
   struct Gadget *gadg;
   struct IntuiMessage *mesg;

   for(;;)
   {
      Wait(1<<SearchWdw->UserPort->mp_SigBit);
      while((mesg=(struct IntuiMessage *)GetMsg(SearchWdw->UserPort))!=NULL)
      {
	 Class=mesg->Class;   /*Get the information*/
	 gadg=mesg->IAddress;
	 ReplyMsg(mesg);      /*Reply to the message*/

	 switch(Class)
	 {
	    case CLOSEWINDOW: /*Close gadget == CANCEL*/
	       return(FALSE);
	    case GADGETUP:
	       switch(gadg->GadgetID)
	       {
		  case 8:  /*Cancel*/
		     return(FALSE);
		  case 7:  /*Wait*/
		     return(MAYBE);
		  case 6:  /*OK*/
		     return(TRUE);
		  case 5:  /*Replace*/
		     parameters.rep=!parameters.rep;
		     return(NONONO);
		  case 4:
		     parameters.partial=!parameters.partial;
		     return(NONONO);
		  case 3:
		     parameters.cse=!parameters.cse;
		  case 2:
		     return(NONONO);
		  case 1:
		     ActivateGadget(ReplaceWith,SearchWdw,NULL);
			/*Activate the Replace with... string gadget when*/
			/*the user presses RETURN in the Search for...*/
			/*gadget*/
		     return(NONONO); /*Don't alter a flag :-) */
	       }
	 }
      }
   }
}


extern struct LineItem *SearchText(Item,Pos,string,scase,partial)
      /*Search an outline for a particular string of characters*/
struct LineItem *Item; /*Item to start searching on*/
USHORT *Pos;	       /*Position in that item to start on*/
char *string;	       /*Search string*/
BYTE scase,partial;    /*scase	- TRUE if case matters, FALSE otherwise*/
		       /*partial-TRUE if it may be a part of a word*/
{
   int c,found,start,thestart;
   struct LineItem *cur,*theitem;
   BYTE status=FALSE;
   char chr,fchr;

   if(!scase) /*If case sensitive, convert the string to all uppercase*/
      for(c=0;c<strlen(string);c++)
	 string[c]=toupper(string[c]);

   if(*Pos >= strlen(Item->Text)) /*If at the end of the line*/
   {
      start=0;	/*Start of next line*/
      Item=(struct LineItem *)Item->NextItem;
   }
   else
      start=*Pos;  /*Else, start at cursor position*/

   for(cur=(struct LineItem *)Item;cur != NULL && !status;/*Search until end*/
	 cur=(struct LineItem *)cur->NextItem) /*or text is found*/
      for(c=start;c<strlen(cur->Text) && !status ;++c)
      {        /*Loop that searches each line:	*/
	 start=0;

	 if((partial) || ( (!partial) && ((c==0) || (cur->Text[c-1]==' ') )))
	       /*If partial words are OK, or if not, if the character*/
	       /*currently being checked is the first letter in a word*/
	 {
	    if(!scase)  /*If case is important*/
	       chr=toupper(cur->Text[c]); /*Switch to uppercase (see above)*/
	    else
	       chr=cur->Text[c];

	    if(string[0]==chr) /*If first character matches...*/
	    {
	       status=TRUE;
	       theitem=(struct LineItem *)cur;
	       thestart=c;
	       for(found=1;found<strlen(string) && status ;found++)
	       {     /*see if the rest matches...*/
		  if(!scase)
		     fchr=toupper(cur->Text[c+found]);
		  else
		     fchr=cur->Text[c+found];

		  if(fchr!=string[found])
		     status=FALSE;
	       }		    /*If the word is a prefix of another word*/
	       if(cur->Text[c+found] != ' ' && cur->Text[c+found]!= NULL &&
			!partial)
		  status = FALSE;      /*return FALSE*/
	    }
	 }
      }
   if(status) /*If found...*/
   {
      *Pos=thestart;	/*Return the position of the start of the text*/
      return(theitem);  /*And the item that it is found in*/
   }			/*(so that the cursor can be moved accordingly)*/
   else
      return(NULL);     /*Otherwise, nothing*/
}

DoSearch(again,arexx)  /*Main entry point for 'Search/Replace' and 'Next'*/
BYTE again,arexx;      /*Again:  TRUE if 'Next', FALSE if 'Search/Replace'*/
{
   char Buffer[8],reslt[160],preservation[80];
   int status;
   UBYTE TempY;
   struct LineItem *tempitem,*result;
   struct LineItem *firstitem=(struct LineItem *)CurrentItem;

   USHORT Pos=(again && again != 100) ? CurX-MinX(CurrentItem)+1 :
	  CurX-MinX(CurrentItem);


   status = (again) ? TRUE : GetSRParameters();
	 /*If 'Next', search no matter what.  Otherwise, depends on user*/
   CancelInvs();  /*Cancel text highlighting*/
   if(ErrorInTitle)     /*Cancel any errors in the title bar*/
      TitleErrorCancel();


   if(status)     /*If it's OK to search*/
   {
      strcpy(preservation,parameters.Search);   /*Store the search string*/
	    /*so if SearchText turns it to all caps, we'll still have the*/
	    /*original*/

      result=(struct LineItem *) /*Search the text*/
	    SearchText(CurrentItem,&Pos,parameters.Search,parameters.cse,
	    parameters.partial);

      if(result!=NULL)  /*If found...*/
      {
	 /*Place the cursor at the start of the word*/
	 if(result!=FirstScrnItem || result!=firstitem)
	 {
	    FirstScrnItem=CurrentItem=(struct LineItem *)result;
	    PrintItemList(CurrentItem,1);
	 }
	 PlotCursor(MinX(CurrentItem)+Pos,1);
	 if(parameters.rep) /*If a search and replace*/
	 {		    /*Replace!!!!*/
	    strcpy(reslt,&(CurrentItem->Text[Pos+strlen(parameters.Search)]));
	    strins(reslt,parameters.Replace);
	    CurrentItem->Text[Pos]=NULL;
	    strins(reslt,CurrentItem->Text);

	       /*Break the line apart if necessary*/
	    tempitem=(struct LineItem *)BreakLineApart(CurrentItem,
		  CurrentItem->NextItem,reslt);

	    PlotCursor(1,CurY);
	    Buffer[0]=CSI;
	    Buffer[1]=0x4b;
	    WriteConsole(Buffer,2);
	    TempY=CurY;
	    if(tempitem==CurrentItem)
	       PrintItem(CurrentItem);
	    else
	       PrintItemList(CurrentItem,CurY);
	    PlotCursor(MinX(CurrentItem)+Pos,TempY);
	    CheckModified();  /*Text has been modified*/
	 }
	 strcpy(parameters.Search,preservation);
	 return(TRUE);
      }
      else  /*If it wasn't found*/
      {
	 strcpy(parameters.Search,preservation);
	 if(!arexx)  /*If this wasn't an ARexx command*/
	    TitleError("Wasn't found!!!");
	 return(FALSE);
      }
   }
}

void ModifyParams(search,replace,case_s,partial,s_repl)
char *search,*replace;
BYTE case_s,partial,s_repl;
{
   if(search[0]!=NULL)
      strcpy(parameters.Search,search);
   if(replace[0]!=NULL)
      strcpy(parameters.Replace,replace);
   if(case_s!=100)
      parameters.cse=case_s;
   if(partial!=100)
      parameters.partial=partial;
   if(s_repl!=100)
      parameters.rep=s_repl;
}

void ReplaceAll(verify) /*Search/replace entire document at once*/
BYTE verify;
{
   BYTE stat;

   if(verify)
   {
      ModifyParams("","",100,100,FALSE);
      while(DoSearch(TRUE,TRUE)) /*Search for text*/
      {
	 if(stat=AskChange()) /*Ask user what to do*/
	 {
	    if(stat==TRUE) /*Replace*/
	    {
	       ModifyParams("","",100,100,TRUE);
	       DoSearch(100,TRUE);
	       ModifyParams("","",100,100,FALSE);
	    } /*Else goto next*/
	 }
	 else
	    return;  /*Else cancel*/
      }
      TitleError("No more found");
   }
   else   /*Do all without verification*/
   {
      ModifyParams("","",100,100,TRUE);
      while(DoSearch(TRUE,TRUE));
   }
}

BYTE AskChange() /*Ask user if he wants to do replace*/
{
   struct Window *ReplAllWdw;
   struct IntuiMessage *mesg;
   struct Gadget junkgad,*gadg;
   BYTE ID;

   MakeButtonGadgets(&junkgad,VerifyButtons,VisInfo,&Topaz,VERIFYBUTTONS,0);

      /*Position the window so that it's right under the text, no matter*/
      /*what the point size*/
   NewWindowStructure2.TopEdge=3+2*Screen->Font->ta_YSize;
   NewWindowStructure2.FirstGadget=(struct Gadget *)junkgad.NextGadget;

   NewWindowStructure2.Screen=(struct Screen *)Screen; /*'Liner screen*/
   if((ReplAllWdw=(struct Window *)OpenWindow(&NewWindowStructure2))==NULL)
      return(FALSE); /*Open the window*/

   PrintIText(ReplAllWdw->RPort,&IntuiTextList2,5,1);
   Wait(1<<ReplAllWdw->UserPort->mp_SigBit); /*Wait for gadget press*/

   if((mesg=(struct IntuiMessage *)GetMsg(ReplAllWdw->UserPort))==NULL)
   {
      CloseWindow(ReplAllWdw);
      FreeGadgets(junkgad.NextGadget);
      return(FALSE); /*Get the message*/
   }

   gadg=(struct Gadget *)mesg->IAddress;/*Get the gadget structure's addr*/

   ReplyMsg(mesg); /*Reply to it*/

   ID=gadg->GadgetID;
   CloseWindow(ReplAllWdw); /*Close the window*/
   FreeGadgets(junkgad.NextGadget);
   return(ID); /*Return the gadget ID*/
}

void ModSearchWdw()
{
   CheckStates[0]=!parameters.partial;
   CheckStates[1]=parameters.cse;
   CheckStates[2]=parameters.rep;
}


/*~~~End of Search.c*/
