/* $Header: /blackbird/code/hopper.c 1.9 1997/08/28 15:03:38 mikeg Exp $*/
/****************************************************************************
 *
 * Copyright (c) Hewlett-Packard Co. 1996, 1997
 *
 * All Rights Reserved.  Reproduction, adaptation, or translation
 * without prior written permission is prohibited, except as allowed
 * under the copyright laws.
 *
 * Hewlett-Packard Company
 * 3000 Hanover Street, Palo Alto, CA  94303
 *
 ****************************************************************************
 */

#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/RowColumn.h>
#include <Xm/Frame.h>
#include <Xm/Text.h>
#include <Xm/List.h>
#include <Xm/PushB.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "feature.h"
#include "filter.h"
#include "database.h"
#include "userPane.h"

#define HOPPER_FILTER_VERSION		"HOPPER_FILTER_A.00.00"
#define OCCUPANCY_FILTER_VERSION	"OCCUPANCY_FILTER_A.00.00"

#define MAX_ENERGY_BLOCKED	500
#define MAXSWEEPS		32

enum{
  FREQ_LIST,
  OCCUPANCY,
  NUM_FILTERS
};

enum{
  MINIMUM_OCCUPANCY,
  MAXIMUM_OCCUPANCY,
  NUM_OCCUPANCY_FILTER_PARMS
};

static struct aFilterStruct hopper[NUM_FILTERS];


static char *post_labels[] = {"Minimum Occupancy", "Maximum Occupancy"};
static char *post_units[] = {"Percent", "Percent"};


static struct{
   int    initialized;
   Widget w_form;
   Widget w_label_rowcol;
   Widget w_text_rowcol;
   Widget w_unit_rowcol;
   Widget w_label[NUM_OCCUPANCY_FILTER_PARMS];
   Widget w_text[NUM_OCCUPANCY_FILTER_PARMS];
   Widget w_unit[NUM_OCCUPANCY_FILTER_PARMS];
} occupancyUI = {0};

static struct{
   double minOccupancy;
   double maxOccupancy;
} postParms[MAX_FILTER_SLOTS];

static double blockedEnergy[MAX_ENERGY_BLOCKED];
static int numBlockedEnergy = 0;
static double mergeFrequency = 10000.0;

