#include <string>
#include <vector>
#include <string.h>
#include <errno.h>
#include "os_specific.h"
#include "field.h"
#include "entry.h"
#include "files_bb.h"
#include "file_entry.h"
#include "opus_commands.h"
#include "msg.h"
#include "opus_exceptions.h"
#include "osfile_public.h"
#include "sys_public.h"

//////////////////////////////////////////////////////////////////////////
//
// Function: file_based_bb_shr_templates::pstat_osf_post
//
// Purpose: 
//
//    Shared code for the post method of classes File_pstat_bb and
//    File_osf_bb.
//
// Description:
//
//    see File_pstat_bb::post and File_osf_bb::post
//
// Returns:
//
//    see File_pstat_bb::post and File_osf_bb::post
//
// Exceptions thrown:
//
//    see File_pstat_bb::post and File_osf_bb::post
//
// Example:
//
//    see File_pstat_bb::post and File_osf_bb::post
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 05/27/98  37796    WMiller     Initial code
// 10/30/03  49601    Sontag      Add call to check entry
// 03/03/07  57369    Sontag      Use size_t where appropriate
//////////////////////////////////////////////////////////////////////////

template<class T, class R>
void file_based_bb_shr_templates::pstat_osf_post(
                       const T*,             // I - dummy arg
                       const R*      bb,     // I - calling object
                       const Entry*  e,      // I - entry
                       const std::string& nm)     // I - "pstat" or "osf"
{
   // construct type/method name for messages
   std::string otype("File_" + nm);
   std::string method(otype + "_bb::post");

   // check argument
   if (!e) {
      Msg m;
      m << sev(Msg::E) << type(Msg::BADVAL) << method <<
         " - Null argument." << endm;
      Entry* ce = const_cast<Entry*>(e);
      throw Bad_val<Entry*>(ce);
   }
   const T* p;
   if ((p = dynamic_cast<const T*>(e)) == 0) {
      Msg m;
      m << sev(Msg::E) << type(Msg::BADTYPE) << method <<
         " - Argument is not of type " << otype << "." << endm;
      Entry* ce = const_cast<Entry*>(e);
      throw Type<Entry*>(ce, ce->str());
   }
   e->check(); // call the Entry-specific validity checker

   // check that PSTAT/OSF does not already exist
   T* lp = dynamic_cast<T*>(p->clone());
   lp->search_mask();
   std::vector<Entry*> res;
   if (bb->search(lp, res)) {
      for (size_t i = 0; i < res.size(); i++) delete res[i];
      delete lp;
      Msg m;
      m << sev(Msg::E) << type(Msg::DUPL) << method << " - " << nm <<
         " already exists: " << e->str() << endm;
      Entry* ce = const_cast<Entry*>(e);
      throw Already<Entry*>(ce, ce->str());
   }
   delete lp;

   // get file name
   std::string s(p->str());

   // create file
   errno = 0;
   File_entry* fname = new File_entry(bb->get_bb_dir() + s);
   try {
      Files_bb fbb;
      fbb.post(fname); // this will call the File_entry::check() method
      delete fname;
      Msg m;
      m << sev(Msg::D) << method << " - Created " << nm << " " << s << "."
        << endm;
   }
   catch(...) {
      delete fname;
      Msg m;
      m << sev(Msg::E) << type(Msg::FIO) << method <<
         " - Failed to create " << nm << " " << s << "." << endm;
      throw;
   }
}

