/*=========================================================================
  Star4.c -- This module builds and submits a requester in response to
  the user's choice of 'Set Parameters' from the parameters menu
  of the Star Chart program. The requester lets the user modify the 
  longitude, latitude, date, time and observed horizon for their observing
  location.
  

  Credits for Star Chart:
       Robert L. Hill of the Orange County, CA. Amiga Friends User Group
                      wrote the original version of StarChart in AmigaBasic
                      The star data and many of the main functions of this
                      version are derived from that program.

       Ray R. Larson  wrote the c version 1.0 of StarChart, 'intuitionizing'
                      and enhancing the speed and functions of the original.

  Copyright (c) 1986 by Ray R. Larson
  
  This program may be freely distributed and copied, but may not be sold
  without the permission of the author. If you modify or enhance it, 
  please include the above credits (and please send me a copy!).

Ray R. Larson
6425 Central Ave. #304
El Cerrito, CA 94530

BitNet  LARSON@UCBCMSA
=========================================================================*/
/*------------Header file for all of the standard stuff----*/ 
/*-------------plus definitions of global structures-------*/
#include "star.h" 

/*  Definitions for Gadget ID numbers  (see Star3.c)         */ 
#define  LONGDEGGAD   0
#define  LONGMINGAD   1
#define  LATDEGGAD   2
#define  YEARGAD   3
#define  MONGAD   4
#define  DAYGAD   5
#define  HOURGAD   6
#define  MINGAD   7
#define  SECGAD   8
#define  NORTHGAD   9
#define  SOUTHGAD   10
#define  CANGAD   11
#define  OKGAD   12
/* another macro */
#define ABS(x) ((x<0)?(-(x)):(x))

/****************************************************************************
 *  Global Structures 
 ***************************************************************************/

/* the newwindow structure used in GT */
extern struct NewWindow nw;
extern struct Window *w;
extern SHORT SecondsCount;
extern struct ParamStruct Parms;
extern SHORT Dd[],DSum[]; /* tables of days per month- in starglb.c */

/****************************************************************************
 * External function definitions
 ***************************************************************************/
extern struct IntuiMessage *GetMsg();

/****************************************************************************
 * Requester structure and global pointer to it's gadget list.
 ***************************************************************************/
struct Requester ParamRequester;
struct ParamStruct workParms;

extern struct Gadget *ParamGads, north, south, day;

/* Integer string gadgets - defined in star3.c */
extern struct Gadget sec, min, hour, day, mon, year,
       LatDeg, LongMin, LongDeg;

/**********************************************************************
 *  Border Definitions for the requester
 **********************************************************************/

SHORT Req_Pairs2[] = {
  1,     1,   
  358,     1,   
  358,     188,   
  1,     188,   
  1,     1    
};
SHORT Req_Pairs1[] = {
  0,     0,   
  362,     0,   
  362,     192,   
  0,     192,   
  0,     0    
};

struct Border Req_bord_2 = {
  2, 2,       /* LeftEdge, TopEdge */
  1,  7,  JAM1,  /* FrontPen, BackPen, DrawMode*/
  5,             /* Count of XY pairs */  
  (SHORT *)&Req_Pairs2, /* XY pairs */
  NULL           /* Next Border */
};

struct Border Req_bord_1 = {
  0,  0,       /* LeftEdge, TopEdge */
  7,  3,  JAM1,  /* FrontPen, BackPen, DrawMode*/
  5,             /* Count of XY pairs */  
  (SHORT *)&Req_Pairs1, /* XY pairs */
  &Req_bord_2           /* Next Border */
};

extern struct TextAttr TxtAt_BU;

struct IntuiText Req_Text = {
   2, 7,     /* FrontPen, BackPen */
   JAM2,       /* DrawMode */
   50, 1,     /* LeftEdge, TopEdge */
   &TxtAt_BU, /* ITextFont Pointer */ 
   /* The IText */
   (UBYTE *)"    Parameters for Star Chart    ",
   NULL    /* NextText */
 };

extern struct TextAttr TxtAt_B;

