; ------------------------------------------------------------------------------------------------

PRO eis_db::eis_get_available_db, LOCAL = local, HOST = host

host_struct  = {hostname: '', pm_hostname: '', pm_address:0}

remote_hosts = REPLICATE (host_struct, 1)

remote_hosts.hostname    = ['cdsa6.nascom.nasa.gov']
remote_hosts.pm_hostname = ['cdsm9.nascom.nasa.gov']
remote_hosts.pm_address  = [8080]

x = LOC_FILE (self.main_db + '.dbd', PATH = GETENV ('ZDBASE'), COUNT = cnt)

IF x [0] THEN BEGIN

   local = 1
   host  = host_struct

ENDIF ELSE BEGIN

; Set the found flag to 0 (FALSE).  When we find a responsive host, then we will set this to
; 1 (TRUE)

  found = 0

; Use i to index the list of remote hosts.

  i = 0

; Find the number remote hosts in the remote hosts lists.

  nhosts = N_ELEMENTS (remote_hosts)

; First we create an IDL_RPC_CLIENT object.  There are some parameters that we can specify, but
; none are really needed.  The object is designed to work straight out the box.

  rpc = OBJ_NEW ('IDL_RPC_CLIENT')

  WHILE (NOT found AND i LT nhosts) DO BEGIN

    IF rpc->RPCInit (HOSTNAME    = remote_hosts [i].hostname,     $
                     PM_ADDRESS  = remote_hosts [i].pm_address,   $
                     PM_HOSTNAME = remote_hosts [i].pm_hostname)  THEN BEGIN

       IF rpc->RPCSetVariable (NAME = 'tmp', VAL = 1) THEN BEGIN

          IF rpc->RPCGetVariable (NAME = 'tmp', VAL = v) THEN BEGIN

            IF v EQ 1 THEN BEGIN

               host  = remote_hosts [i]
               found = 1

            ENDIF ELSE BREAK

          ENDIF ELSE BREAK

       ENDIF ELSE BREAK

    ENDIF ELSE BREAK

  ENDWHILE

  IF NOT found THEN host = ''

  local = 0

  status  = rpc->RPCCleanup ()

; All done.  Get rid of the IDL RPC Client object and exit.

  OBJ_DESTROY, rpc

ENDELSE

RETURN

END

; ------------------------------------------------------------------------------------------------

FUNCTION eis_db::init, REMOTE = remote, _EXTRA = extra

; Set the names of the main and experiment databases

self.exp_db  = 'eis_experiment'
self.main_db = 'eis_main'

IF KEYWORD_SET (remote) THEN BEGIN

   self->eis_get_available_db, HOST = host, LOCAL = local

   IF local THEN BEGIN

      RETURN, self->db_tools::init (_EXTRA = extra)

   ENDIF

   IF N_TAGS (host) GT 0 THEN BEGIN

      res =   self->db_tools::init (/REMOTE,                           $
                                    /CLIENT,                           $
                                    HOST        = host.hostname,       $
                                    PM_ADDRESS  = host.pm_address,     $
                                    PM_HOSTNAME = host.pm_hostname,    $
                                    _EXTRA = extra)

      IF res EQ 0 THEN RETURN, 0

      res = self->send_remote_cmd ("setenv, 'ZDBASE=/solarsoft/solarb/eis/database/test/'")

      IF res EQ 0 THEN RETURN, 0

      RETURN, res

   ENDIF

   PRINT, "Remote host could not be found"

   RETURN, 0

ENDIF ELSE BEGIN

  RETURN, self->db_tools::init (_EXTRA = extra)

ENDELSE

END

; ------------------------------------------------------------------------------------------------

PRO eis_db::cleanup

self->db_tools::cleanup

RETURN

END

; ------------------------------------------------------------------------------------------------

PRO eis_db::add_main, rec, QUIET = quiet

; Verify that the record to be added to the database contains all the required fields.

status = self->check_record (self.main_db, rec, QUIET = quiet)

; If the check record method fails, then we will just return.  We will let the method take
; care printing out any error messages.

IF status [0] EQ -1 THEN RETURN

; Get the current time in TAI format.

GET_UTC, utc
now = UTC2TAI (utc)

; Lock the database

key = self->lock_database (self.main_db, EXPIRE = 900, WTIME = 180)

; Check if we managed to get the database.  If we did not, then we will just exit now.

IF key EQ 0 THEN RETURN

; Find the number of records we are adding into the database

nrec = N_ELEMENTS (rec)

; Make sure that none of the proposed entries use the same program number

;IF nrec GT 1 THEN BEGIN

;   prog_num = rec [SORT (rec.prog_num)].prog_num

;   w = WHERE (prog_num (1:*) - prog_num EQ 0, count)

;   IF count GT 0 THEN BEGIN

;       self.msg = 'Proposed entries use the same program number'

;       tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;       GOTO, FINISH

;   ENDIF

;ENDIF

; Reformat the times to millisecond accuracy.  This is necessary so that the times are
; written out in a controlled way.

rec.date_obs = DOUBLE ( STRING ( rec.date_obs, FORMAT = '(F15.3)' ))
rec.date_end = DOUBLE ( STRING ( rec.date_end, FORMAT = '(F15.3)' ))

; Perform some tests on each record that we are adding into the database.

FOR i = 0, nrec - 1 DO BEGIN