/***********************************************************************/
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
/***********************************************************************/
static struct aUserPane userPane;
static Widget w_frame = NULL;
static Widget w_list = NULL;
static void clearCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
   numBlockedEnergy = 0;
   if(w_list) XmListDeleteAllItems(w_list);
}
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
void addFrequencyToUserPaneList(double frequency)
{
   XmString CString;
   char string[80];
   if(w_list){
      sprintf(string,"%.0f", frequency);
      CString = XmStringCreateLtoR(string, XmFONTLIST_DEFAULT_TAG);
      XmListAddItems(w_list, &CString, 1, 0);
      XmStringFree(CString);
   }
}
/***********************************************************************/
/* Preset                                                              */
/***********************************************************************/
static void presetUserPane(void)
{
   if(w_list) XmListDeleteAllItems(w_list);
}
/***********************************************************************/
/* Remove                                                              */
/***********************************************************************/
static void removeUserPane(void)
{
   XtUnmanageChild(w_frame);
}
/***********************************************************************/
/* Add                                                                 */
/***********************************************************************/
static Widget addUserPane(void)
{
   XtManageChild(w_frame);
   return(w_frame);
}
/***********************************************************************/
/* Create                                                             */
/***********************************************************************/
static void createUserPane(Widget parent)
{
   XmString CString;
   Widget w_frame_label, w_form, w_clear;
   int n;
   Arg args[10];

   w_frame = XtVaCreateWidget("user_pane",
        xmFrameWidgetClass,
        parent,
        XmNresizable,       True,
        XmNshadowType,      XmSHADOW_ETCHED_IN,
        XmNshadowThickness, 5,
        XmNmarginWidth,     0,
        XmNmarginHeight,    0,
        NULL);

   CString = XmStringCreateLocalized("User Pane");
   w_frame_label = XtVaCreateManagedWidget("label",
      xmLabelWidgetClass,
      w_frame,
      XmNlabelString,       CString,
      XmNchildType,         XmFRAME_TITLE_CHILD,
      XmNchildHorizontalAlignment, XmALIGNMENT_CENTER,
      XmNchildVerticalAlignment, XmALIGNMENT_WIDGET_BOTTOM,
      NULL);
   XmStringFree(CString);

   w_form = XtVaCreateManagedWidget("form",
      xmFormWidgetClass,
      w_frame,
      NULL);

   CString = XmStringCreateLocalized(" Clear ");
   w_clear = XtVaCreateManagedWidget("button",
      xmPushButtonWidgetClass, 
      w_form,
      XmNlabelString,          CString,
      XmNleftAttachment,       XmATTACH_FORM,
      XmNleftOffset,           5,
      XmNbottomAttachment,     XmATTACH_FORM,
      XmNbottomOffset,         5,
      NULL);
   XmStringFree(CString);
   XtAddCallback(w_clear, XmNactivateCallback, clearCallback, NULL);

   n = 0;
   XtSetArg (args[n], XmNselectionPolicy,        XmSINGLE_SELECT);  n++;
   XtSetArg (args[n], XmNlistSizePolicy,         XmVARIABLE);  n++;
   XtSetArg (args[n], XmNvisibleItemCount,       10);  n++;
   XtSetArg (args[n], XmNscrollBarDisplayPolicy, XmSTATIC);  n++;
   w_list = XmCreateScrolledList(w_form, "freq_list", args, n);
   XtVaSetValues(XtParent(w_list),
      XmNtopAttachment,        XmATTACH_FORM,
      XmNleftAttachment,       XmATTACH_FORM,
      XmNleftOffset,           5,
      XmNrightAttachment,      XmATTACH_FORM,
      XmNrightOffset,          5,
      XmNbottomAttachment,     XmATTACH_WIDGET,
      XmNbottomWidget,         w_clear,
      XmNbottomOffset,         5,
      NULL);
   XtManageChild(w_list);

}
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
int loadE3238sUserPane(struct aUserPane **p)
{
   userPane.libRevision = USER_PANE_LIB_REVISION;
   strcpy(userPane.name, "Lockout Frequencies");
   userPane.create_func = createUserPane;
   userPane.add_func    = addUserPane;
   userPane.remove_func = removeUserPane;
   userPane.recall_func = NULL;
   userPane.save_func   = NULL;
   userPane.preset_func = presetUserPane;
   *p = &userPane;
   return(1);
}
/***********************************************************************/
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
/***********************************************************************/
static void CWFilterDescription(int filterIndex, char *description)
{
   strcpy(description, "Already removed once by post filter");
}
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
static int saveCWFilter(int filterIndex, char *state)
{
   strcpy(state, HOPPER_FILTER_VERSION);
   return(0);
}
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
static int recallCWFilter(int filterIndex, char *state)
{
   if(strncmp(state, HOPPER_FILTER_VERSION, strlen(HOPPER_FILTER_VERSION))){
      return(-1);
   }
}
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
static void presetCWFilter(void)
{
   numBlockedEnergy = 0;
   printf("Preset -> Reset Counter = %i\n",numBlockedEnergy);
}

/***********************************************************************/
/*                                                                     */
/***********************************************************************/
static int CWFilter(struct aPreFilterDataStruct *pre)
{
   int i;

   for(i=0;i<numBlockedEnergy;i++){
      if(fabs(blockedEnergy[i] - pre->frequency) < mergeFrequency){
         return(0);  /* Remove */
      }
   }
   return(1); /* Keep */
}
/***********************************************************************/
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
/***********************************************************************/
static void occupancyFilterDescription(int filterIndex, char *description)
{
   sprintf(description, "Keep if Occupancy is between %.1f %% and %.1f %%.", 
                         postParms[filterIndex].minOccupancy, postParms[filterIndex].maxOccupancy);
}