struct IntuiText Error_Text = {
   3, 2,     /* FrontPen, BackPen */
   JAM2,       /* DrawMode */
   0, 0,     /* LeftEdge, TopEdge */
   &TxtAt_B, /* ITextFont Pointer */ 
   /* The IText */
   (UBYTE *)"?",
   NULL    /* NextText */
 };

/****************************************************************************
 *               The Code part of the GetParam module.
 ***************************************************************************/
GetParams()
{
  BOOL cancelchosen;
  LONG iflags;
  struct RastPort *reqrp;


  /* fix the IDCMP flags to report requester set and clear */
  iflags = nw.IDCMPFlags;
  iflags |= REQCLEAR;
  ModifyIDCMP(w,iflags);
  
 
      /* initialize the text Param requester */ 
      InitParamReq();

      /* Put up the requester  */ 
      Request(&ParamRequester,w);
      
      /* set the north/south imagery */
       reqrp = ParamRequester.ReqLayer->rp;

       if(workParms.Horizon == 0)
         { SetDrMd(reqrp, COMPLEMENT);
           RectFill(reqrp,94L,153L,158L,169L);
           SetDrMd(reqrp, JAM1);
          }
       else
          { SetDrMd(reqrp, COMPLEMENT);
           RectFill(reqrp,206L,153L,274L,169L);
           SetDrMd(reqrp, JAM1);
          }

      /* let the user fill in the parameters and wait for a cancel or ok. */  
      cancelchosen = WaitParamReq(reqrp);
      
      /* if the user cancelled, just return -- otherwise set Parms and    */
      /* re-calculate and redisplay the star chart.                       */
      if (cancelchosen) return(0);
      
      /* figure out current siderial time, etc. and update the Parms struct */
      CalcParams(&workParms);
      Parms = workParms;
      
      /* recalculate star positions and display them. */
      DisplayStars();
      DisplayAllText();
      
} /* GetParams */

/*
 *  Init the Itext Param requester
 */
InitParamReq()
{ 
  struct RastPort *reqrp;
  
  InitRequester(&ParamRequester);
  ParamRequester.LeftEdge  = 110;
  ParamRequester.TopEdge   = 2;
  ParamRequester.Width     = 363;
  ParamRequester.Height    = 193;
  ParamRequester.ReqGadget = ParamGads;
  ParamRequester.ReqText   = &Req_Text;
  ParamRequester.BackFill  = 2;
  ParamRequester.Flags     = 0;
  ParamRequester.ReqBorder = &Req_bord_1;
  
  /* copy the parms structure */
  workParms = Parms;

   InitStringGad(&sec,SecondsCount);
   InitStringGad(&min,workParms.Minute);
   InitStringGad(&hour,workParms.Hour);
   InitStringGad(&day,workParms.Day);
   InitStringGad(&mon,workParms.Month);
   InitStringGad(&year,workParms.Year);
   InitStringGad(&LatDeg,workParms.Latitude);
   InitStringGad(&LongMin,workParms.LongitudeMin);
   InitStringGad(&LongDeg,workParms.LongitudeDeg);


} /* InitParamReq */


InitStringGad(gad,val)
struct Gadget *gad;
SHORT val;
{
  struct StringInfo *si;
  
  si = (struct StringInfo *)gad->SpecialInfo;
  sprintf(si->Buffer,"%d",val);
  si->NumChars = (LONG)strlen(si->Buffer);
  *(si->UndoBuffer) = '\0';
}

  
/****************************************************************************
 *  WaitParamReq - permit user to change the values in the parameter gadgets
 *                 and check the values they install until either the 
 *                 CANCEL gadget is clicked or the OK gadget is clicked AND
 *                 there are no outstanding input errors
 ***************************************************************************/
BOOL WaitParamReq(reqrp)
struct RastPort *reqrp;
{
  ULONG  class = GADGETDOWN;
  USHORT Param = 0;
  USHORT selected = 0;
  struct IntuiMessage *message;
  struct Gadget *gadget, *lastgad;
  BOOL cancelflag = FALSE;
  LONG  errorflag = 0L, checkvals();

  lastgad =  NULL;

