// Host NOOP primitive


#include <vector>
#include <primitive.h>

// ****************************************************************** //
// Routine to create new file names for /NF switch
string mk_nf_name	(string root,		// root string
			int_4 type,		// naming type
			int_4 digits,		// Num digits/time format
			int_4 count)		// file count
{
  string adigits, ofname, timestr;
  string azeros = "00000000";
  int_4 l0, l1;
  real_8 t1, t2;
  
  // ------------------------------------ //
  
  if (type == 1) {		// Numeric sequence
    if (digits > 8) digits = 8;
    l1 = m_l2a(count, adigits);
    if (l1 < digits) {
      l0 = digits-l1;
      ofname = root + azeros.substr(0, l0) + adigits;
    }
    else {
      adigits = adigits.substr(l1-digits);
      ofname = root + adigits;
    }
  }
  else {			// Time-based naming
    m_now(t1, t2);
    if (digits == 1) {		// form = YYYYMMDD_HHMMSS
      l0 = m_times2str(t1, t2, '_', timestr);
      timestr = timestr.substr(0, 20);
      l0 = m_subs(timestr, timestr, "::", "_");
      l0 = m_gsub(timestr, timestr, ":", "");
      ofname = root + timestr;
    }
    else if (digits == 2) {		// form = YYYYMMDD_HHMMSS
      l0 = m_times2str(t1, t2, '_', timestr);
      timestr = timestr.substr(2, 18);
      l0 = m_subs(timestr, timestr, "::", "_");
      l0 = m_gsub(timestr, timestr, ":", "");
      ofname = root + timestr;
    }
    else if (digits == 3) {	// form = HHMMSS
      l0 = m_times2str(t1, t2, '_', timestr);
      timestr = timestr.substr(12, 8);
      l0 = m_gsub(timestr, timestr, ":", "");
      ofname = root + timestr;
    }
      
  }
  return(ofname);
}


