/*

File: search.c
Author: Neil Cafferkey
Copyright (C) 2000 Neil Cafferkey

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA.

*/

#include "viewer.h"
#include <stdlib.h>
#include <exec/memory.h>
#include <intuition/screens.h>
#include <libraries/gadtools.h>

#include "search_protos.h"
#include "general_protos.h"
#include "article_protos.h"
#include "index_protos.h"
#include "paedia_protos.h"
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/gadtools.h>


#define SUBJECT_LINE_COUNT 4
#define MIN_ARTICLE_LINE_COUNT 4
#define DEFAULT_SEARCH_WIDTH 250
#define DEFAULT_SEARCH_HEIGHT 250
enum {KEYWORD_BOX_ID=1,SEARCH_BUTTON_ID,SUBJECT_VIEW_ID,ARTICLE_VIEW_ID};

const TEXT *keyword_box_label="Search Words";
const TEXT *search_button_label="Search";
const TEXT *subject_view_label="Subjects";
const TEXT *article_view_label="Articles";


/* Prototypes */

static BOOL OpenSearchGadgets(Search search);
static VOID CloseSearchGadgets(Search search);



/* Function: CreateSearch
 * ======================
 * Creates a Search.
 */

Search CreateSearch(struct MsgPort *msg_port,Paedia paedia,
   struct MinList *object_list,UWORD left,UWORD top)
{
   Search search;
   BOOL success=TRUE;
   struct Screen *screen=NULL;
   struct IntuiText i_text;
   struct Window *window;

   if(search=AllocMem(sizeof(Search_imp),MEMF_CLEAR))
   {

      search->node.ln_Type=NT_SEARCH;
      search->paedia=paedia;
      search->object_list=object_list;
      search->msg_port=msg_port;

      /* Allocate memory for search string */

      if((search->keywords=AllocMem(64+1,MEMF_ANY))!=NULL)
      {
         *search->keywords='\0';
      }
      else
      {
         ReportError(NULL,ERROR_REPORT_MEM,NULL,0);
         success=FALSE;
      }

      /* Open the search window */

      search->window=window=OpenWindowTags(NULL,
         WA_Left,left,
         WA_Top,top,
         WA_Width,DEFAULT_SEARCH_WIDTH,
         WA_Height,DEFAULT_SEARCH_HEIGHT,
         WA_Flags,WFLG_CLOSEGADGET|WFLG_DEPTHGADGET|WFLG_SIZEBBOTTOM|
            WFLG_SIZEGADGET|WFLG_ACTIVATE|WFLG_NEWLOOKMENUS|WFLG_DRAGBAR,
         WA_Title,"Index",
         WA_MinWidth,0,
         WA_MinHeight,0,
         WA_MaxWidth,-1,
         WA_MaxHeight,-1,
         TAG_END);

      /* Lock the screen so it can be examined during the life of this
         Search. */

      search->screen=screen=LockPubScreen(NULL);

      if(window)
      {

         /* Set the window's message port and IDCMP flags */

         window->UserPort=msg_port;

         ModifyIDCMP(window,IDCMP_CLOSEWINDOW|IDCMP_NEWSIZE|
            IDCMP_CHANGEWINDOW|STRINGIDCMP|BUTTONIDCMP|LISTVIEWIDCMP);

         /* Store Search's pointer in its window */

         (Search)window->UserData=search;

         /* Calculate gadget positions and sizes */

         search->font_height=screen->Font->ta_YSize;
         search->gadget_left=window->BorderLeft+INTERWIDTH;
         search->gadget_top=window->BorderTop+INTERHEIGHT
            +search->font_height+INTERHEIGHT;

         search->gadget_rel_width=
            -window->BorderRight-INTERWIDTH-search->gadget_left;

         i_text.ITextFont=screen->Font;
         i_text.IText=search_button_label;
         i_text.NextText=NULL;

         search->search_button_width=IntuiTextLength(&i_text)+12;
         search->search_button_rel_left=-window->BorderLeft
            -INTERWIDTH-search->search_button_width;

         search->keyword_box_rel_width=
            -window->BorderRight-2*INTERWIDTH
            -search->search_button_width-search->gadget_left;
         search->keyword_box_height=search->font_height+6;

         search->subject_view_top=search->gadget_top
            +search->keyword_box_height+search->font_height+2*INTERHEIGHT;
         search->subject_view_height=search->font_height*SUBJECT_LINE_COUNT
            +4;
         search->article_view_top=search->subject_view_top
            +search->subject_view_height+search->font_height+2*INTERHEIGHT;
         search->article_view_rel_height=-search->article_view_top
            -window->BorderBottom-INTERHEIGHT;

         /* Set minimum window dimensions */

         i_text.IText=keyword_box_label;
         search->min_width=-search->search_button_rel_left
            +IntuiTextLength(&i_text)+2*INTERWIDTH+window->BorderLeft;

         search->min_height=search->article_view_top+search->font_height*
            MIN_ARTICLE_LINE_COUNT+4+INTERHEIGHT+window->BorderBottom;


         if(!WindowLimits(window,search->min_width,search->min_height,0,0))
            ChangeWindowBox(window,window->LeftEdge,window->TopEdge,
               search->min_width,search->min_height);

         /* Create gadgets */

         search->visual_info=GetVisualInfoA(screen,NULL);
         OpenSearchGadgets(search);

      }
      else
      {
         success=FALSE;
      }

      if(!success)
      {
         KillSearch(search);
         search=NULL;
      }
   }
   else
      ReportError(NULL,ERROR_REPORT_MEM,NULL,0);

