###############################################################################
# Copyright (c) 2011 - 2012 Trimble Navigation Ltd
# $Id: NoPiSC_Process.py,v 1.4 2012/08/23 21:51:48 shankar Exp $
###############################################################################
# NoPiSC_Process.py
#
# This module has functions necessary to concatenate single day NoPi output mtb
# files into a single mtb
###############################################################################

import os
import sys

###############################################################################
# Include NoPi_Utils/ directory to Python's search path for modules
###############################################################################

def add_utils_dir_to_path() :
  global file_dir
  file_path = os.path.abspath(sys.argv[0])
  file_dir = os.path.dirname(file_path)
  top_dir = os.path.dirname(file_dir)
  new_dir = os.path.join(top_dir, 'NoPi_Utils') 
  sys.path.insert(0, new_dir) 

  return()

add_utils_dir_to_path()

from NoPiUT_Common_Meas import *

global master_combo_list, previous_day
master_combo_list = []
previous_day      = []
DAY_INVALID       = -1
IDX_INVALID       = -1

###############################################################################
# Clears previous_day[] to DAY_INVALID. This is required at the end of each
# iteration over baselines. If any new combo is found, a new row is appended to
# the list.
###############################################################################

def clear_prev_day_reg() :

  for idx in range( 0, len( previous_day ) ) :
    previous_day[ idx ] = DAY_INVALID

###############################################################################
# This function checks if combos listed in a single day's diffs_summary file
# were previously included in the master_combos list. master_combos lists all
# unique combos found in any diffs_summary file. A combo's row number in
# master_combo list is used as its master combo number.
#
# Any new combo is included in the master_combos and previous_day lists.
###############################################################################

def update_master_combo_list( diffs_summ, data_path, baseline, day_num ) :

  # Loop over each combo read from summary file and check if it exists
  # in the master combo list. If the combo is new, append to
  # master_combo and previous_day
  for combo_idx in range( diffs_summ.num_combos ) :
    combo_name = construct_combo_name( diffs_summ, combo_idx )
    # If combo is valid, check if it is a new combo
    if diffs_summ.combo[ combo_idx ].combo_vld :
      master_combo_num = get_master_combo_num( combo_name )
      if ( master_combo_num == IDX_INVALID ) :
        tmp_list = [ combo_name, diffs_summ.combo[ combo_idx ].resolve_sdiff ]
        master_combo_list.append( tmp_list )
        master_combo_num = len( master_combo_list ) - 1
        previous_day.append( DAY_INVALID )

      # Find the current day stats_combo file and append it to the
      # allstats file.
      stats_fname = data_path + 'stats_combo_' + str( combo_idx ) + '.mtb'
      concat_stats( stats_fname, master_combo_num, baseline, day_num )

  return( )

###############################################################################
# Check if a combo exists in the master combo list. If the combo exists, return
# the index number within the "master" combo list.
###############################################################################

def get_master_combo_num( combo_name ) :

  combo_num = IDX_INVALID

  # Check if combo exists in master combo list.If found, return combo
  # index
  for idx in range( 0, len( master_combo_list ) ) :
    # master_combo_list is a nx2 matrix:
    # Col 0: Combo Name Col 1: SD/DD Amb Res.
    tmp_combo = master_combo_list[idx][0]
    if ( tmp_combo == combo_name ) :
      combo_num = idx
      break

  return( combo_num )

###############################################################################
# This function concatenates a single day's statistics file to an allstats file
# specific to each baseline/combo.
#
# If data is over non consecutive days, appropriate NaNs are included
# in the concatenated file. This is done to simplify plotting
# statistics.
###############################################################################

