// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
// ** Copyright UCAR (c) 1992 - 2007
// ** University Corporation for Atmospheric Research (UCAR)
// ** National Center for Atmospheric Research (NCAR)
// ** Research Applications Lab (RAL)
// ** P.O.Box 3000, Boulder, Colorado, 80307-3000, USA
// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*



////////////////////////////////////////////////////////////////////////


using namespace std;

#include <iostream>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>
#include <cstdio>
#include <cmath>

#include "vx_met_util/vsdb_columns.h"
#include "vx_analysis_util/vsdb_line.h"
#include "vx_analysis_util/vsdb_offsets.h"
#include "vx_analysis_util/vsdblinetype_to_string.h"
#include "vx_analysis_util/analysis_utils.h"
#include "vx_analysis_util/vsdb_offsets.h"


////////////////////////////////////////////////////////////////////////


static const char * default_interp_mthd = "NA";
static const int    default_interp_pnts = 0;

static const char * suffix_list [] = {

   ".vsdb",
   ".vsb"

};

static const int n_suffixes = sizeof(suffix_list)/sizeof(*suffix_list);


////////////////////////////////////////////////////////////////////////


   //
   //  Code for class VSDBLine
   //


////////////////////////////////////////////////////////////////////////


VSDBLine::VSDBLine()

{


}


////////////////////////////////////////////////////////////////////////


VSDBLine::~VSDBLine()

{


}


////////////////////////////////////////////////////////////////////////


VSDBLine::VSDBLine(const VSDBLine & L)

{

assign(L);

return;

}


////////////////////////////////////////////////////////////////////////


VSDBLine & VSDBLine::operator=(const VSDBLine & L)

{

if ( this == &L )  return ( * this );

assign(L);

return ( * this );

}


////////////////////////////////////////////////////////////////////////


void VSDBLine::dump(ostream & out, int depth) const

{

int month, day, year, hour, minute, second;
char junk[256];
Indent prefix(depth);


DataLine::dump(out, depth);

out << prefix << "\n";

out << prefix << "Version        = "   << version()     << "\n";
out << prefix << "Model          = \"" << model()       << "\"\n";

sec_to_hms(fcst_lead(), hour, minute, second);
sprintf(junk, "%02d:%02d:%02d", hour, minute, second);
out << prefix << "Fcst Lead      = "   << fcst_lead()  << "  ( " << junk << " )\n";

unix_to_mdyhms(valid_time(), month, day, year, hour, minute, second);
sprintf(junk, "%s %d, %d  %02d:%02d:%02d", short_month_name[month], day, year, hour, minute, second);
out << prefix << "Fcst Valid     = "   << valid_time() << "  ( " << junk << " )\n";

out << prefix << "Obs Type       = \"" << obtype()          << "\"\n";
out << prefix << "Vx Mask        = \"" << vx_mask()         << "\"\n";
out << prefix << "Line Type      = \"" << line_type()       << "\"\n";
out << prefix << "Var            = \"" << var()             << "\"\n";
out << prefix << "Level          = \"" << level()           << "\"\n";

unix_to_mdyhms(init_time(), month, day, year, hour, minute, second);

sprintf(junk, "%s %d, %d  %02d:%02d:%02d", short_month_name[month], day, year, hour, minute, second);
out << prefix << "Init Time      = "   << init_time() << "  ( " << junk << " )\n";

sec_to_hms(init_hour(), hour, minute, second);
sprintf(junk, "%02d:%02d:%02d", hour, minute, second);
out << prefix << "Init Hour      = "   << init_hour()  << "  ( " << junk << " )\n";

out << prefix << "Line Number    = "   << LineNumber        << "\n";
out << prefix << "Max Item Width = "   << max_item_width()  << "\n";

vsdblinetype_to_string(Type, junk);

out << prefix << "Type           = "   << junk              << "\n";

out << prefix << "Interp Mthd    = \"" << interp_mthd()     << "\"\n";
out << prefix << "Interp Pnts    = \"" << interp_pnts()     << "\"\n";

return;

}


////////////////////////////////////////////////////////////////////////


int VSDBLine::version() const

{

int k;
const char * c = get_item(version_offset);

k = atoi(c + 1);   //  skip the "V"

return ( k );

}


////////////////////////////////////////////////////////////////////////


const char * VSDBLine::model() const

{

const char * c = get_item(model_offset);

return ( c );

}


////////////////////////////////////////////////////////////////////////