; Check that the PROG_ID referenced by the record actually exists in the PROGRAM database,
; but only if a non-zero value is used.

;    IF rec [i].prog_id GT 0 THEN BEGIN

;       r = self->db_rec_exists ( 'program', 'PROG_ID',  rec [i].prog_id, /QUIET)

;       IF NOT r THEN BEGIN

;          self.msg = 'Program ID : ' + STRTRIM (rec [i].prog_id, 1) + ' not found in database.'

;          tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;          GOTO, FINISH

;       ENDIF

;    ENDIF

; Make sure that the campaign number matches an entry in the EIS campaign database.
; -- This currently uses SOHO campaign database.  At some point it will have to be
; -- changed to the EIS database or removed altogether.

;    IF rec [i].cmp_no GT 0 THEN BEGIN

;       r = self->db_rec_exists ( 'campaign', 'CMP_NO',  rec [i].cmp_no, /QUIET)

;       IF NOT r THEN BEGIN

;          self.msg = 'Campaign Num : ' + STRTRIM (rec [i].cmp_no, 1) + ' was not found in database.'

;          tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;          GOTO, FINISH

;       ENDIF

;    ENDIF

; Make sure that the object code matches an entry in the object database.

;    IF rec [i].object NE '' THEN BEGIN

;       r = self->db_rec_exists ( 'object', 'OBJECT',  rec [i].object, /QUIET)

;       IF NOT r THEN BEGIN

;          self.msg = 'Object : ' + STRTRIM (rec [i].object, 1) + ' was not found in database.'

;          tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;          GOTO, FINISH

;       ENDIF

;    ENDIF

; Make sure that the Study ID and Study Variation match an entry in the EIS study database.

;    dbname = ['study_var', 'study_title', 'study']
;    items  = ['STUDY_ID', 'STUDYVAR']
;    vals   = [rec [i].study_id, rec [i].studyvar]

;    r = self->db_rec_exists ( dbname, items, vals, /QUIET)

;    IF NOT r THEN BEGIN

;       self.msg = 'Study ID/variation ' +       $
;                  STRTRIM (rec [i].study_id, 1) + ', ' +   $
;                  STRTRIM (rec [i].variation, 1) +     $
;                  ' was not found in database.'

;       tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;       GOTO, FINISH

;    ENDIF

; Make sure that the proposed entry is not already in the database.

;    r = self->db_rec_exists ( self.main_db, 'PROG_NUM',  rec [i].prog_num, /QUIET)

;    IF r THEN BEGIN

;       self.msg = 'Program Number : ' + STRTRIM (rec [i].prog_num, 1) + ' is already in the database.'

;       tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;       GOTO, FINISH

;    ENDIF

; Make sure that the stop data is greater then the start date.

    IF rec [i].date_end LE rec [i].date_obs THEN BEGIN

       self.msg = 'The stop date must be after the start date.'

       tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;       GOTO, FINISH

    ENDIF

;    IF rec [i].obt_end LE rec [i].obt_time THEN BEGIN

;       self.msg = 'The OBT stop date must be after the start date.'

;       tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;       GOTO, FINISH

;    ENDIF

; Determine whether the study FOV is pointed to a NOAA active region.

; First set the NOAA_AR tag to 0.  This is the value for no NOAA active
; retion within the FOV.

    rec [i].noaa_ar = 0

; Call GET_NAR to get the array of NOAA active region structures for each
; active region that was in existence on the day the study was
; run.
; (2018-Jul-19) JTM: Revised test on returned value.
    

    nar = GET_NAR (TAI2UTC (rec [i].date_obs))

; Check if GET_NAR returned any structures.

    ; IF N_TAGS (nar [0]) GT 0 THEN BEGIN
    if is_struct(nar) then begin

; Convert the field of view for the study to a box defined by
; (X,Y) and (X', Y')

       x0 = rec [i].xcen - rec [i].fovx / 2
       x1 = rec [i].xcen + rec [i].fovx / 2
       y0 = rec [i].ycen - rec [i].fovy / 2
       y1 = rec [i].ycen + rec [i].fovy / 2

       FOR ar = 0, N_ELEMENTS (nar) - 1 DO BEGIN

           nx = nar[ar].x
           ny = nar[ar].y

           IF nx GT x0 AND nx LT x1 AND ny GT y0 AND ny LT y1 THEN BEGIN

              rec [i].noaa_ar = nar[ar].noaa

              BREAK

           ENDIF

        ENDFOR

     ENDIF


; Check if a value is set for the comments tag.  If no value is set, then set it to 'N'

    IF rec [i].cmnt_entry EQ '' THEN rec [i].cmnt_entry = 'N'

; Set the date_mod tag to the current date/time.

    rec [i].date_mod = now

ENDFOR

; Call the db_rec_add method to add the new records into the database.

r = self->db_rec_add (self.main_db, rec, /NOLOCK, QUIET = quiet)

FINISH:

; Unlock the database

self->unlock_database, self.main_db

; OK, were done.

RETURN

END

; ------------------------------------------------------------------------------------------------

PRO eis_db::update_main, rec, index, QUIET = quiet

; Create a dummy record.

r = {EIS_MAIN_LIST}

; Get the current version of the record that we will be updating from the database.

self->db_get_record, self.main_db, index, r, QUIET = quiet

; Copy any fields record that should not be updated.