/***********************************************************************/
/*                                                                     */
/***********************************************************************/
static void updateOccupancyParameters(int filterIndex)
{
   int i;
   char string[32];
   for(i=0;i<NUM_OCCUPANCY_FILTER_PARMS;i++){
      switch(i){
         case MINIMUM_OCCUPANCY: sprintf(string, "%.1f", postParms[filterIndex].minOccupancy); break;
         case MAXIMUM_OCCUPANCY: sprintf(string, "%.1f", postParms[filterIndex].maxOccupancy); break;
      }
      XmTextSetString(occupancyUI.w_text[i], string);
   }
}
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
static void setOccupancyParameters(int filterIndex)
{
   int i;
   char *string;
   double value;
   for(i=0;i<NUM_OCCUPANCY_FILTER_PARMS;i++){
      string = XmTextGetString(occupancyUI.w_text[i]);
      value = atof(string);
      XtFree(string);
      switch(i){
         case MINIMUM_OCCUPANCY:
            if((value >= 0.0) && (value <= 100)){
               postParms[filterIndex].minOccupancy = value;
            }
            break;
         case MAXIMUM_OCCUPANCY:
            if((value >= 0.0) && (value <= 100)){
               postParms[filterIndex].maxOccupancy = value;
            }
            break;
      }
   }
   if(postParms[filterIndex].minOccupancy > postParms[filterIndex].maxOccupancy){
      value = postParms[filterIndex].maxOccupancy;
      postParms[filterIndex].maxOccupancy = postParms[filterIndex].minOccupancy;
      postParms[filterIndex].minOccupancy = value;
   }
}
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
static void occupancyTextCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
   XmTextVerifyCallbackStruct *tvcb;
   int i, length, reason_code;
   char *ptr, c;

   reason_code = ((XmAnyCallbackStruct *)call_data)->reason;
   switch(reason_code){
      case XmCR_MODIFYING_TEXT_VALUE:           /* XmNmodifyVerifyCallback */
         tvcb = (XmTextVerifyCallbackStruct *)call_data;
         ptr = tvcb->text->ptr;
         length = tvcb->text->length;
         for(i=0;i<length;i++){
            c = *ptr++;
            if(!(((c >= '0') && (c<='9')) || (c=='.'))){
               tvcb->doit = False;
               return;
            }
         }
         break;

      case XmCR_FOCUS:
         XtVaSetValues(w, XmNcursorPositionVisible, True, NULL);
         break;

      case XmCR_LOSING_FOCUS:
         XtVaSetValues(w, XmNcursorPositionVisible, False, NULL);
         break;

   }
}
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
static Widget occupancyFilterUI(Widget parent, int mode, int filterIndex)
{
   int i;
   XmString CString;
   Pixel shadow_pixel;

   switch(mode){
      case USER_FILTER_UI_UPDATE:
         if(!occupancyUI.initialized){
            occupancyUI.w_form = XtVaCreateManagedWidget("form",
               xmFormWidgetClass,
               parent,
               XmNmarginWidth,         5,
               NULL);

            occupancyUI.w_label_rowcol = XtVaCreateManagedWidget("maxocc_row_column",
               xmRowColumnWidgetClass,
               occupancyUI.w_form,
               XmNpacking,          XmPACK_COLUMN,
               XmNorientation,      XmVERTICAL,
               XmNnumColumns,       1,
               XmNentryAlignment,   XmALIGNMENT_BEGINNING,
               XmNtopAttachment,    XmATTACH_FORM,
               XmNleftAttachment,   XmATTACH_FORM,
               XmNbottomAttachment, XmATTACH_FORM,
               XmNadjustLast,       False,
               XmNadjustMargin,     False,
               XmNmarginWidth,      0,
               XmNspacing,          3,
               NULL);

            occupancyUI.w_text_rowcol = XtVaCreateManagedWidget("maxocc_row_column",
               xmRowColumnWidgetClass,
               occupancyUI.w_form,
               XmNpacking,          XmPACK_COLUMN,
               XmNorientation,      XmVERTICAL,
               XmNnumColumns,       1,
               XmNentryAlignment,   XmALIGNMENT_BEGINNING,
               XmNtopAttachment,    XmATTACH_FORM,
               XmNleftWidget,       occupancyUI.w_label_rowcol,
               XmNleftAttachment,   XmATTACH_WIDGET,
               XmNbottomAttachment, XmATTACH_FORM,
               XmNadjustLast,       False,
               XmNadjustMargin,     False,
               XmNmarginWidth,      0,
               XmNspacing,          3,
               NULL);
            XtVaGetValues(occupancyUI.w_text_rowcol, XmNbottomShadowColor, &shadow_pixel, NULL);

            occupancyUI.w_unit_rowcol = XtVaCreateManagedWidget("maxocc_row_column",
               xmRowColumnWidgetClass,
               occupancyUI.w_form,
               XmNpacking,          XmPACK_COLUMN,
               XmNorientation,      XmVERTICAL,
               XmNnumColumns,       1,
               XmNentryAlignment,   XmALIGNMENT_BEGINNING,
               XmNtopAttachment,    XmATTACH_FORM,
               XmNleftWidget,       occupancyUI.w_text_rowcol,
               XmNleftAttachment,   XmATTACH_WIDGET,
               XmNbottomAttachment, XmATTACH_FORM,
               XmNrightAttachment,  XmATTACH_FORM,
               XmNadjustLast,       False,
               XmNadjustMargin,     False,
               XmNmarginWidth,      0,
               XmNspacing,          3,
               NULL);

            for(i=0;i<NUM_OCCUPANCY_FILTER_PARMS;i++){
               CString = XmStringCreateLocalized(post_labels[i]);
               occupancyUI.w_label[i] = XtVaCreateManagedWidget("label",
                     xmLabelWidgetClass,
                     occupancyUI.w_label_rowcol,
                     XmNlabelString,  CString,
                     XmNmarginHeight, 3,
                     XmNmarginTop,    4,
                     XmNmarginBottom, 5,
                     NULL);
               XmStringFree(CString);

               occupancyUI.w_text[i] = XtVaCreateManagedWidget("label",
                     xmTextWidgetClass,
                     occupancyUI.w_text_rowcol,
                     XmNcursorPositionVisible, False,
                     XmNblinkRate,    0,
                     XmNmaxLength,    6,
                     XmNcolumns,      6,
                     XmNbackground,   shadow_pixel,
                     XmNmarginHeight, 3,
                     NULL);
               XtOverrideTranslations(occupancyUI.w_text[i], XtParseTranslationTable("<Key>Return: "));
               XtAddCallback(occupancyUI.w_text[i], XmNmodifyVerifyCallback, occupancyTextCallback, (XtPointer)i);
               XtAddCallback(occupancyUI.w_text[i], XmNfocusCallback,        occupancyTextCallback, (XtPointer)i);
               XtAddCallback(occupancyUI.w_text[i], XmNlosingFocusCallback,  occupancyTextCallback, (XtPointer)i);

               CString = XmStringCreateLocalized(post_units[i]);
               occupancyUI.w_unit[i] = XtVaCreateManagedWidget("label",
                     xmLabelWidgetClass,
                     occupancyUI.w_unit_rowcol,
                     XmNlabelString,  CString,
                     XmNmarginHeight, 3,
                     XmNmarginTop,    4,
                     XmNmarginBottom, 5,
                     NULL);
               XmStringFree(CString);
            }
            occupancyUI.initialized = 1;
         }
         updateOccupancyParameters(filterIndex);
         break;

      case USER_FILTER_UI_OK:
         setOccupancyParameters(filterIndex);
         updateOccupancyParameters(filterIndex);
         break;

      case USER_FILTER_UI_CANCEL:
         break;

      case USER_FILTER_UI_DESTROY:
         occupancyUI.initialized = 0;
         break;
   }
   return(occupancyUI.w_form);
}