//////////////////////////////////////////////////////////////////////////
//
// Function: file_based_bb_shr_templates::pstat_osf_erase
//
// Purpose: 
//
//    Shared code for the erase method of classes File_pstat_bb and
//    File_osf_bb.
//
// Description:
//
//    see File_pstat_bb::erase and File_osf_bb::erase
//
// Returns:
//
//    see File_pstat_bb::erase and File_osf_bb::erase
//
// Exceptions thrown:
//
//    see File_pstat_bb::erase and File_osf_bb::erase
//
// Example:
//
//    see File_pstat_bb::erase and File_osf_bb::erase
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 05/27/98  37796    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T, class R>
void file_based_bb_shr_templates::pstat_osf_erase(
                        const T*,             // I - dummy arg
                        R*            bb,     // I - calling object
                        const Entry*   e,     // I - entry
                        const std::string& nm)     // I - "pstat" or "osf"
{
   // construct type/method name for messages
   std::string otype("File_" + nm);
   std::string method(otype + "_bb::erase");

   // validate argument
   if (!e) {
      Msg m;
      m << sev(Msg::E) << type(Msg::BADVAL) << method <<
         " - Null argument." << endm;
      Entry* ce = const_cast<Entry*>(e);
      throw Bad_val<Entry*>(ce);
   }
   const T* p;
   if ((p = dynamic_cast<const T*>(e)) == 0) {
      Msg m;
      m << sev(Msg::E) << type(Msg::BADTYPE) << method <<
         " - Argument is not of type " << otype << "." << endm;
      Entry* ce = const_cast<Entry*>(e);
      throw Type<Entry*>(ce, ce->str());
   }

   // search for PSTAT/OSF
   std::vector<Entry*> res;
   switch (bb->search(e, res)) {
   case 1:
      {
      // delete PSTAT/OSF
      std::string s(bb->get_bb_dir() + res[0]->str());
      delete res[0];
      errno = 0;
      if (remove(s.c_str()) == 0) {
         Msg m;
         m << sev(Msg::D) << method << " - Deleted " << nm << " " << s <<
            "." << endm;
      } else {
         int status = errno;
         Msg m;
         m << sev(Msg::E) << type(Msg::FIO) << method <<
            " - Failed to delete " << nm << " " << s << "." << endm;
         throw Io_error<int>(status, strerror(status));
      }
      break;
      }
   case 0:
      {
      std::string s(p->str());
      Msg m;
      m << sev(Msg::E) << type(Msg::MISSING) << method << " - No " << nm <<
        "  matching " << s << " found." << endm;
      Entry* ce = const_cast<Entry*>(e);
      throw No_entry<Entry*>(ce, ce->str());
      }
   default: // more than one PSTAT/OSF found is bad
      {
      std::string s(p->str());
      Msg m;
      m << sev(Msg::E) << type(Msg::AMBIG) << method <<
         " - More than one " << nm << " matching " << s <<
         " was found. Matches:\n";
      std::vector<Entry*>::iterator vi;
      std::vector<Entry*>* list = new std::vector<Entry*>;
      for (vi = res.begin(); vi != res.end(); vi++) {
         m << (*vi)->str() << "\n";
         list->push_back(*vi);
      }
      m << endm;
      throw Ambiguous<std::vector<Entry*>*>(list);
      }
   }
}

//////////////////////////////////////////////////////////////////////////
//
// Function: file_based_bb_shr_templates::pstat_osf_search
//
// Purpose: 
//
//    Shared code for the search method of classes File_pstat_bb and
//    File_osf_bb.
//
// Description:
//
//    see File_pstat_bb::search and File_osf_bb::search
//
// Returns:
//
//    see File_pstat_bb::search and File_osf_bb::search
//
// Exceptions thrown:
//
//    see File_pstat_bb::search and File_osf_bb::search
//
// Example:
//
//    see File_pstat_bb::search and File_osf_bb::search
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 05/27/98  37796    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T, class R>
int file_based_bb_shr_templates::pstat_osf_search(
                        const T*,             // I - dummy arg.
                        const R*        bb,   // I - calling object
                        const Entry*     e,   // I - entry
                        std::vector<Entry*>& res,  // I - results vector
                        const std::string& nm)     // I - "pstat" or "osf"
{
   // construct type/method name for messages
   std::string otype("File_" + nm);
   std::string method(otype + "_bb::search");

   // validate argument
   if (!e) {
      Msg m;
      m << sev(Msg::E) << type(Msg::BADVAL) << method <<
         " - Null argument." << endm;
      Entry* ce = const_cast<Entry*>(e);
      throw Bad_val<Entry*>(ce);
   }
   const T* p;
   if ((p = dynamic_cast<const T*>(e)) == 0) {
      Msg m;
      m << sev(Msg::E) << type(Msg::BADTYPE) << method <<
         " - Argument not of type " << otype << "." << endm;
      Entry* ce = const_cast<Entry*>(e);
      throw Type<Entry*>(ce, ce->str());
   }

   // get file name
   std::string s(bb->get_bb_dir() + p->str());

   // search for match
   long context;
   int status;
   if ((status = OSFILE_init (s.c_str(), &context)) != OSFILE_SUCCESS) {
     Msg m;
      m << sev(Msg::E) << type(Msg::FIO) << method <<
         " - Search init failed for " << s << "." << endm;
      throw Io_error<const char*>(OSFILE_perror(status));
   }
   char c_res[FILENAME_MAX];
   while ((status = OSFILE_find(&context, c_res)) == OSFILE_SUCCESS) {
      T* tmp = new T(c_res);
      res.push_back(tmp);
      Msg m;
      m << sev(Msg::D) << method << " - Search for " << s << " found "
         << c_res << "." << endm;
   }
   if (status != OSFILE_NO_MORE_FILE && status != OSFILE_NO_SUCH_FILE) {
     Msg m;
      m << sev(Msg::E) << type(Msg::FIO) << method << " - Search failure for "
        << s << endm;
      OSFILE_find_end(&context);
      throw Io_error<const char*>(OSFILE_perror(status));
   }
   OSFILE_find_end(&context);

   return(res.size());
}

