#!/usr/bin/env python
""" Displays a single record of a file, pivoted with one field per line, with field names
    displayed as labels to the left of the field values.  Also allows simple navigation
    between records.

    arguments & options:
    -h, --help            show this help message and exit
    -i [INFILES [INFILES ...]], --infiles [INFILES [INFILES ...]]
                          input filenames or default to for stdin
    -o OUTFILE, --outfile OUTFILE
                          output filename or '-' for stdout (the default)
    -d DELIMITER, --delimiter DELIMITER
                          csv delimiter
    -q {quote_all,quote_minimal,quote_nonnumeric,quote_none},
    --quoting {quote_all,quote_minimal,quote_nonnumeric,quote_none}
                          csv quoting
    --quotechar QUOTECHAR
                          csv quotechar
    --escapechar ESCAPECHAR
                          csv escapechar
    --has-header          csv dialect - indicates header exists
    -r RECNUM, --recnum RECNUM
                          Displays this record number based on 0 offset,
                          defaulting to 1
    -V, --version         show version number then exit
    --long-help           Print more verbose help

    Examples:
       $ gristle_viewer sample.csv -r 3
                    Presents the third record in the file with one field per line
                    and field names from the header record as labels in the left
                    column.
       $ gristle_viewer sample.csv -r 3  -d '|' -q quote_none
                    In addition to what was described in the first example this
                    adds explicit csv dialect overrides.

    This source code is protected by the BSD license.  See the file "LICENSE"
    in the source code root directory for the full language or refer to it here:
       http://opensource.org/licenses/BSD-3-Clause
    Copyright 2011,2012,2013,2017 Ken Farmer
"""
#    To do:
#       1.  check for recnum > number of records in input file
#       2.  consider adding records up to some threshold to dictionary
#           to speed navigation for files.
#       3.  confirm that record counting works identical for each situation
#       5.  improve error if no args provided - recommend -h for help

import sys
from os.path import basename
from pprint import pprint as pp
from typing import Dict, List, Optional, Any, Tuple
from signal import signal, SIGPIPE, SIG_DFL

import datagristle.file_type as file_type
import datagristle.field_determinator  as field_determinator
import datagristle.field_type as field_type
import datagristle.configulator as configulator
import datagristle.csvhelper as csvhelper
import datagristle.file_io as file_io

#Ignore SIG_PIPE and don't throw exceptions on it... (http://docs.python.org/library/signal.html)
signal(SIGPIPE, SIG_DFL)

NAME = basename(__file__)
SHORT_HELP = 'View one record at a time from a csv formatted with headers\n'


def main():
    """ Analyzes file then displays a single record and allows simple
        navigation between records.
    """
    config = get_args()
    field_names = None

    while True:
        try:
            input_handler = file_io.InputHandler(config['infiles'],
                                                 config['delimiter'],
                                                 config['quoting'],
                                                 config['quotechar'],
                                                 config['has_header'])
        except EOFError:
            print('Warning: empty file')
            return 0
        else:
            if not field_names:
                field_names = get_field_names(config['infiles'], input_handler.dialect)
            rec = get_rec(input_handler, config['recnum'])
            input_handler.close()
            if rec is None:
                print('No record found')
                return

        output_handler = file_io.OutputHandler(config['outfile'], dialect=None)
        display_rec(rec, field_names, output_handler)
        output_handler.close()

        if config['outfile'] != '-':
            break

        response = input('Rec: %d     Q[uit] P[rev] N[ext] T[op], or a specific record number: ' % config['recnum']).lower()
        if response == 'q':
            break
        elif response == 'p':
            config['recnum'] -= 1
        elif response == 'n':
            config['recnum'] += 1
        elif response == 't':
            config['recnum'] = 0
        elif field_type._get_type(response) == 'integer':
            config['recnum'] = int(response)
        else:
            print('Invalid response, please enter q, p, n, t, or a specific record number')
    return 0



def get_field_names(input_files, dialect):

    my_file = file_type.FileTyper(input_files[0],
                                  delimiter=dialect.delimiter,
                                  quoting=csvhelper.get_quote_name(dialect.quoting))
    my_file.analyze_file()
    my_fields = field_determinator.FieldDeterminator(input_files[0],
                                                     my_file.format_type,
                                                     my_file.field_cnt,
                                                     dialect.has_header,
                                                     dialect)
    my_fields.analyze_fields()
    return my_fields.field_names



def display_rec(rec: str,
                field_names: List[str],
                output_handler: file_io.OutputHandler):
    """ Displays a single record
    """
    # figure out label length for formatting:
    if field_names:
        max_v_len = 0
        for val in field_names.values():
            if len(val) > max_v_len:
                max_v_len = len(val)
        min_format_len = max_v_len + 4
    else:
        for f_sub in range(len(rec)):
            field_names.append('field_%d' % f_sub)
        min_format_len = 12

    # write in column order:
    for f_sub in range(len(rec)):
        try:
            output_handler.write_text_rec('%-*s  -  %-40s\n' % (min_format_len, field_names[f_sub], rec[f_sub]))
        except KeyError:
            output_handler.write_text_rec('*** Missing Field - possibly due to csv parsing issues ***\n')




def get_rec(input_handler, recnum: int) -> Optional[List[Any]]:
    """ Gets a single record from a file
        Since it reads from the begining of the file it can take a while to get
        to records at the end of a large file

        To do:
           - possibly keep file open in case user wants to navigate about
           - possibly keep some of the data in a dictionary in case the user
             wants to navigate about
    """
    for row in input_handler:
        if input_handler.rec_cnt == recnum+1:
            result = row
            break
    else:
        result = None
    return result



class ViewerConfigulator(configulator.Config):
    def validate_custom_config(self, config: configulator.CONFIG_TYPE):
        if config['infiles'] == '-':
            self.parser.error('An input filename is required')



def get_args() -> Dict[str, Any]:
    config_mgr = ViewerConfigulator(NAME, SHORT_HELP, __doc__)
    config_mgr.add_standard_config('infiles')
    config_mgr.add_standard_config('outfile')
    config_mgr.add_standard_config('delimiter')
    config_mgr.add_standard_config('quoting')
    config_mgr.add_standard_config('quotechar')
    config_mgr.add_standard_config('escapechar')
    config_mgr.add_standard_config('has_header')
    config_mgr.add_custom_config(name='recnum',
                                 short_name='r',
                                 arg_type='option',
                                 default=0,
                                 config_type=int,
                                 help_msg='Displays this record number based on 0 offset, defaulting to 1')
    config_mgr.process_configs()
    return config_mgr.config



if __name__ == '__main__':
    sys.exit(main())
