#include "entry.h"
#include "osfile_public.h"
#include "msg.h"
#include "opus_exceptions.h"

//////////////////////////////////////////////////////////////////////////
//
// Method: File_status_lock::File_status_lock
//
// Purpose: 
//
//    The File_status_lock object constructor.
//
// Description:
//
//    The constructor creates an Opus_lock_file object for the indicated
//    status file, if supplied.
//
// Returns:
//
//    none
//
// Exceptions thrown:
//
//    EFROM->File_status_lock::gen_key
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 10/18/00  41103    WMiller     Initial code
// 10/29/02  46765    Sontag      Standardizing for the gcc 3.2 compiler
//
//////////////////////////////////////////////////////////////////////////

template<class T>
File_status_lock<T>::File_status_lock() : locked_ent(0)
{
   // empty block
}

template<class T>
File_status_lock<T>::File_status_lock(
                             const T* ent,           // I - status file to lock
                             const std::string& dir) // I - directory
                                                    //     containing the
                                                    //     status files
                             : locked_ent(0)
{
   gen_key(ent, dir);
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_status_lock::~File_status_lock
//
// Purpose: 
//
//    The File_status_lock object destructor.
//
// Description:
//
//    The destructor destroys the object and deletes its internal copy
//    of the target Entry object.
//
// Returns:
//
//    none
//
// Exceptions thrown:
//
//    none
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 10/18/00  41103    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
File_status_lock<T>::~File_status_lock()
{
   delete locked_ent;
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_status_lock::lock
//
// Purpose: 
//
//    Attempt to lock the status file.
//
// Description:
//
//    An attempt is made to lock the target file up to three times.
//
// Returns:
//
//    none
//
// Exceptions thrown:
//
//    EFROM->Opus_lock_file::lock
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 10/18/00  41103    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
void File_status_lock<T>::lock()
{
   Opus_lock_file::lock();   // generate lock file
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_status_lock::release
//
// Purpose: 
//
//    Release lock on status file.
//
// Description:
//
//    This method deletes the lock file, if one is present and the file
//    is locked.
//
// Returns:
//
//    none
//
// Exceptions thrown:
//
//    EFROM->Opus_lock_file::release
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 10/18/00  41103    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
void File_status_lock<T>::release()
{
   // attempt to delete lock file
   Opus_lock_file::release();
   delete locked_ent;
   locked_ent = 0;
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_status_lock::gen_key
//
// Purpose: 
//
//    Create lock file name and entry search mask.
//
// Description:
//
//    This method creates the lock file name based on the target name,
//    and the file search mask used to obtain a copy of the status file
//    once the lock is generated.
//
// Returns:
//
//    none
//
// Exceptions thrown:
//
//    EFROM->Opus_lock_file::assign
//    Bad_val<Entry*> - if the first argument is null;
//                      Bad_val.arg points to the first argument
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 10/18/00  41103    WMiller     Initial code
// 10/29/02  46765    Sontag      Standardizing for the gcc 3.2 compiler
//
//////////////////////////////////////////////////////////////////////////

template<class T>
void File_status_lock<T>::gen_key(
                            const T* ent,            // I - target status file
                            const std::string& dir)  // I - dir containing
                                                     //     the status files
{
   // validate argument
   if (!ent) {
      Msg m;
      m << sev(Msg::E) << type(Msg::BADVAL) <<
        "File_status_lock::File_status_lock - Null argument." << endm;
      T* ce = const_cast<T*>(ent);
      throw Bad_val<T*>(ce);
   }

   // generate lock name
   locked_ent = dynamic_cast<T*>(ent->clone());
   locked_ent->mask();
   Opus_lock_file::assign(dir + locked_ent->str());

   // create search mask
   locked_ent->search_mask();
   search_mask = dir + locked_ent->str();

   delete locked_ent;
   locked_ent = 0;
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_status_lock::operator==
//
// Purpose: 
//
//    Compare the File_status_lock object with another.  
//
// Description:
//
//    This operator compares the object with the File_status_lock object
//    argument. Two File_status_lock objects are equal if the lock file names
//    are identical.
//
//    ***Note that a pointer (not a reference) to the object with which 
//       the comparison is to be made is required.
//
// Returns:
//
//    true  - if the locks are identical
//    false - if the locks are not identical
//
// Exceptions thrown:
//
//    none
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 10/18/00  41103    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
bool File_status_lock<T>::operator==(
                               const Opus_lock* ol) // I - lock to compare
                                                    //     to
                               const
{
   return(Opus_lock_file::operator==(ol));
}

//////////////////////////////////////////////////////////////////////////
//
// Method: File_status_lock::get_locked_entry
//
// Purpose: 
//
//    Get a copy of the target status file as an Entry object.
//
// Description:
//
//    A copy of the locked Entry object is fetched off disk (if necessary)
//    and a copy is constructed off the heap and returned to the caller. The
//    target must be locked prior to calling this method. The client should
//    delete the new object when it is no longer needed.
//
// Returns:
//
//    The Entry object that is the target of this lock.
//
// Exceptions thrown:
//
//    Io_error<const char*> - if an I/O occurs while locating the entry;
//                            Io_error.arg contains the I/O error message
//    Not_ready<void*>      - if the target has not been locked;
//                            Not_ready.arg points to null.
//
// Example:
//
//    none
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 10/18/00  41103    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T>
Entry* File_status_lock<T>::get_locked_entry() const
{
   if (!locked) {
      Msg m;
      m << sev(Msg::E) << type(Msg::MISUSE) <<
        "File_status_lock::get_locked_entry - Target not locked." << endm;
      throw Not_ready<void*>(0);
   }

   if (!locked_ent) {

      // fetch a copy of the locked object
      long context;
      int status;
      if ((status = OSFILE_init (search_mask.c_str(), &context)) !=
                                                        OSFILE_SUCCESS) {
         Msg m;
         m << sev(Msg::E) << type(Msg::FIO) <<
           "File_status_lock::get_locked_entry - Search init failed for " << 
           search_mask << "." << endm;
         throw Io_error<const char*>(OSFILE_perror(status));
      }
      char c_res[FILENAME_MAX];
      if ((status = OSFILE_find(&context, c_res)) == OSFILE_SUCCESS) {
         locked_ent = new T(c_res);
         Msg m;
         m << sev(Msg::D) << 
            "File_status_lock::get_locked_entry - Found entry: " <<
           locked_ent->str() << endm;
         OSFILE_find_end(&context);
      } else {
         Msg m;
         m << sev(Msg::E) << type(Msg::FIO) << 
           "File_status_lock::get_locked_entry - Search failure for " <<
           search_mask << endm;
         OSFILE_find_end(&context);
         throw Io_error<const char*>(OSFILE_perror(status));
      }
   }
   return(locked_ent->clone());
}