   return search;
}



/* Function: OpenSearchGadgets
 * ===========================
 * Sets up a Search's gadgets. Returns a success flag.
 */

static BOOL OpenSearchGadgets(Search search)
{
   BOOL success=TRUE;
   struct Screen *screen=search->screen;
   struct NewGadget new_gadget;
   struct Window *window=search->window;

   /* Recalculate variable gadget positions and sizes */



   /* Create a gadtools context for this window */

   search->context=CreateContext(&search->gadgets);

   /* Create the keyword box */

   new_gadget.ng_LeftEdge=search->gadget_left;
   new_gadget.ng_TopEdge=search->gadget_top;
   new_gadget.ng_Width=window->Width+search->keyword_box_rel_width;
   new_gadget.ng_Height=search->keyword_box_height;
   new_gadget.ng_GadgetText=keyword_box_label;
   new_gadget.ng_TextAttr=screen->Font;
   new_gadget.ng_GadgetID=KEYWORD_BOX_ID;
   new_gadget.ng_Flags=PLACETEXT_ABOVE|NG_HIGHLABEL;
   new_gadget.ng_VisualInfo=search->visual_info;
   new_gadget.ng_UserData=0;

   search->keyword_box=CreateGadget(STRING_KIND,search->context,
      &new_gadget,GTST_String,search->keywords,TAG_END);

   /* Create the search button */

   new_gadget.ng_LeftEdge=window->Width+search->search_button_rel_left;
   new_gadget.ng_TopEdge=search->gadget_top;
   new_gadget.ng_Width=search->search_button_width;
   new_gadget.ng_Height=search->keyword_box_height;
   new_gadget.ng_GadgetText=search_button_label;
   new_gadget.ng_TextAttr=screen->Font;
   new_gadget.ng_GadgetID=SEARCH_BUTTON_ID;
   new_gadget.ng_Flags=0;
   new_gadget.ng_VisualInfo=search->visual_info;
   new_gadget.ng_UserData=0;

   search->search_button=CreateGadget(BUTTON_KIND,search->keyword_box,
      &new_gadget,TAG_END);

   /* Create the subject listview */

   new_gadget.ng_LeftEdge=search->gadget_left;
   new_gadget.ng_TopEdge=search->subject_view_top;
   new_gadget.ng_Width=window->Width+search->gadget_rel_width;
   new_gadget.ng_Height=search->subject_view_height;
   new_gadget.ng_GadgetText=subject_view_label;
   new_gadget.ng_TextAttr=screen->Font;
   new_gadget.ng_GadgetID=SUBJECT_VIEW_ID;
   new_gadget.ng_Flags=0;
   new_gadget.ng_VisualInfo=search->visual_info;
   new_gadget.ng_UserData=0;

   search->subject_view=CreateGadget(LISTVIEW_KIND,search->search_button,
      &new_gadget,GTLV_Labels,search->index_list,GTLV_Selected,
      search->selected_subject_no,GTLV_ShowSelected,NULL,
      GTLV_Top,search->top_subject_no,TAG_END);

   /* Create the article listview */

   new_gadget.ng_LeftEdge=search->gadget_left;
   new_gadget.ng_TopEdge=search->article_view_top;
   new_gadget.ng_Width=window->Width+search->gadget_rel_width;
   new_gadget.ng_Height=window->Height+search->article_view_rel_height;
   new_gadget.ng_GadgetText=article_view_label;
   new_gadget.ng_TextAttr=screen->Font;
   new_gadget.ng_GadgetID=ARTICLE_VIEW_ID;
   new_gadget.ng_Flags=0;
   new_gadget.ng_VisualInfo=search->visual_info;
   new_gadget.ng_UserData=0;

   if(search->current_subject!=NULL)
   {
      search->article_view=CreateGadget(LISTVIEW_KIND,search->subject_view,
         &new_gadget,GTLV_Labels,&search->current_subject->ref_list,
         GTLV_Top,search->top_article_no,TAG_END);
   }
   else
   {
      search->article_view=CreateGadget(LISTVIEW_KIND,search->subject_view,
         &new_gadget,GTLV_Labels,NULL,TAG_END);
   }

   /* Add gadgets to window and make them visible */

   if(search->gadgets!=NULL)
   {
      AddGList(window,search->gadgets,-1,-1,NULL);
      RefreshGList(search->gadgets,window,NULL,-1);
      GT_RefreshWindow(window,NULL);
   }
   else
   {
      success=FALSE;
   }

   if(!success)
   {
      KillSearch(search);
      search=NULL;
   }

   return success;
}