rec.CMNT_ENTRY = r.CMNT_ENTRY

; Verify that the record to be added to the database contains all the required fields.

status = self->check_record (self.main_db, rec, QUIET = quiet)

; If the check record method fails, then we will just return.  We will let the method take
; care printing out any error messages.

IF status [0] EQ -1 THEN RETURN

; Get the current time in TAI format.

GET_UTC, utc
now = UTC2TAI (utc)

; Lock the database

key = self->lock_database (self.main_db, EXPIRE = 900, WTIME = 180)

; Check if we managed to get the database.  If we did not, then we will just exit now.

IF key EQ 0 THEN RETURN

; Reformat the times to millisecond accuracy.  This is necessary so that the times are
; written out in a controlled way.

rec.date_obs = DOUBLE ( STRING ( rec.date_obs, FORMAT = '(F15.3)' ))
rec.date_end = DOUBLE ( STRING ( rec.date_end, FORMAT = '(F15.3)' ))

IF rec.date_obs GT r.date_obs THEN rec.date_obs = r.date_obs
IF rec.date_end LT r.date_end THEN rec.date_end = r.date_end

; Set the date_mod tag to the current date/time.

rec.date_mod = now

; Determine whether the study FOV is pointed to a NOAA active region.

; First set the NOAA_AR tag to 0.  This is the value for no NOAA active
; retion within the FOV.

IF r.noaa_ar EQ 0 THEN BEGIN

; First set the NOAA_AR tag to 0.  This is the value for no NOAA active
; retion within the FOV.

   rec.noaa_ar = 0

; Call GET_NAR to get the array of NOAA active region structures for each
; active region that was in existence on the day the study was
; run.
; (2018-Jul-19) JTM: Revised test on returned value.

   nar = GET_NAR (TAI2UTC (rec.date_obs))

; Check if GET_NAR returned any structures.

   ; IF N_TAGS (nar [0]) GT 0 THEN BEGIN
   if is_struct(nar) then begin

; Convert the field of view for the study to a box defined by
; (X,Y) and (X', Y')

      x0 = rec.xcen - rec.fovx / 2
      x1 = rec.xcen + rec.fovx / 2
      y0 = rec.ycen - rec.fovy / 2
      y1 = rec.ycen + rec.fovy / 2

      FOR ar = 0, N_ELEMENTS (nar) - 1 DO BEGIN

          nx = nar[ar].x
          ny = nar[ar].y

          IF nx GT x0 AND nx LT x1 AND ny GT y0 AND ny LT y1 THEN BEGIN

             rec.noaa_ar = nar[ar].noaa

             BREAK

         ENDIF

      ENDFOR

   ENDIF

ENDIF ELSE BEGIN

  rec.noaa_ar = r.noaa_ar

ENDELSE

; Call the db_rec_add method to add the new records into the database.

res = self->db_rec_add (self.main_db, rec, index, /NOLOCK, QUIET = quiet)

; Unlock the database

self->unlock_database, self.main_db

; OK, were done.

RETURN

END

; ------------------------------------------------------------------------------------------------


PRO eis_db::add_exper, rec, QUIET = quiet, STATUS = status

; Set dbname to the names of the experiment databases that we will be using.

dbname = [self.exp_db]

; Set status to 0.  If the procedure succeeds, then we will reset it to 1.

status = 0

; Get a description of the items in each of these databases.  If any of these dabases are
; not visible, then we will generate an error and quit.

; Try and get a description for database 0

self->get_db_info, dbname [0], DB_ITEM = dbitem0, DB_DESC = db0

; If this operation fails (db0 is not a structure) then generate an error.

IF N_TAGS (db0) LT 1 THEN BEGIN

   self.msg = 'Could not access database : ' + dbname [0]

   tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

   RETURN

ENDIF

; Try and get a description for database 1

;self->get_db_info, dbname [1], DB_ITEM = dbitem1, DB_DESC = db1

; If this operation fails (db1 is not a structure) then generate an error.

;IF N_TAGS (db1) LT 1 THEN BEGIN

;   self.msg = 'Could not access database : ' + dbname [1]

;   tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;   RETURN

;ENDIF

; Try and get a description for database 2

;self->get_db_info, dbname [2], DB_ITEM = dbitem2, DB_DESC = db2

; If this operation fails (db2 is not a structure) then generate an error.

;IF N_TAGS (db2) LT 1 THEN BEGIN

;   self.msg = 'Could not access database : ' + dbname [2]

;   tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;   RETURN

;ENDIF

; Verify that the record to be added to the experiment databases matches the database items
; in the experiment database.  Since we are checking against multiple databases, we will have
; to use the nostrict keyword.

chk = self->check_record (dbname, rec, QUIET = quiet, /NOSTRICT)

; If the check record method fails, then we will just return.  We will let the method take
; care printing out any error messages.

IF chk [0] EQ -1 THEN RETURN

; Get the current time in TAI format.

GET_UTC, utc
now = UTC2TAI (utc)

; Lock all of the experiment databases.  Since we are locking 3 databases at once, this
; requires a bit of work.

; First set up a keys array to keep the keys of databases that we manage to lock

;key = INTARR (3)

; Now, loop through and lock each database, in turn.

;FOR i = 0, 2 DO BEGIN

;   key [i] = self->lock_database (dbname [i], EXPIRE = 900, WTIME = 180, QUIET = quiet)