def concat_stats( stats_fname, master_combo_num, baseline, day_num ) :

  # Ensure single day stats_combo file exists. If it does not, either
  # Stats Generator was not invoked or the file was deleted  
  if ( os.access( stats_fname, os.R_OK ) ) :  
    stats = read_stats_file( stats_fname )
  else :
    print 'Missing stats file: %s Baseline: %s Date: %s' % ( stats_fname, 
                                                             baseline, 
                                                             day_num )
    return()

  # Generate concatenated stats file name. The files are written to
  # the user specified top level data directory
  # Concatenated stats file name format: 
  # allstats_<baseline>_combo_<unique combo #>.mtb
  allstats_fname = file_dir + '/' + 'allstats_'

  if ( baseline != '' ) :  # Empty baseline name eg: Apollo zero baseline
    allstats_fname += baseline + '_'

  allstats_fname += ( 'combo_' 
                      + str( master_combo_num ) 
                      + '.mtb' )

  prev_day_num = previous_day[ master_combo_num ]

  # If a combo does not processed on any two consecutive days, append
  # NaNs before concatenating the current day's statistics.
  if ( not( os.path.exists( allstats_fname ) ) or 
       prev_day_num == DAY_INVALID ) :
    fout = open( allstats_fname, 'wt' )
  else :
    fout = open( allstats_fname, 'a' )  
  
  # Append NaNs if data is not from consecutive days.
  if ( prev_day_num != DAY_INVALID and 
       day_num - prev_day_num > 1 ) :
    nan_string = get_nan_string()
    append_stats( fout, nan_string, prev_day_num + 1 )
  # Append current day's stats data to the allstats file and update previous_day[]
  append_stats( fout, stats, day_num )
  previous_day[ master_combo_num ] = day_num
  fout.close()

  return()

###############################################################################
# Function writes each line from a stats_combo file to the
# concatenated stats file
#
# NOTE: The concatenated file is not closed within this function. This
# is support the case when both NaNs and a single day's statistics
# must be appended to the same concatenated stats file.
###############################################################################

def append_stats( fout, stats, day_num ) :
  for line in stats :
    fout.write( '%2d%s' % ( day_num, line ) )
  return()

###############################################################################
# Generate a string of NaNs to append to the concatenated stats
# file. Follows the same format as used in each row of a stats_combo
# file
###############################################################################

def get_nan_string( ) :
  num_metrics = get_num_metrics()
  sv_id       = 0
  nan_string  = []
  for meas_type in range( 3 ) :
    nan_string.append( '%2d %d %d %d %d %6d %s\n' % \
                       ( sv_id,
                         meas_type,
                         0,  # Elevation Mask
                         0,  # CNo Mask
                         0,  # Bit Flag
                         0,  # Total Epochs
                         'NaN '*num_metrics ) )
  
  return( nan_string )

###############################################################################
# Generate a string of NaNs to append to the concatenated stats
# file. Follows the same format as used in each row of a stats_combo
# file
###############################################################################

def read_stats_file( stats_fname ) :
  fin = open( stats_fname, 'rt' )
  file_lines = fin.readlines()
  fin.close()

  return( file_lines )

###############################################################################
# Generate a string of NaNs to append to the concatenated stats
# file. Follows the same format as used in each row of a stats_combo
# file.
#
# get_metrics_list() is defined in NoPi_Utils/NoPiUT_Common_Meas.py
###############################################################################

def get_num_metrics() :
  metrics = get_metrics_list()

  return( len( metrics ) )

###############################################################################
# Create a summary file containing: start_date, end_date, baseline
# processed and master combo list. 
# This file will be used when invoking the stats display scripts. 
###############################################################################

def create_concat_summary_file( baseline_name, baseline_dir, start_date, end_date ) :

  fname = file_dir + '/' + 'concat_summary.txt'

  fout = open( fname, 'wt' )
  fout.write( '%s\n' % start_date.strftime('%Y%m%d') )
  fout.write( '%s\n' % end_date.strftime('%Y%m%d') )

  # Write each baseline to summary file
  num_baselines = len( baseline_name )
  fout.write( '%d\n' % num_baselines )
  for idx in range( 0, num_baselines ) :
    fout.write( '%s, %s\n' % ( baseline_name[idx],     \
                               baseline_dir[idx] ) 
              )

  # Loop over master_combo_list and write to file
  num_combos = len( master_combo_list )
  fout.write( '%d\n' % num_combos )
  # Each row contains: <combo_name>, < SD/DD Ambiguity Resolution Flag>
  for idx in range( 0, num_combos ) :
    fout.write( '%s, %d\n' % ( master_combo_list[idx][0],  \
                               master_combo_list[idx][1] )
              )

  fout.close()

  return()