int VSDBLine::fcst_lead() const

{

int j;
const char * c = get_item(fcst_lead_offset);

j = timestring_to_sec(c);

return ( j );

}


////////////////////////////////////////////////////////////////////////


const char * VSDBLine::obtype() const

{

const char * c = get_item(obtype_offset);

return ( c );

}


////////////////////////////////////////////////////////////////////////


const char * VSDBLine::vx_mask() const

{

const char * c = get_item(vx_mask_offset);

return ( c );

}


////////////////////////////////////////////////////////////////////////


const char * VSDBLine::line_type() const

{

const char * c = get_item(line_type_offset);

return ( c );

}


////////////////////////////////////////////////////////////////////////


const char * VSDBLine::var() const

{

const char * c = get_item(var_offset);

return ( c );

}


////////////////////////////////////////////////////////////////////////


const char * VSDBLine::level() const

{

const char * c = get_item(level_offset);

return ( c );

}


////////////////////////////////////////////////////////////////////////


const char * VSDBLine::interp_mthd() const

{

const char * c;

//
// Only retieve this column if the current line is long enough
//
if(interp_mthd_offset[Type] >= 0 &&
   interp_mthd_offset[Type] < N_items) {
   c = get_item(interp_mthd_offset[Type]);
}
//
// Otherwise, set to the default interp_mthd
//
else {
   c = default_interp_mthd;
}

return ( c );

}


////////////////////////////////////////////////////////////////////////


int VSDBLine::interp_pnts() const

{

const char * c;

int k;

//
// Only retieve this column if the current line is long enough
//
if(interp_pnts_offset[Type] >= 0 &&
   interp_pnts_offset[Type] < N_items) {
   c = get_item(interp_pnts_offset[Type]);
   k = atoi(c);
}
//
// Otherwise, set to the default interp_mthd
//
else {
   k = default_interp_pnts;
}

return ( k );

}


////////////////////////////////////////////////////////////////////////


unixtime VSDBLine::valid_time() const

{

unixtime t;
const char * c = get_item(fcst_valid_offset);

t = timestring_to_unix(c);

return ( t );

}


////////////////////////////////////////////////////////////////////////


unixtime VSDBLine::init_time() const

{

int s;
unixtime t;

t = valid_time();

s = fcst_lead();

t -= s;

return ( t );

}


////////////////////////////////////////////////////////////////////////


int VSDBLine::init_hour() const

{

int s, mon, day, yr, hr, min, sec;
unixtime t;

t = init_time();

unix_to_mdyhms(t, mon, day, yr, hr, min, sec);

s = hr * sec_per_hour + min * sec_per_minute + sec;

return ( s );

}


////////////////////////////////////////////////////////////////////////


void VSDBLine::determine_line_type()

{

// const int N = N_items;
const char * const c = line_type();

Type = no_vsdb_line_type;

     if ( strcmp(c, "SL1L2"      ) == 0 )  Type = vsdb_sl1l2;
else if ( strcmp(c, "SAL1L2"     ) == 0 )  Type = vsdb_sal1l2;
else if ( strcmp(c, "VL1L2"      ) == 0 )  Type = vsdb_vl1l2;
else if ( strcmp(c, "VAL1L2"     ) == 0 )  Type = vsdb_val1l2;

else if ( strncmp(c, "FHO",    3 ) == 0 )  Type = vsdb_fho;

else if ( strncmp(c, "CTC",    3 ) == 0 )  Type = vsdb_ctc;
else if ( strncmp(c, "CTP",    3 ) == 0 )  Type = vsdb_ctp;
else if ( strncmp(c, "CFP",    3 ) == 0 )  Type = vsdb_cfp;
else if ( strncmp(c, "COP",    3 ) == 0 )  Type = vsdb_cop;
else if ( strncmp(c, "CTS",    3 ) == 0 )  Type = vsdb_cts;
else if ( strncmp(c, "CNT",    3 ) == 0 )  Type = vsdb_cnt;
else if ( strncmp(c, "MPR",    3 ) == 0 )  Type = vsdb_mpr;

else if ( strncmp(c, "NBRCTC", 6 ) == 0 )  Type = vsdb_nbrctc;
else if ( strncmp(c, "NBRCTS", 6 ) == 0 )  Type = vsdb_nbrcts;
else if ( strncmp(c, "NBRCNT", 6 ) == 0 )  Type = vsdb_nbrcnt;

else if ( strncmp(c, "ISC",    3 ) == 0 )  Type = vsdb_isc;

else {

   cerr << "\n\nVSDBLine::determine_line_type() -> "
        << "Line type should be set to one of:\n"
        << "\tSL1L2, SAL1L2, VL1L2, VAL1L2,\n"
        << "\tFHO, CTC, CTP, CFP, COP,\n"
        << "\tCTS, CNT, MPR,\n"
        << "\tNBRCTC, NBRCTS, NBRCNT,\n"
        << "\tor ISC\n\n"
        << flush;

   exit ( 1 );

}

   //
   //  done
   //

return;

}