; Check if we managed to get the database.  If we did not, then we must go and unlock
; any database that we already have locked.

;   IF key [i] EQ 0 THEN BEGIN

; Go and unlock each locked database.  We will use the values from the key array
; just to make sure we don't unlock any database that we didn't lock in the first place.

;      FOR j = 0, i - 1 DO self->unlock_database, dbname [j], KEY = key [j], QUIET = quiet

; Ok, were done.  Return.

;      RETURN

;   ENDIF

;ENDFOR

; Lock the database

key = self->lock_database (self.exp_db, EXPIRE = 900, WTIME = 180)

; Check if we managed to get the database.  If we did not, then we will just exit now.

IF key EQ 0 THEN RETURN

; Find the name of each of the tags in the record that we are adding to the database.

item = TAG_NAMES (rec [0])

; Find the number of tags in each record structure.

nitem = N_ELEMENTS (item)

; Find the number of records we are adding into the database

nrec = N_ELEMENTS (rec)

; Make sure that none of the proposed entries use the same program number and
; sequence index combination.

IF nrec GT 1 THEN BEGIN

; Loop through each of the records, except the final record, which are being added
; to the database.

;   FOR i = 0, nrec - 2 DO BEGIN

; First check for duplicate program numbers

;       p = WHERE (rec [i].prog_num EQ rec [i + 1:*].prog_num)

; If we have duplicate program numbers, then check for duplicate sequence indices.

;       IF p [0] NE -1 THEN BEGIN

; Now check those records that have duplicate program numbers to see if any of them
; also duplicates the sequence index of the target record.

;          dup = WHERE (rec [i].seq_ind EQ (rec [i + 1:*]) [p].seq_ind)

; If dup [0] is not equal to -1 then we have found a duplicate.

;          IF dup [0] NE -1 THEN BEGIN

;  Ooops. Print an error message and return.

;              self.msg = 'Proposed entries use the same PROG_NUM and SEQ_IND'

;              tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;              GOTO, FINISH

;          ENDIF

;       ENDIF

;   ENDFOR

ENDIF

; Make sure that none of the proposed entries use the same sequence number

IF nrec GT 1 THEN BEGIN

;   seq_num = rec [SORT (rec.seq_num)].seq_num

;   w = WHERE (seq_num [1:*] - seq_num EQ 0, count)

;   IF count GT 0 THEN BEGIN

;       self.msg = 'Proposed entries use the same sequence number'

;       tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;       GOTO, FINISH

;   ENDIF

ENDIF

; Reformat the times to millisecond accuracy.  This is necessary so that the times are
; written out in a controlled way.

rec.date_obs = DOUBLE ( STRING ( rec.date_obs, FORMAT = '(F15.3)' ))
rec.date_end = DOUBLE ( STRING ( rec.date_end, FORMAT = '(F15.3)' ))
rec.obt_time = DOUBLE ( STRING ( rec.obt_time, FORMAT = '(F15.3)' ))
rec.obt_end  = DOUBLE ( STRING ( rec.obt_end,  FORMAT = '(F15.3)' ))

; Set the date_mod tag to the current date/time.

rec.date_mod = now

; Perform some tests on each record that we are adding into the database.

FOR i = 0, nrec - 1 DO BEGIN

; Make sure that the Raster ID and Raster Variation match an entry in the EIS study database.

;    dbase  = ['raster_var', 'raster']
;    items  = ['RAS_ID', 'RAS_VAR']
;    vals   = [rec [i].ras_id, rec [i].ras_var]

;    r = self->db_rec_exists ( dbase, items, vals, /QUIET)

;    IF NOT r THEN BEGIN

;       self.msg = 'Raster ID/variation ' +        $
;                  STRTRIM (rec [i].ras_id, 1) + ', ' +     $
;                  STRTRIM (rec [i].ras_var, 1) +     $
;                  ' was not found in the database.'

;       tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;       GOTO, FINISH

;    ENDIF


; Check that the data Window ID number matches an entry in the database.

;    r = self->db_rec_exists ( 'datawin', 'DW_ID',  rec [i].dw_id, /QUIET)

;    IF NOT r THEN BEGIN

;       self.msg = 'Data Window ID : ' + STRTRIM (rec [i].dw_id, 1) + ' not found in database.'

;       tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;       GOTO, FINISH

;    ENDIF

; Make sure that the proposed entry is not already in the database.

;    dbase  = [self.exp_db]
;    items  = ['PROG_ID', 'SEQ_IND']
;    vals   = [rec [i].prog_id, rec [i].seq_ind]

;    r = self->db_rec_exists (dbase, items, vals, /QUIET)

;    IF r THEN BEGIN

;       self.msg = 'Program number ' + STRTRIM (rec [i].prog_id, 1) + ' and ' + $
;                  'sequence index ' + STRTRIM (rec [i].seq_ind, 1) +     $
;                  ' are already in the database.'

;       tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;       GOTO, FINISH

;    ENDIF

;    r = self->db_rec_exists (self.exp_db, 'SEQ_NUM', rec [i].seq_num, /QUIET)

;    IF r THEN BEGIN

;       self.msg = 'Sequence number ' + STRTRIM (rec [i].seq_num, 1) +   $
;                  ' is already in the database.'

;       tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;       GOTO, FINISH

;    ENDIF