//////////////////////////////////////////////////////////////////////////
//
// Function: file_based_bb_shr_templates::pstat_osf_replace
//
// Purpose: 
//
//    Shared code for the replace method of classes File_pstat_bb
//    and File_osf_bb.
//
// Description:
//
//    see File_pstat_bb::replace, File_osf_bb::replace
//
// Returns:
//
//    see File_pstat_bb::replace, File_osf_bb::replace
//
// Exceptions thrown:
//
//    see File_pstat_bb::replace, File_osf_bb::replace
//
// Example:
//
//    see File_pstat_bb::replace, File_osf_bb::replace
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 03/29/00  41131    WMiller     Initial code
//
//////////////////////////////////////////////////////////////////////////

template<class T, class R>
void file_based_bb_shr_templates::pstat_osf_replace(
                        const T*,              // I - dummy arg.
                        const R*        bb,    // I - calling object
                        const Entry* old_ent,  // I - old entry
                        const Entry* new_ent,  // I - new entry
                        const std::string& nm) // I - caller ID string
{
   // construct type/method name for messages
   std::string otype("File_" + nm);
   std::string method(otype + "_bb::replace");

   // validate arguments
   if (!old_ent) {
      Msg m;
      m << sev(Msg::E) << type(Msg::BADVAL) << method <<
         " - Null argument." << endm;
      throw Bad_val<int>(0);
   }
   if (!new_ent) {
      Msg m;
      m << sev(Msg::E) << type(Msg::BADVAL) << method <<
         " - Null argument." << endm;
      throw Bad_val<int>(1);
   }
   const T* ofe;
   const T* nfe;
   if ((ofe = dynamic_cast<const T*>(old_ent)) == 0 ||
       (nfe = dynamic_cast<const T*>(new_ent)) == 0) {
      Msg m;
      m << sev(Msg::E) << type(Msg::BADTYPE) << method <<
         " - Argument not of type " << otype << "." << endm;
      if (ofe) throw Type<const Entry*>(nfe, nfe->str());
      else     throw Type<const Entry*>(ofe, ofe->str());
   }

   // PSTAT/OSF must have identical unique fields
   T* omask = 0;
   T* nmask = 0;
   try {
      omask = dynamic_cast<T*>(ofe->clone());
      omask->mask();
      nmask = dynamic_cast<T*>(nfe->clone());
      nmask->mask();
      if (!(*omask == nmask)) {
         Msg m;
         m << sev(Msg::E) << type(Msg::BADTYPE) << method <<
            " - Unique fields of " << otype << " do not match." << endm;
         throw Type<const Entry*>(nfe, nfe->str());
      }
   }
   catch(...) {
      delete omask;
      delete nmask;
      throw;
   }
   delete omask;
   delete nmask;

   // perform rename
   std::string old_fname(bb->get_bb_dir() + ofe->str());
   std::string new_fname(bb->get_bb_dir() + nfe->str());
   errno = 0;
   if (SYS_rename_file_dev(old_fname.c_str(), new_fname.c_str()) !=
                                                        SYS_SUCCESS)  {
      int status = errno;
      Msg m;
      m << sev(Msg::E) << type(Msg::FIO) << method << 
        " - Rename attempt failed for " << old_fname << 
        " to " << new_fname << endm;
      throw Io_error<int>(status, strerror(status));
   }

   Msg m;
   m << sev(Msg::D) << method << " - Replaced " << old_fname <<
      " with " << new_fname << endm;
}

//////////////////////////////////////////////////////////////////////////
//
// Function: file_based_entry_shr_templates::entry_equal_to
//
// Purpose: 
//
//    Shared code for operator== of classes File_entry, File_pstat, and
//    File_osf.
//
// Description:
//
//    see File_entry::operator==, File_pstat::operator==, and
//    File_osf::operator==.
//
// Returns:
//
//    see File_entry::operator==, File_pstat::operator==, and
//    File_osf::operator==.
//
// Exceptions thrown:
//
//    see File_entry::operator==, File_pstat::operator==, and
//    File_osf::operator==.
//
// Example:
//
//    see File_entry::operator==, File_pstat::operator==, and
//    File_osf::operator==.
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 05/27/98  37796    WMiller     Initial code
// 03/15/00  40934    WMiller     Case-less compare under VMS
// 04/06/00  41248    WMiller     Delete Field objects
// 03/03/07  57369    Sontag      Use size_t where appropriate
//////////////////////////////////////////////////////////////////////////