////////////////////////////////////////////////////////////////////////


int VSDBLine::read_line(LineDataFile * ldf)

{

int status;

status = DataLine::read_line(ldf);

if ( !status )  {

   clear();

   Type = no_vsdb_line_type;

   return ( 0 );

}

determine_line_type();

return ( 1 );

}


////////////////////////////////////////////////////////////////////////


   //
   //  Code for misc functions
   //


////////////////////////////////////////////////////////////////////////


StringArray get_vsdb_filenames(const StringArray & search_dirs)

{

int j;
const int N = search_dirs.n_elements();
StringArray a, b;
struct stat sbuf;


for (j=0; j<N; ++j)  {

   if ( stat(search_dirs[j], &sbuf) < 0 )  {

      cerr << "\n\n get_vsdb_filenames() -> can't stat \""
           << search_dirs[j] << "\"\n\n";

      exit ( 1 );

   }

   if ( S_ISDIR(sbuf.st_mode) )  {

      b = get_vsdb_filenames_from_dir(search_dirs[j]);

      a.add(b);

      b.clear();

   } else if ( S_ISREG(sbuf.st_mode) )  {

      if ( is_vsdb_filename(search_dirs[j]) )  a.add(search_dirs[j]);

   }

}


return ( a );

}


////////////////////////////////////////////////////////////////////////


StringArray get_vsdb_filenames_from_dir(const char * directory_path)

{

DIR * directory = (DIR *) 0;
struct dirent * entry = (struct dirent *) 0;
StringArray a, b;
char entry_path[PATH_MAX];
struct stat sbuf;



directory = opendir(directory_path);

if ( !directory )  {

   cerr << "\n\n get_vsdb_filenames_from_dir() -> can't open directory path \""
        << directory_path << "\"\n\n";

   exit ( 1 );

}

while ( (entry = readdir(directory)) != NULL )  {

   if ( strcmp(entry->d_name, "." ) == 0 )  continue;
   if ( strcmp(entry->d_name, "..") == 0 )  continue;

   sprintf(entry_path, "%s/%s", directory_path, entry->d_name);

   if ( stat(entry_path, &sbuf) < 0 )  {

      cerr << "\n\n get_vsdb_filenames_from_dir() -> can't stat \""
           << entry_path << "\"\n\n";

      exit ( 1 );

   }

   if ( S_ISDIR(sbuf.st_mode) )  {

      b = get_vsdb_filenames_from_dir(entry_path);

      a.add(b);

      b.clear();

   } else if ( S_ISREG(sbuf.st_mode) )  {

      if ( is_vsdb_filename(entry_path) )  a.add(entry_path);

   }

}   //  while








   //
   //  done
   //

closedir(directory);  directory = (DIR *) 0;

return ( a );

}


////////////////////////////////////////////////////////////////////////


int is_vsdb_filename(const char * path)

{

int j, k, n;
int match;
const char * short_name = (const char *) 0;

   //
   //  get short name
   //

j = strlen(path) - 1;

while ( (j >= 0) && (path[j] != '/') )  --j;

++j;

short_name = path + j;

   //
   //  does the filename end in a proper suffix?
   //

n = strlen(short_name);

match = 0;

for (j=0; j<n_suffixes; ++j)  {

   k = strlen(suffix_list[j]);

   if ( strncmp(short_name + (n - k), suffix_list[j], k) == 0 ) {

      match = 1;

      break;

   }

}

if ( !match )  return ( 0 );

   //
   //  done
   //

return ( 1 );

}


////////////////////////////////////////////////////////////////////////


VSDBLineType determine_line_type(const char *c)