/***********************************************************************/
/*                                                                     */
/***********************************************************************/
static int saveOccupancyFilter(int filterIndex, char *state)
{
   sprintf(state, "%s %g %g", OCCUPANCY_FILTER_VERSION, 
                              postParms[filterIndex].minOccupancy, postParms[filterIndex].maxOccupancy);
   return(0);
}
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
static int recallOccupancyFilter(int filterIndex, char *state)
{
   double minOccupancy, maxOccupancy, value;

   if(strncmp(state, OCCUPANCY_FILTER_VERSION, strlen(OCCUPANCY_FILTER_VERSION))){
      return(-1);
   }
   if(sscanf(state, "%*s %lg %lg", &minOccupancy, &maxOccupancy) != 2){
      return(-1);
   }
   if((minOccupancy < 0.0) || (minOccupancy > 100) || 
      (maxOccupancy < 0.0) || (maxOccupancy > 100)){
      return(-1);
   }
   if(minOccupancy > maxOccupancy){
      value = minOccupancy;
      maxOccupancy = minOccupancy;
      minOccupancy = value;
   }
   postParms[filterIndex].minOccupancy = minOccupancy;
   postParms[filterIndex].maxOccupancy = maxOccupancy;
   return(0);
}
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
static void presetOccupancyFilter(void)
{
   int i;
   for(i=0;i<MAX_FILTER_SLOTS;i++){
      postParms[i].minOccupancy = 1;
      postParms[i].maxOccupancy = 10;
   }
}
/***********************************************************************/
/*                                                                     */
/***********************************************************************/
static int doOccupancyFilter(struct aPostFilterDataStruct *post)
{
   double occupancy;
   int i;

   if(post->entry->numSweeps > MAXSWEEPS){
      occupancy = ((double)post->entry->numIntercepts/(double)post->entry->numSweeps) * 100.0;
      if(occupancy < postParms[post->filterIndex].minOccupancy){
         return(1); /** Remove entry because too low of occupancy ie. Noise **/
      }
      else if(occupancy > postParms[post->filterIndex].maxOccupancy){
         for(i=0;i<numBlockedEnergy;i++){
            if(fabs(blockedEnergy[i] - post->entry->frequency) < mergeFrequency){
               return(1); /** Remove since occupancy is too high and already in the table **/
            }
         }
         blockedEnergy[numBlockedEnergy] = post->entry->frequency;
         numBlockedEnergy++;
         addFrequencyToUserPaneList(post->entry->frequency);
         return(1); /** Remove since occupancy is too high and added to table **/
      }
   }
   return(0);
}