template<class T>
bool file_based_entry_shr_templates::entry_equal_to(
                                          const T* e,       // I - left hand
                                                            // argument
                                          const Entry* ee)  // I - right hand
                                                            // argument
{
   if (!ee) return(false);
   if (e == ee) return(true);

   // types must match
   const T* lhs;
   const T* rhs;
   if ((lhs = dynamic_cast<const T*>(e))  == 0 ||
       (rhs = dynamic_cast<const T*>(ee)) == 0) {
      return(false);
   }
   Field* lhf;
   Field* rhf;
   std::string lhst;
   std::string rhst;

   // compare individual fields considering wildcards
   for (size_t i = 0; i < ((size_t)lhs->size()); i++) {
      lhf  = lhs->get_Nth_field(i);
      rhf  = rhs->get_Nth_field(i);
      lhst = lhf->str();
      rhst = rhf->str();
      delete lhf;
      delete rhf;
      for (size_t j = 0; j < ((size_t)lhst.size()); j++) {
         if (rhst[j] == Os_specific::FILE_MULTI_CHAR_WILDCARD) return(true);
         if (lhst[j] != rhst[j] &&
             rhst[j] != Os_specific::FILE_SINGLE_CHAR_WILDCARD)
            return(false);
      }
   }

   return(true);
}

//////////////////////////////////////////////////////////////////////////
//
// Function: file_entry_field_shr_templates::apply_diffs
//
// Purpose: 
//
//    Shared code for operator% of classes Directory, Rootname, Extension,
//    and Dangle.
//
// Description:
//
//    see Directory::operator%, Rootname::operator%, Extension::operator%,
//    and Dangle::operator%.
//
// Returns:
//
//    see Directory::operator%, Rootname::operator%, Extension::operator%,
//    and Dangle::operator%.
//
// Exceptions thrown:
//
//    see Directory::operator%, Rootname::operator%, Extension::operator%,
//    and Dangle::operator%.
//
// Example:
//
//    see Directory::operator%, Rootname::operator%, Extension::operator%,
//    and Dangle::operator%.
//
// Modification History:
//
// Date      OPR      Who         Reason
// --------- -------- ----------- ----------------------------------------
// 05/27/98  37796    WMiller     Initial code
// 10/29/02  46765    Sontag      Standardizing for the gcc 3.2 compiler
// 03/03/07  57369    Sontag      Use size_t where appropriate
//////////////////////////////////////////////////////////////////////////

template<class T>
void file_entry_field_shr_templates::apply_diffs(
                            T* lhs,     // I - field to apply diffs to
                            Field* rhs) // I - source field
{
   Msg m;

   // field types must match
   T* tmp;
   if (lhs->id() != rhs->id() || !(tmp = dynamic_cast<T*>(rhs))) {
     m << sev(Msg::E) << type(Msg::BADTYPE) <<
         "?::operator% - Field types do not match." << endm;
      throw Type<Field*>(rhs, rhs->ustr());
   }

   // look for FILE_ACTION_INDICATOR
   std::string diff;
   std::string com;
   std::string unpad(tmp->ustr());
   size_t pos;
   bool file_action = false;
   if ((pos = unpad.find(File_entry::FILE_ACTION_INDICATOR)) != std::string::npos)
   {
      file_action = true;
      diff = unpad.substr(0, pos);
      com  = unpad.substr(pos + 1);
   } else {
      diff = unpad;
   }

   // look for wildcard
   if ((pos = diff.find(File_entry::DIFF_WILDCARD)) != std::string::npos) {
      if (pos != 0 && pos != diff.length()) { // misplaced wildcard
         m << sev(Msg::E) << type(Msg::BADVAL) <<
          "?::operator% - Modifier wildcard in wrong place; must be first "
          "or last" << endm;
         throw Bad_val<Field*>(rhs, rhs->ustr());
      }
      std::string s(lhs->ustr());
      if (pos == 0) lhs->assign(s + diff.substr(1));          // append
      else          lhs->assign(diff.substr(0, pos - 1) + s); // prepend
   } else { // no wildcard
      lhs->assign(diff);
   }

   if (file_action) throw File_action<std::string>(com);
}