; Make sure that the stop data is greater then the start date.

    IF rec [i].date_end LE rec [i].date_obs THEN BEGIN

       self.msg = 'The stop date must be after the start date.'

       tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;       GOTO, FINISH

    ENDIF

;    IF rec [i].obt_end LE rec [i].obt_time THEN BEGIN

;       self.msg = 'The OBT stop date must be after the start date.'

;       tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

;       GOTO, FINISH

;    ENDIF

; Check if a value is set for the comments tag.  If no value is set, then set it to 'N'

;    IF rec [i].comments EQ '' THEN rec [i].comments = 'N'

; Set the date_mod tag to the current date/time.

;    rec [i].date_mod = now

ENDFOR

; Find the entry number in the main database.

; Create an array to hold pointers from records being added to the experiment database to
; the corresponding records in the main database.

main_ptr = LONARR (nrec)

; Open the main database.

DBOPEN, self.main_db

; Find the entry number in the main database for each record that we are adding to the
; experiment database.

FOR i = 0, nrec - 1 DO BEGIN

; Call DBFIND() to find corresponding index to record i in the main database.

   ind = DBFIND ('TL_ID = ' + STRTRIM (rec [i].tl_id, 1), /SILENT)

; Check if found an index into the main database.  If we didn't then we will just print
; out an error and quit.

   IF ind [0] LT 0 THEN BEGIN

      self.msg = 'Timeline ID ' + STRTRIM (rec [i].main_id, 1) +       $
                  ' could not be found in the main database.'

      tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

      GOTO, FINISH

   ENDIF

; Copy the index value we just found in the array of pointers into the main database

   main_ptr [i] = ind [0]

ENDFOR

; Close the main database.

DBCLOSE

; Get sample records for each of the experiment databases.

;sr0 = self->get_struct (dbitem0, QUIET = quiet)
;sr1 = self->get_struct (dbitem1, QUIET = quiet)
;sr2 = self->get_struct (dbitem2, QUIET = quiet)

; Create 3 N element arrays (where N is the number of records that are being added
; to the database) that will be used to update the actual experiment databases.

;r0 = REPLICATE (sr0, nrec)
;r1 = REPLICATE (sr1, nrec)
;r2 = REPLICATE (sr2, nrec)

; Get the names of the tags from each sample record

;r0tags = TAG_NAMES (sr0)
;r1tags = TAG_NAMES (sr1)
;r2tags = TAG_NAMES (sr2)

; Create the maps that relate the tags in the rec structure to the equivalent tags in
; each of the sample databases.

;m0 = INTARR (N_ELEMENTS (r0tags))
;m1 = INTARR (N_ELEMENTS (r1tags))
;m2 = INTARR (N_ELEMENTS (r2tags))

;FOR i = 0, N_ELEMENTS (r0tags) - 1 DO m0 [i] = (WHERE (r0tags [i] EQ item)) [0]
;FOR i = 0, N_ELEMENTS (r1tags) - 1 DO m1 [i] = (WHERE (r1tags [i] EQ item)) [0]
;FOR i = 0, N_ELEMENTS (r2tags) - 1 DO m2 [i] = (WHERE (r2tags [i] EQ item)) [0]

; Use Map 0 to copy the values from the appropiate tags of the record structure that
; was passed as a parameter to this procedure into the record stucture array that will
; be used to write into the experiment database.

;FOR i = 0, N_ELEMENTS (m0) - 1 DO IF m0 [i] NE -1 THEN r0.(i) = rec.(m0 [i])

; Add in the pointers to the main record.

;r0.main_ptr =  main_ptr
rec.main_ptr =  main_ptr

; Open the experiment database for writing

DBOPEN, dbname [0], 1

; Call the db_rec_add method to add the new records into the database.  Store the indices
; of any records that we have added to the database in e0

;e0 = self->db_rec_add (dbname [0], r0, /NOLOCK, QUIET = quiet)
e0 = self->db_rec_add (dbname [0], rec, /NOLOCK, QUIET = quiet)

; Check for any errors.  we will the let db_rec_add() do the actual error reporting.
; If an error is encountered when writing to this database, then we will just quit.

; IF e0 [0] EQ -1 THEN GOTO, FINISH

IF e0 [0] EQ -1 THEN BEGIN

   GOTO, FINISH

ENDIF

; Use Map 1 to copy the values from the appropiate tags of the record structure that
; was passed as a parameter to this procedure into the record stucture array that will
; be used to write into the experiment1 database.

; FOR i = 0, N_ELEMENTS (m1) - 1 DO IF m1 [i] NE -1 THEN r1.(i) = rec.(m1 [i])

; Add in the pointers to the experiment record.

; r1.exper0_ptr = e0

; Open the experiment1 database for writing

; DBOPEN, dbname [1], 1

; Call the db_rec_add method to add the new records into the database.  Store the indices
; of any records that we have added to the database in e1

; e1 = self->db_rec_add (dbname [1], r1, /NOLOCK, QUIET = quiet)

; Check for any errors.  we will the let db_rec_add() do the actual error reporting.
; If an error is encountered when writing to this database, then we will have to remove
; the entries we just made into the experiment database before quiting.

; IF e1 [0] EQ -1 THEN BEGIN

; Store the old value of !PRIV in oldpriv.  Reset !PRIV to a value of 3.

;   oldpriv = !PRIV
;   !PRIV = 3