/***********************************************************************/
/*                                                                     */
/***********************************************************************/
int loadE3238sFilter(struct aFilterStruct **filter, int numFeatures, struct aFeatureDefinition **feature)
{

   strcpy(hopper[FREQ_LIST].name,   "CW Remove");
   hopper[FREQ_LIST].libRevision  = USER_FILTER_LIB_REVISION;
   hopper[FREQ_LIST].save_func    = saveCWFilter;
   hopper[FREQ_LIST].recall_func  = recallCWFilter;
   hopper[FREQ_LIST].setup_func   = NULL;
   hopper[FREQ_LIST].preset_func  = presetCWFilter;
   hopper[FREQ_LIST].desc_func    = CWFilterDescription;
   hopper[FREQ_LIST].exec_func    = CWFilter;
   hopper[FREQ_LIST].type         = PRE_FILTER_TYPE;
   
   strcpy(hopper[OCCUPANCY].name, "Occupancy");
   hopper[OCCUPANCY].libRevision  = USER_FILTER_LIB_REVISION;
   hopper[OCCUPANCY].save_func    = saveOccupancyFilter;
   hopper[OCCUPANCY].recall_func  = recallOccupancyFilter;
   hopper[OCCUPANCY].setup_func   = occupancyFilterUI;
   hopper[OCCUPANCY].preset_func  = presetOccupancyFilter;
   hopper[OCCUPANCY].desc_func    = occupancyFilterDescription;
   hopper[OCCUPANCY].exec_func    = doOccupancyFilter;
   hopper[OCCUPANCY].type         = POST_FILTER_TYPE;

   presetOccupancyFilter();
   presetCWFilter();
   *filter = hopper;
   return(NUM_FILTERS);
}