  while (class != REQCLEAR)
    {
    if ((message=(struct IntuiMessage *) GetMsg(w->UserPort)) == 0L)
      {
      Wait(1L<<w->UserPort->mp_SigBit);
      continue;
      }

    class  = message->Class;
    gadget = (struct Gadget *) message->IAddress;
    ReplyMsg(message);
    switch (class)
      {
      case GADGETDOWN: /* check to see if the user clicked on a new */
		       /* gadget withou hitting return on the old one */
		       /* if so, treat this like a gadgetup for the   */
		       /* previous gadget.   			      */

		      if (lastgad == NULL) 
			 errorflag = checkvals(reqrp,gadget,errorflag,class);
                      else
			 errorflag = checkvals(reqrp,lastgad,errorflag,class);
           	      lastgad = gadget;
                      break;

      case GADGETUP: 
	     switch (gadget->GadgetID)
		{
		case  NORTHGAD: 
                      if(workParms.Horizon != 0)
                        { SetDrMd(reqrp, COMPLEMENT);
                          RectFill(reqrp,94L,153L,158L,169L);
                          RectFill(reqrp,206L,153L,274L,169L);
                          SetDrMd(reqrp, JAM1);
                         }
			 workParms.Horizon = 0;
			 break;   
			 
		case  SOUTHGAD: 
                      if(workParms.Horizon != 1)
                        { SetDrMd(reqrp, COMPLEMENT);
                          RectFill(reqrp,94L,153L,158L,169L);
           		  RectFill(reqrp,206L,153L,274L,169L);
                          SetDrMd(reqrp, JAM1);
                         }
			 workParms.Horizon = 1;
			 break;   
		

		case  CANGAD : 
		             cancelflag = TRUE;
                             break; 
 
               	case  OKGAD  :
 			     if (errorflag == 0) 
			         EndRequest(&ParamRequester,w);
			     else DisplayBeep(w->WScreen);    
			     break;

                default: errorflag = checkvals(reqrp,gadget,errorflag,class);

             } /* end of GADGETUP switch */

        } /* end of CLASS switch */
    }/* end of while loop */
    

return(cancelflag);
       
} /* WaitParamReq */
   
/****************************************************************************
 *  checkvals - verify that the values returned by the integer gadgets are
 *              in the appropriate range for the parameter.
  ***************************************************************************/