; Remove the entries we just made into the experiment database.

;   DBDELETE, e0, dbname [0]

; Reset !PRIV back to its original value.

;   !PRIV = oldpriv

; Jump to the FINISH section of the method

;   GOTO, FINISH

; ENDIF

; Use Map 2 to copy the values from the appropiate tags of the record structure that
; was passed as a parameter to this procedure into the record stucture array that will
; be used to write into the experiment2 database.

; FOR i = 0, N_ELEMENTS (m2) - 1 DO IF m2 [i] NE -1 THEN r2.(i) = rec.(m2 [i])

; Add in the pointers to the experiment record.

; r2.exper_ptr = e1

; Open the experiment1 database for writing

; DBOPEN, dbname [2], 1

; Call the db_rec_add method to add the new records into the database.  Store the indices
; of any records that we have added to the database in e1

; e2 = self->db_rec_add (dbname [2], r2, /NOLOCK, QUIET = quiet)

; Check for any errors.  we will the let db_rec_add() do the actual error reporting.
; If an error is encountered when writing to this database, then we will have to remove
; the entries we just made into the experiment database before quiting.

; IF e2 [0] EQ -1 THEN BEGIN

; Store the old value of !PRIV in oldpriv.  Reset !PRIV to a value of 3.

;   oldpriv = !PRIV
;   !PRIV = 3

; Remove the entries we just made into the experiment database.

;   DBDELETE, e0, dbname [0]

; Remove the entries we just made into the experiment1 database.

;   DBDELETE, e1, dbname [1]

; Reset !PRIV back to its original value.

;   !PRIV = oldpriv

; Jump to the FINISH section of the method

;   GOTO, FINISH

; ENDIF

; It worked.  Reset status to 1.

status = 1

FINISH:

; Unlock each of the experiment databases that were previously locked.

;FOR i = 0, 2 DO BEGIN

;   self->unlock_database, dbname [i], QUIET = quiet

;ENDFOR

self->unlock_database, dbname [0], QUIET = quiet

; OK, were done.

RETURN

END

; -----------------------------------------------------------------------------------------------------

PRO eis_db::update_exper, rec, index, QUIET = quiet

r = {EIS_EXPER_LIST}

; Get the current version of the record that we will be updating from the database.

self->db_get_record, self.exp_db, index, r, QUIET = quiet

; Copy any fields record that should not be updated.

rec.CMNT_ENTRY = r.CMNT_ENTRY
rec.MAIN_PTR   = r.MAIN_PTR

; Verify that the record to be added to the experiment databases matches the database items
; in the experiment database.  Since we are checking against multiple databases, we will have
; to use the nostrict keyword.

chk = self->check_record (self.exp_db, rec, QUIET = quiet, /NOSTRICT)

; If the check record method fails, then we will just return.  We will let the method take
; care printing out any error messages.

IF chk [0] EQ -1 THEN RETURN

; Get the current time in TAI format.

GET_UTC, utc
now = UTC2TAI (utc)

key = self->lock_database (self.exp_db, EXPIRE = 900, WTIME = 180)

; Check if we managed to get the database.  If we did not, then we will just exit now.

IF key EQ 0 THEN RETURN

; Reformat the times to millisecond accuracy.  This is necessary so that the times are
; written out in a controlled way.

rec.date_obs = DOUBLE ( STRING ( rec.date_obs, FORMAT = '(F15.3)' ))
rec.date_end = DOUBLE ( STRING ( rec.date_end, FORMAT = '(F15.3)' ))
rec.obt_time = DOUBLE ( STRING ( rec.obt_time, FORMAT = '(F15.3)' ))
rec.obt_end  = DOUBLE ( STRING ( rec.obt_end,  FORMAT = '(F15.3)' ))

; Set the date_mod tag to the current date/time.

rec.date_mod = now


; Make sure that the stop data is greater then the start date.

IF rec.date_end LE rec.date_obs THEN BEGIN

   self.msg = 'The stop date must be after the start date.'

   tmp = self->error ( /INFORMATIONAL, QUIET = quiet )

; Unlock the experiment database that was previously locked.

   self->unlock_database, self.exp_db, QUIET = quiet

ENDIF

; Open the experiment database for writing

DBOPEN, self.exp_db, 1

; Set the date_mod tag to the current date/time.

; rec.date_mod = now

; Call the db_rec_add method to add the new records into the database.

res = self->db_rec_add (self.exp_db, rec, index, /NOLOCK, QUIET = quiet)

; Unlock the experiment database that was previously locked.

self->unlock_database, self.exp_db, QUIET = quiet

; OK, were done.

RETURN

END

; -----------------------------------------------------------------------------------------------------

FUNCTION eis_db::db_list_add, dbase,                             $
                              list,                              $
                              index,                             $
                              NOLOCK = nolock,                   $
                              QUIET = quiet,                     $
                              NOINDEX = noindex,                 $
                              NOVERIFY = noverify,               $
                              NOSORT = NOSORT,                   $
                              NOSTRICT = nostrict,               $
                              CONV_TIMES = conv_times,           $
                              RESET_DATE_MOD = reset_date_mod


; Check if the conv_times keyword is set.  If it is not then we can skip
; the next section.

IF NOT KEYWORD_SET (conv_times) THEN BEGIN