{

VSDBLineType Type;

Type = no_vsdb_line_type;

     if ( strcmp(c, "SL1L2"      ) == 0 )  Type = vsdb_sl1l2;
else if ( strcmp(c, "SAL1L2"     ) == 0 )  Type = vsdb_sal1l2;
else if ( strcmp(c, "VL1L2"      ) == 0 )  Type = vsdb_vl1l2;
else if ( strcmp(c, "VAL1L2"     ) == 0 )  Type = vsdb_val1l2;

else if ( strncmp(c, "FHO",    3 ) == 0 )  Type = vsdb_fho;

else if ( strncmp(c, "CTC",    3 ) == 0 )  Type = vsdb_ctc;
else if ( strncmp(c, "CTP",    3 ) == 0 )  Type = vsdb_ctp;
else if ( strncmp(c, "CFP",    3 ) == 0 )  Type = vsdb_cfp;
else if ( strncmp(c, "COP",    3 ) == 0 )  Type = vsdb_cop;
else if ( strncmp(c, "CTS",    3 ) == 0 )  Type = vsdb_cts;
else if ( strncmp(c, "CNT",    3 ) == 0 )  Type = vsdb_cnt;
else if ( strncmp(c, "MPR",    3 ) == 0 )  Type = vsdb_mpr;

else if ( strncmp(c, "NBRCTC", 6 ) == 0 )  Type = vsdb_nbrctc;
else if ( strncmp(c, "NBRCTS", 6 ) == 0 )  Type = vsdb_nbrcts;
else if ( strncmp(c, "NBRCNT", 6 ) == 0 )  Type = vsdb_nbrcnt;

else if ( strncmp(c, "ISC",    3 ) == 0 )  Type = vsdb_isc;

else {

   cerr << "\n\ndetermine_line_type() -> "
        << "Line type should be set to one of:\n"
        << "\tSL1L2, SAL1L2, VL1L2, VAL1L2,\n"
        << "\tFHO, CTC, CTP, CFP, COP,\n"
        << "\tCTS, CNT, MPR,\n"
        << "\tNBRCTC, NBRCTS, NBRCNT,\n"
        << "\tor ISC\n\n"
        << flush;

   exit ( 1 );

}

   //
   //  done
   //

return(Type);

}


////////////////////////////////////////////////////////////////////////


int determine_column_offset(VSDBLineType type, const char *c)

{

int offset;

switch(type) {

   case vsdb_sl1l2:
      offset = get_column_offset(sl1l2_columns, n_sl1l2_columns, c);
      break;

   case vsdb_sal1l2:
      offset = get_column_offset(sal1l2_columns, n_sal1l2_columns, c);
      break;

   case vsdb_vl1l2:
      offset = get_column_offset(vl1l2_columns, n_vl1l2_columns, c);
      break;

   case vsdb_val1l2:
      offset = get_column_offset(val1l2_columns, n_val1l2_columns, c);
      break;

   case vsdb_fho:
      offset = get_column_offset(fho_columns, n_fho_columns, c);
      break;

   case vsdb_ctc:
      offset = get_column_offset(ctc_columns, n_ctc_columns, c);
      break;

   case vsdb_ctp:
      offset = get_column_offset(ctp_columns, n_ctp_columns, c);
      break;

   case vsdb_cfp:
      offset = get_column_offset(cfp_columns, n_cfp_columns, c);
      break;

   case vsdb_cop:
      offset = get_column_offset(cop_columns, n_cop_columns, c);
      break;

   case vsdb_cts:
      offset = get_column_offset(cts_columns, n_cts_columns, c);
      break;

   case vsdb_cnt:
      offset = get_column_offset(cnt_columns, n_cnt_columns, c);
      break;

   case vsdb_mpr:
      offset = get_column_offset(mpr_columns, n_mpr_columns, c);
      break;

   case vsdb_nbrctc:
      offset = get_column_offset(nbrctc_columns, n_nbrctc_columns, c);
      break;

   case vsdb_nbrcts:
      offset = get_column_offset(nbrcts_columns, n_nbrcts_columns, c);
      break;

   case vsdb_nbrcnt:
      offset = get_column_offset(nbrcnt_columns, n_nbrcnt_columns, c);
      break;

   case vsdb_isc:
      offset = get_column_offset(isc_columns, n_isc_columns, c);
      break;

   default:
      cerr << "\n\n  determine_offset() -> unexpected vsdb line type value of "
           << type << "\n\n";

      exit ( 1 );
      break;
};

return(offset);

}


////////////////////////////////////////////////////////////////////////