LONG checkvals(reqrp,checkgad,errorflag,class)
struct RastPort *reqrp;
struct Gadget *checkgad;
LONG errorflag;
ULONG class;
{
  struct StringInfo *si;
  BOOL  inputerror = FALSE;
  LONG  checknum, pendingerror;

   if (checkgad == NULL) return(errorflag);
   pendingerror = errorflag;
 
   si = (struct StringInfo *)checkgad->SpecialInfo;
   if (si == NULL) return(errorflag);

  /* this is kind of a kludge, but necessary since Intuition will have set */
  /* LongInt to zero, IF the user clicks on a gadget THEN  clicks on       */
  /* another, without hitting return on the old one and not modifying the  */
  /* contents of the string buffer, the LongInt value for the gadget is    */
  /* reported as zero.							   */ 
  if (class == GADGETUP) checknum = si->LongInt;
  else checknum = atol(si->Buffer);
     
  /* check the values input */
  switch (checkgad->GadgetID)
       {
	case  LONGDEGGAD: 
	      if (INRANGE(checknum,-180L,180L))
	        { workParms.LongitudeDeg = checknum;
	          errorflag &= ~(1L << LONGDEGGAD);
		  break;
		}
	      else inputerror = TRUE;
	      break;
			
	case  LONGMINGAD:
	      if (INRANGE(checknum,0L,59L))
	        { workParms.LongitudeMin = checknum;
	          errorflag &= ~(1L << LONGMINGAD);
		  break;
		}
	      else inputerror = TRUE;
	      break;   
			
	case  LATDEGGAD: 
	      if (INRANGE(checknum,0L,90L))
	        { workParms.Latitude = checknum;
	          errorflag &= ~(1L << LATDEGGAD);
		  break;
		}
	      else inputerror = TRUE;
		break;   
			
	case  YEARGAD:
	      if (INRANGE(checknum,1900L,2100L))
	        { workParms.Year = checknum;
	          errorflag &= ~(1L << YEARGAD);
		  break;
		}
	      else inputerror = TRUE;
		break;   
 			
	case  MONGAD:
	      if (INRANGE(checknum,1L,12L))
	        { workParms.Month = checknum;
	          errorflag &= ~(1L << MONGAD);
		  if ((INRANGE(workParms.Day,1,Dd[workParms.Month])) == 0)
		     { 
	               checkgad = &day;
		       si = (struct StringInfo *)checkgad->SpecialInfo;
                       inputerror = TRUE;
		       break;
		      }
		 break;
		}
	      else inputerror = TRUE;
		break;   
 			
	case  DAYGAD:
	      if (INRANGE(checknum,1L,(LONG)Dd[workParms.Month]))
	        { workParms.Day = checknum;
	          errorflag &= ~(1L << DAYGAD);
		  break;
		}
	      else inputerror = TRUE;
	      break;   
 			
	case  HOURGAD:
	      if (INRANGE(checknum,0L,23L))
	        { workParms.Hour = checknum;
	          errorflag &= ~(1L << HOURGAD);
		  break;
		}
	      else inputerror = TRUE;
	      break;   
 			
	case  MINGAD:
	      if (INRANGE(checknum,0L,59L))
	        { workParms.Minute = checknum;
	          errorflag &= ~(1L << MINGAD);
		  break;
		}
	      else inputerror = TRUE;
		 break;   
			 
	case  SECGAD:
	      if (INRANGE(checknum,0L,59L))
	        { SecondsCount = checknum;
	          errorflag &= ~(1L << SECGAD);
		  break;
		}
	      else inputerror = TRUE;
		 break;   
	 } /* end of Gadget id switch */

    if (inputerror == TRUE) /* put a "?" next to the gadget */
      { 
        errorflag |= (1L << checkgad->GadgetID);
        DisplayBeep(w->WScreen);
        PrintIText(reqrp,&Error_Text,
	      (LONG)(checkgad->LeftEdge - 10),(LONG)checkgad->TopEdge);
      }

    if ((pendingerror != errorflag) && (inputerror == FALSE))
      { /* user must have fixed an error, so turn off the ? */
        pendingerror = errorflag;
        Error_Text.FrontPen = 2;
        PrintIText(reqrp,&Error_Text,
	      (LONG)(checkgad->LeftEdge - 10),(LONG)checkgad->TopEdge);
        Error_Text.FrontPen = 3;
      }
return(errorflag);
       
} /* end of checkvals */


/*************************************************************************
 *  CalcParams - Calculate local siderial time (Lst) and Greenwich
 *               siderial time based on the current work parameters.
 ************************************************************************/
CalcParams(p)
struct ParamStruct *p;
{
  FLOAT workhours, LocalOffset, MeanLocTime;
  FLOAT CalcGst(); /* calcGst is in starglb.c */
  SHORT degrees, temp;

  
  degrees = ABS(p->LongitudeDeg) % 15;
  workhours = (FLOAT)degrees 
		+ ((FLOAT)p->LongitudeMin / 60.0);
  LocalOffset = (workhours * 4.0)/60.0;
  
 /* get Greenwich time */
  p->Gst = CalcGst(p->Day,p->Month);
  workhours = (FLOAT)p->Hour + ((FLOAT)p->Minute / 60.0);
  if(p->LongitudeDeg < 0)
     MeanLocTime = workhours + LocalOffset;
  else 
     MeanLocTime = workhours - LocalOffset;
     
  /* local siderial time */
  p->Lst = MeanLocTime + p->Gst;
  if (p->Lst > 24.0) 
     p->Lst -= 24.0;
  if (p->Lst < 0) 
     p->Lst += 24;
  
  /* set siderial times in the workparms */
  temp = (SHORT)(p->Lst * 60.0);
  p->Hr = temp / 60;
  p->Mn = temp % 60;
}