; Loop to check each structure in the list.

   FOR i = 0, N_ELEMENTS (list) - 1 DO BEGIN

      CASE list [i].key OF

        'DATE_OBS' : list [i].val = UTC2TAI (ANYTIM2UTC (list [i].val))
        'DATE_END' : list [i].val = UTC2TAI (ANYTIM2UTC (list [i].val))
        'OBT_TIME' : list [i].val = UTC2TAI (ANYTIM2UTC (list [i].val))
        'OBT_END'  : list [i].val = UTC2TAI (ANYTIM2UTC (list [i].val))
        'DATE_MOD' : list [i].val = UTC2TAI (ANYTIM2UTC (list [i].val))

         ELSE :

      ENDCASE

   ENDFOR

ENDIF

; Check if the RESET_DATE_MOD keyword is set.  If it is, then we try to reset
; the data DATE_MOD value to the current time.

IF KEYWORD_SET (conv_times) THEN BEGIN

; Loop to check each structure in the list.  If we find one labeled DATE_MOD, then
; we reset its value to the current UTC time.

   FOR i = 0, N_ELEMENTS (list) - 1 DO BEGIN

      CASE list [i].key OF

        'DATE_MOD' : BEGIN

            GET_UTC, utc

            list [i].val = UTC2TAI (utc)

            END


         ELSE :

      ENDCASE

   ENDFOR

ENDIF


RETURN, self->db_tools::db_list_add (dbase,                  $
                                     list,                   $
                                     index,                  $
                                     NOLOCK = nolock,        $
                                     QUIET = quiet,          $
                                     NOINDEX = noindex,      $
                                     NOVERIFY = noverify,    $
                                     NOSORT = NOSORT,        $
                                     NOSTRICT = nostrict     $
                                     )
END


; -----------------------------------------------------------------------------------------------------

FUNCTION eis_db::db_get_full_record, dbase, index, CONV_TIMES = conv_times

; call the base version of the db_get_full_record() method.

rval = self->db_tools::db_get_full_record (dbase, index)

; Check if the conv_times keyword is set.  If it is not then we can just return
; the result of the base method, since this is the only keyword that we process.

IF NOT KEYWORD_SET (conv_times) THEN RETURN, rval

FOR i = 0, N_ELEMENTS (rval) - 1 DO BEGIN

   CASE rval [i].key OF

     'DATE_OBS' : rval [i].val = UTC2STR (TAI2UTC (DOUBLE (rval [i].val)), /VMS, /TRUNC)
     'DATE_END' : rval [i].val = UTC2STR (TAI2UTC (DOUBLE (rval [i].val)), /VMS, /TRUNC)
     'OBT_TIME' : rval [i].val = UTC2STR (TAI2UTC (DOUBLE (rval [i].val)), /VMS, /TRUNC)
     'OBT_END'  : rval [i].val = UTC2STR (TAI2UTC (DOUBLE (rval [i].val)), /VMS, /TRUNC)
     'DATE_MOD' : rval [i].val = UTC2STR (TAI2UTC (DOUBLE (rval [i].val)), /VMS, /TRUNC)

      ELSE :

   ENDCASE

ENDFOR

RETURN, rval

END

; -----------------------------------------------------------------------------------------------------

PRO eis_db::db_get_record, dbase,                                   $
                           index,                                   $
                           rec,                                     $
                           QUIET = quiet,                           $
                           ALL_FIELDS = all_fields,                 $
                           RETURN_LIST = return_list,               $
                           CONV_TIMES = conv_times

; call the base version of the db_get_record() method.

self->db_tools::db_get_record, dbase,                                   $
                               index,                                   $
                               rec,                                     $
                               QUIET = quiet,                           $
                               ALL_FIELDS = all_fields,                  $
                               RETURN_LIST = return_list

; Check if the conv_times keyword is set.  If it is not then we can just return
; since this is the only keyword that we process.

IF NOT KEYWORD_SET (conv_times) THEN RETURN

IF KEYWORD_SET (return_list) THEN BEGIN

   FOR i = 0, N_ELEMENTS (rec) - 1 DO BEGIN

       CASE rec [i].key OF

         'DATE_OBS' : rec [i].val = UTC2STR (TAI2UTC (DOUBLE (rec [i].val)), /VMS, /TRUNC)
         'DATE_END' : rec [i].val = UTC2STR (TAI2UTC (DOUBLE (rec [i].val)), /VMS, /TRUNC)
         'OBT_TIME' : rec [i].val = UTC2STR (TAI2UTC (DOUBLE (rec [i].val)), /VMS, /TRUNC)
         'OBT_END'  : rec [i].val = UTC2STR (TAI2UTC (DOUBLE (rec [i].val)), /VMS, /TRUNC)
         'DATE_MOD' : rec [i].val = UTC2STR (TAI2UTC (DOUBLE (rec [i].val)), /VMS, /TRUNC)

         ELSE :

       ENDCASE

   ENDFOR

ENDIF

RETURN

END

; -----------------------------------------------------------------------------------------------------


FUNCTION eis_db::eis_fname_to_db_fname, eis_fname

; Set ascii_underscore to the ASCII value of the underscore character.

ascii_underscore = (BYTE ('_')) [0]

; Set ascii_coma to the ASCII value of a colon

ascii_colon = (BYTE (':')) [0]

; Convert the file name into a string of bytes.

fname_as_bytes = BYTE (eis_fname)