// ****************************************************************** //
void mainroutine() 
{
  enum {
    NORMAL  = 1,
    BLOCKED = 2,
    CONSUME = 3,
    CLOSE   = 4
  };


  CPHEADER HIN, HOUT;
  int_4 LPER;
  real_4 cur_time, elapsed;
  real_4 cur_time_ar, elapsed_ar;
  real_4 elapsed_nf;
  int_4 nfblocks = 0;
  real_4 nftime = 0.0;

  
  // ----------------------------------------------------- //
  // Get parameters
  string infile = m_apick(1);
  string outfile = m_apick(2);
  real_8 srate = m_dpick(3);
  
  // Get switches
  bool rt = m_get_pswitch("RT");
  
  int_4 ls;
  string nfstr;
  // Get & analyze /NF switch. If anticipate output being a pipe, do
  // not check for /NF, since can't do new files.
  if (m_filename_is_pipe(outfile)) {
    ls = -1;
  }
  else  {
    ls = m_get_uswitch("NF", nfstr);	// NewFile switch
  }

  int_4 nf_digits = 4;	// 4 digits
  int_4 nfn_type  = 1;		// Numeric sequence names
  int_4 nfmode    = 0;
  bool nfvb       = false;

  if (ls >= 0) {
    nfvb = m_get_sswitch("NFVB");
    if (ls == 1) {
      if (nfstr == "W") nfmode = 3;
    }
    else {
      if (nfstr.find(":") != 1) m_error("Bad /NF specification");
      string ss;
      switch (nfstr[0]) {
      case 'B':
        nfmode = 1;		// Block mode for new files
	ss = nfstr.substr(2);	// Stuff after ":"
	nfblocks = atoi(ss.c_str());
	nftime = -1.0;
	break;
      case 'T':
        nfmode = 2;
	ss = nfstr.substr(2);	// Stuff after ":"
	nftime = atof(ss.c_str());
	nfblocks = -1;
	break;
      default:
	m_error("Bad /NF specification");
      }
    }

    // Get & analyze /NFN switch
    string nfn_str;
    int_4 ls2 = m_get_uswitch("NFN", nfn_str);	// NewFile naming type
   
    if (ls2 >= 0) {
      if (nfn_str.find(":") != 1) m_error("Bad /NFN specification");
      string ss;
      switch (nfn_str[0]) {
      case 'N':
	nfn_type = 1;		// Names as sequential numbers
	ss = nfn_str.substr(2);	// stuff after ":"
	nf_digits = atoi(ss.c_str());
	break;
      case 'T':
	nfn_type = 2;
	ss = nfn_str.substr(2);
	nf_digits = atoi(ss.c_str());	// Use to indicate time format
	if (nf_digits > 3) {
	  m_warning("Invalid NewFile name format. Using format 1.");
	  nf_digits = 1;
	}
	break;
      default:
	m_error("Bad /NFN specification");
      }
    } 
  }	// end of if ls <= 0 .. else ..

  string nf_root;
  int_4 nf_count = 0;
  if (nfmode != 0) {
    nf_root = outfile;
    nf_count = m_get_switch_def("NFSTN", 0);
    outfile = mk_nf_name(nf_root, nfn_type, nf_digits, nf_count);
  }
  
  
  // Open input file
  m_init(HIN, infile, "", "", 0);
  m_open(HIN, HCBF_INPUT);
  if (srate <= 0.0) srate = 1.0/HIN.xdelta;
  
  // Open output file
  m_propagate(HIN, HOUT);
  HOUT.file_name = outfile;

  if (nfvb) {
    m_info("Opening new file: " + HOUT.file_name);
  }

  /* --------------------------------------------------- *
   * --- The /TRUNCATE switch will cause an existing --- *
   * --- destination file to first be physically     --- *
   * --- deleted, thereby forcing its size to cor-   --- *
   * --- respond to the actual number of elements    --- *
   * --- copied.                                     --- *
   * --------------------------------------------------- */

  if (m_get_pswitch ("TRUNCATE")) {              // vv 25/10/07 (DLH)
    m_existence(HOUT, 1);
    if (HOUT.exist) m_delete(HOUT.file_name);
    m_propagate(HIN, HOUT);
    HOUT.file_name = outfile;
  }                                              // ^^ 25/10/07 (DLH)

  HOUT.size = HIN.size;
  m_allocate(HOUT);

  if (2 == HIN.class_) {
    m_force1000(HIN);
    m_force1000(HOUT);
  }
  

  vector<int_1> buf;		// single buffer for all data types

  if (4 == HIN.class_) {
    // Handle type 4000 by treating buf's data as VRB
    LPER = 1;
    buf = vector<int_1>(sizeof(VRB));
    (void)m_vrbinit(*reinterpret_cast<VRB*>(&buf[0]), 0, HIN.record_len);
  } 
  else {
    LPER = m_xfer_length(HIN.bpa, -max(HIN.bpa, 32768));
    if (2 == HIN.class_) {
      // Although LPER is units of atoms for this primitive, we don't
      // want to read partial frames.
      if ((LPER % HIN.subsize) != 0) {
	LPER += (HIN.subsize - (LPER % HIN.subsize));
      }
    }
    int_4 nbytes = m_bytes_reqd(max(HIN.bpe, HOUT.bpe), LPER);
    buf = vector<int_1>(nbytes);
  }

  HIN.cons_len = m_get_switch_def("CL", HIN.cons_len);

  // Init real-time mode
  int_4 mode = m_get_switch_def("MODE", NORMAL);
  int_4 amode = m_get_switch_def("AMODE", BLOCKED);
  real_8 act_rate = 0;
  int_4 wopen_nf = 1;
  int_4 idm = m_mwinit("MODE", mode, "Normal, Block, Consume, Abort");
  int_4 irt = m_dwinit("Samp Rate", srate, 1.0, -1.0, 1.0);
  int_4 iar = m_dwinit("Actual Rate", act_rate, 1.0, -1.0, 1.0);
  int_4 inf = m_mwinit("Start New File", wopen_nf, "No, Start New");
  
  int_4 ngot = 0;
  real_8 new_srate     = srate;
  int_4 buf_count      = 0;
  int_4 bufs_this_file = 0;
  int_4 bufs_per_upd = 100;

  real_4 time_per_buffer = 0.0;
  if (rt) {
    time_per_buffer = float(LPER) / srate;
    if (float(bufs_per_upd)*time_per_buffer > 1.0) {
      bufs_per_upd = int(1.0/time_per_buffer);
      if (bufs_per_upd < 1) bufs_per_upd = 1;
    }
  }
   
  
  // =================================================== //
  m_sync();

  real_4 ref_time;
  m_secnds(0.0, ref_time);
  real_4 ref_time_ar = ref_time;
  real_4 ref_time_nf = ref_time;
  
  while (m_do(LPER, HIN.xfer_len)) {
    (void)m_mwget(idm, mode);
    if (mode == CLOSE) break;       // Abort from widget or Mc.break

    // Is it time to process some data?
    bool process = false;
    if (rt) {
      if (m_dwget(irt, new_srate) != 0) {
        if (new_srate > 0.0) {
	  srate = new_srate;
          time_per_buffer = float(LPER) / srate;
          bufs_per_upd = 100;
          if (float(bufs_per_upd)*time_per_buffer > 1.0) {
            bufs_per_upd = int(1.0/time_per_buffer);
            if (bufs_per_upd < 1) bufs_per_upd = 1;
          }
	}
      }
      if (mode != BLOCKED) {
	m_secnds(0.0, cur_time);
	elapsed = cur_time - ref_time;
	if (elapsed < 0.0) elapsed += 86400.0;
	if (elapsed >= time_per_buffer) {
          process = true;
          ref_time = cur_time;
	}
      }
    }
    else {
      if (mode != BLOCKED) process = true;
    }
    
    // Process data or pause a moment
    if (process) {
      ++buf_count;
      m_grabx(HIN, &buf[0], ngot);
      // if class == 4, add a special case for handling Type4000 data
      if (mode != 3) {
        m_filad(HOUT, &buf[0], ngot);
	++bufs_this_file;
      }
    }
    else {
      m_pause(Mc->pause);
    }
    
    // Write out actual data rate to widget
    if (mode == BLOCKED) {
      m_dwput(iar, 0.0);	// actual rate is 0 in block mode
    }
    // else update each <buf_per_upd> buffers
    else if (process && (buf_count%bufs_per_upd == 0)) {	
      m_secnds(0.0, cur_time_ar);
      elapsed_ar = cur_time_ar - ref_time_ar;
      if (elapsed_ar < 0.0) elapsed_ar += 86400.0;
      act_rate =  float(bufs_per_upd)*float(LPER) / elapsed_ar;
      m_dwput(iar, act_rate);
      ref_time_ar = cur_time_ar;
    }
    
    // Buffer count-down mode
    if (mode < 0) {
      ++mode;
      if (mode == 0) mode = amode;
      m_mwput(idm, mode);
    }
    
    // Is it time for new output file?
    if (nfmode != 0) {
      bool mk_new_file = false;
      if (nfmode == 1) {	// by buffers per file
        if (bufs_this_file >= nfblocks) mk_new_file = true;
      }
      else if (nfmode == 2) {
	real_4 cur_time_nf;
        m_secnds(0.0, cur_time_nf);
	elapsed_nf = cur_time_nf - ref_time_nf;
	if (elapsed_nf < 0.0) elapsed_nf += 86400.0;
	if (elapsed_nf > nftime) {
	  mk_new_file = true;
	  ref_time_nf = cur_time_nf;
	}
      }
      else if (nfmode == 3) {
        m_mwget(inf, wopen_nf);
	if (wopen_nf == 2) {
	  mk_new_file = true;
	  wopen_nf = 1;
	  m_mwput(inf, wopen_nf);
	}
      }
      if (mk_new_file) {
        m_close(HOUT);
	m_propagate(HIN, HOUT);
	++nf_count;
	HOUT.file_name = mk_nf_name(nf_root, nfn_type, nf_digits, nf_count);
	HOUT.xstart = HIN.xstart + HIN.xdelta*HIN.offset;
	if (nfvb) {
	  m_info("Opening new file: " + HOUT.file_name);
	}
	m_allocate(HOUT);
	bufs_this_file = 0;
      }
    } // end of if nfmode != 0 ..

  }   // end while m_do
  

  if (4 == HIN.class_) {
    (void)m_vrbfree(*reinterpret_cast<VRB*>(&buf[0]), 0);
  }

  m_close(HIN);
  m_close(HOUT);
}
	