/* Function: CloseSearchGadgets
 * ============================
 * Destroys a Search's gadgets.
 */

static VOID CloseSearchGadgets(Search search)
{
   TEXT *keywords;

   /* Copy keyword string from gadget */

   GT_GetGadgetAttrs(search->keyword_box,search->window,NULL,GTST_String,
      &keywords,TAG_END);
   if(search->keywords)
      strcpy(search->keywords,keywords);

   /* Store listview attributes */

   GT_GetGadgetAttrs(search->subject_view,search->window,NULL,GTLV_Selected,
      &search->selected_subject_no,GTLV_Top,&search->top_subject_no,
      TAG_END);

/*printf("search->selected_subject_no=%u\n",search->selected_subject_no);*/

   GT_GetGadgetAttrs(search->article_view,search->window,NULL,GTLV_Top,
      &search->top_article_no,TAG_END);

   /* Remove and free gadgets */

   RemoveGList(search->window,search->gadgets,-1);
   FreeGadgets(search->gadgets);

   /* Clear window */

   SetRast(search->window->RPort,0);
   RefreshWindowFrame(search->window);

   return;
}



/* Function: HandleSearchInput
 * ===========================
 */

VOID HandleSearchInput(Search search,struct IntuiMessage *msg)
{
   BOOL close=FALSE;
   ULONG article_no=0;
   struct IntuiMessage *mod_msg;
   struct Gadget *gadget=(struct Gadget *)msg->IAddress;
   TEXT *keywords;
   Article article;
   Reference ref;
   Sequence article_data;

   mod_msg=GT_FilterIMsg(msg);
   if(mod_msg!=NULL)
   {

      switch(mod_msg->Class)
      {

      case IDCMP_NEWSIZE:
         CloseSearchGadgets(search);
         OpenSearchGadgets(search);
         break;

      case IDCMP_CLOSEWINDOW:
         close=TRUE;
         break;

      case IDCMP_GADGETUP:

         switch(gadget->GadgetID)
         {
         case KEYWORD_BOX_ID:
         case SEARCH_BUTTON_ID:

            /* Copy keyword string from gadget */

            GT_GetGadgetAttrs(search->keyword_box,search->window,NULL,
               GTST_String,&keywords,TAG_END);
            strcpy(search->keywords,keywords);

            /* Remove subject and article lists from their gadgets */

            GT_SetGadgetAttrs(search->subject_view,search->window,NULL,
               GTLV_Labels,NULL,TAG_END);

            GT_SetGadgetAttrs(search->article_view,search->window,NULL,
               GTLV_Labels,NULL,TAG_END);

            /* Do a new index search */

            if(search->index_list)
               KillIndexResults(search->index_list);

            SetWindowPointer(search->window,WA_BusyPointer,TRUE,TAG_END);

            search->index_list=FindIndexRecords(search->paedia->index,
               search->keywords);

            /* Display new subjects */

            GT_SetGadgetAttrs(search->subject_view,search->window,NULL,
               GTLV_Labels,search->index_list,GTLV_Selected,0,TAG_END);

            SetWindowPointer(search->window,TAG_END);

         case SUBJECT_VIEW_ID:

            /* Get current subject number */

            GT_GetGadgetAttrs(search->subject_view,search->window,NULL,
               GTLV_Selected,&search->selected_subject_no,TAG_END);

            if(search->selected_subject_no!=~0)
            {
               search->current_subject=(IndexRecord)GetMinListNode(
                  (struct MinList *)search->index_list,
                  search->selected_subject_no);

               /* Display new article titles */

               GT_SetGadgetAttrs(search->article_view,search->window,NULL,
                  GTLV_Labels,&search->current_subject->ref_list,TAG_END);
            }
            else
            {
               search->current_subject=NULL;
            }

            break;

         case ARTICLE_VIEW_ID:

            /* Display selected article */

            ref=(Reference)GetMinListNode(
               (struct MinList *)&search->current_subject->ref_list,
               mod_msg->Code);

            article_data=GetPaediaItem(search->paedia,ref->link_no);
            article=CreateArticle(article_data,search->msg_port);
            if(article)
               AddTail((struct List *)search->object_list,&article->node);

         default:

         }

      case IDCMP_CHANGEWINDOW:
         if(!WindowLimits(search->window,search->min_width,
            search->min_height,0,0))
            ChangeWindowBox(search->window,search->window->LeftEdge,
               search->window->TopEdge,search->min_width,
               search->min_height);
         break;

      default:

      }

      GT_PostFilterIMsg(mod_msg);
   }

   ReplyMsg((struct Message *)msg);

   if(close)
   {
      Remove(&search->node);
      KillSearch(search);
   }

   return;
}



/* Function: KillSearch
 * ====================
 * Destroys a Search.
 */

VOID KillSearch(Search search)
{

   if(search->index_list)
      KillIndexResults(search->index_list);

   if(search->window)
   {

      CloseSearchGadgets(search);

      Forbid();
      StripWindowMessages(search->window);
      search->window->UserPort=NULL;
      ModifyIDCMP(search->window,0);
      Permit();
      CloseWindow(search->window);
      UnlockPubScreen(NULL,search->screen);
   }

   FreeVisualInfo(search->visual_info);

   if(search->keywords!=NULL)
      FreeMem(search->keywords,64+1);

   /* Free search structure */

   FreeMem(search,sizeof(Search_imp));

   return;
}