; Find any instances of colons used in the file name.

colon_loc = WHERE (fname_as_bytes EQ ascii_coma)

; Check if we found any colon locations.  If we did find colons, replace them with the
; underscore character.

IF colon_loc [0] NE -1 THEN fname_as_bytes [colon_loc] = ascii_underscore


RETURN, 0

END

;
; -----------------------------------------------------------------------------------------------------
;
; Query database values between 2 time points.

PRO eis_db::querry, p1, p2, p3, p4, SEARCH = search, DB = db, INDEX = index

IF N_PARAMS () LT 3 THEN RETURN

; Set the default database to search

main = 1

; Check if parameters 1 and 2 are both of type double.  If they are both of this
; type, then we will assume we are searching for records in a specific time
; period.  Otherwise we will search based on the value contained in parameter 1.

search_by_time = (self->get_type (p1) EQ 5) AND (self->get_type (p2) EQ 5)

IF search_by_time THEN BEGIN

; Copy the values passed as the start time and stop time into t0 and t1.

   t0 = p1
   t1 = p2

; Convert the input dates to TAI format.

   IF DATATYPE (t0, 2) NE 5 THEN t0 = UTC2TAI (ANYTIM2UTC (t0))

   IF DATATYPE (t1, 2) NE 5 THEN t1 = UTC2TAI (ANYTIM2UTC (t1))

; Convert the start and end dates to database querry strings.

   q0 = 'DATE_OBS > ' + TRIM (t0, '(F15.3)')

   q1 = 'DATE_END < ' + TRIM (t1, '(F15.3)')

; Create the database querry to search for the requested records

   test = q0 + ', ' + q1 + ', TL_ID > 0'

ENDIF ELSE BEGIN

; Create the database querry to search for the requested records

   test = 'TL_ID = ' + TRIM (p1)

ENDELSE

; If a search on other database fields is requested, then set up the test string
; to use the additional search parameters.


IF KEYWORD_SET (search) THEN BEGIN

; Check if search contains a string (or array of strings) with a search query.

   ss = WHERE (TRIM (search) NE '')

; If it does, then eliminate any empty strings and add the test string to each
; search string.

   IF ss [0] NE -1 THEN BEGIN

      search = search [ss]

      test = test + ', ' + search

   ENDIF

ENDIF

; Set up the corect database.

IF KEYWORD_SET (db) THEN BEGIN

   db = STRUPCASE (TRIM (db))

   IF STRMID (db, 0, 3) EQ 'EXP' THEN main = 0

   IF db EQ SELF.MAIN_DB THEN main = 1

ENDIF

IF main THEN BEGIN

;-- create a sample record to return the results for the search in.

   obs = {EIS_MAIN_LIST}

;-- set the database to main

   dbase = self.main_db


ENDIF ELSE BEGIN

   obs = {EIS_EXPER_LIST}

   dbase = self.exp_db

ENDELSE

; do the search

index = self->db_find_all_records ( dbase, test, COUNT = nrec )

IF nrec GT 0 THEN self->db_get_record, dbase, index, obs


IF nrec GT 0 THEN BEGIN

   s = SORT (obs.date_obs)
   obs   = obs [s]
   index = index [s]

ENDIF ELSE BEGIN

   obs   = -1
   index = -1

ENDELSE

IF search_by_time THEN BEGIN

   p3 = obs
   p4 = nrec

ENDIF ELSE BEGIN

   p2 = obs
   p3 = nrec

ENDELSE

RETURN

END

; ------------------------------------------------------------------------------------------------

FUNCTION eis_db::db_rec_exists_main, item, val, INDEX = index, QUIET = quiet

RETURN, self->db_tools::db_rec_exists (self.main_db, item, val, INDEX = index, QUIET = quiet)

END

; ------------------------------------------------------------------------------------------------

FUNCTION eis_db::db_rec_exists_exp, item, val, INDEX = index, QUIET = quiet

RETURN, self->db_tools::db_rec_exists (self.exp_db, item, val, INDEX = index, QUIET = quiet)

END

; ------------------------------------------------------------------------------------------------

FUNCTION eis_db::main_db_name

RETURN, self.main_db

END

; ------------------------------------------------------------------------------------------------

FUNCTION eis_db::exp_db_name

RETURN, self.exp_db

END

; ------------------------------------------------------------------------------------------------

FUNCTION eis_db::get_main_record

RETURN, make_eis_main_list ()

END

; ------------------------------------------------------------------------------------------------

FUNCTION eis_db::get_exper_record

RETURN, make_eis_exper_list ()

END

; ------------------------------------------------------------------------------------------------

FUNCTION eis_db::check_db_exist


m = LOC_FILE (self.main_db + '.dbd', PATH = GETENV ('ZDBASE'))
e = LOC_FILE (self.exp_db  + '.dbd', PATH = GETENV ('ZDBASE'))

IF m EQ '' OR e EQ '' THEN result = 0 ELSE result = 1

RETURN, result

END

; ------------------------------------------------------------------------------------------------

PRO eis_db__define

obj = { EIS_DB,             $ ; The object class name.
        main_db:   '',      $ ; Name of the main database
        exp_db:    '',      $ ; Name of the experiment database
        INHERITS db_tools,  $ ; Subclasses the DB_TOOLS object
        INHERITS olib       $ ; Subclasses the OLIB object
      }

END

