#!/usr/bin/env python
#
# Data Quality Checker also known as DQC
#
# Read the README.md for more detailed information
#
#       Barcelona Supercomputing Center - Centro Nacional de Supercomputacion
#       Earth Sciences Department - Computational Earth Sciences
#     
#     Authors: 
#       Joan Sala Calero (joan.sala@bsc.es)

import sys
import traceback

# Local imports
from dqc_wrapper.utils.utils_files import *
from dqc_wrapper.utils.utils_pdf import *

"""
Class: Versioning and messages of the wrapper
"""
class checker_body:

    def __init__(self, name):
        self.name = name
        self.version = "0.0.3"
        self.help_mssg()

    def __version__(self):
        
        string = """        
          _____     ____     _____
         |  __ \   / __ \   / ____|
         | |  | | | |  | | | |
         | |  | | | |  | | | |
         | |__| | | |__| | | |____
         |_____/   \___\_\  \_____|
        
          Data Quality Checker - version {} (C3512 project)
        """.format(self.version)
        
        print(string.replace('\t', ''))

    def help_mssg(self):
        string = "\n#"+ "#" * 80 + "\n#\n"
        string += "#"+ " " * 2+"Data Quality Checker Help message - How to run the DQC:\n#\n"
        string += "#"+ " " * 4+"$ python checker.py <config_file.ini>\n#\n"
        string += "#" + "#" * 80 + "\n"

        self.help = string

"""
Class: Main interface to the checks
---
- Reads config file as unique commandline operator
- Loops over the different checks/sections
"""
def main(args):
    dqc = checker_body("testing")
    dqc.__version__()

    # Help display
    try:
        if len(args) > 1:
            raise Exception('Too many arguments')
        if (args[0] in ["--help","-h","help","sos","commands"]):
            raise Exception('Help message')
    except:
        print(dqc.help)
        sys.exit()        

    # Read configuration and check mandatory fields in the general section   
    config = None
    config_fname = os.path.basename(args[0])
    try:
        if os.path.exists(args[0]):  
            config = configparser.ConfigParser()
            config.read(args[0])
        else:
            raise Exception('Configuration file does not exist')

        # General settings
        path_files = check_param_config(config, 'general', 'input')
        fpattern = check_param_config(config, 'general', 'fpattern')
        log_dir = check_param_config(config, 'general', 'log_dir')
        res_dir = check_param_config(config, 'general', 'res_dir')
        forms_dir = check_param_config(config, 'general', 'forms_dir')
        
        # Dataset settings
        cds_dataset = check_opt_param_config(config, 'dataset', 'cds_dataset')
        cds_variable = check_opt_param_config(config, 'dataset', 'cds_variable')
        varname = check_opt_param_config(config, 'dataset', 'variable')
        dt = check_datatype_config(config, 'dataset', 'datatype')
        cz = int(check_opt_param_config(config, 'dataset', 'chunksize', default=-1)) # default is no chunks

        # Directory checks
        if not os.path.exists(path_files):  
            raise Exception('Input directory does not exist. Check configuration file section [general]')
        if not log_dir:  
            raise Exception('Log directory not defined. Check configuration file section [general], value log_dir')
        if not res_dir:  
            raise Exception('Results directory not defined. Check configuration file section [general], value res_dir')
        if not os.path.exists(res_dir):
            os.makedirs(res_dir)

        # Init logging and list files
        log_file = initiate_logging(log_dir, path_files, config_fname)
        files = dir_or_file(path_files, fpattern)
        files_nc = dir_or_file(path_files, fpattern.replace('.grib', '.nc'), quiet=True)

        # Not needed anymore
        config.remove_section('general')
        config.remove_section('dataset')

    except Exception as e:
        print('ERROR: {}'.format(e))
        sys.exit()
        
    # Skip if zip file exists
    zipfname = get_zip_fname(res_dir, args[0])
    if os.path.exists(zipfname):
        print('INFO: Skipping {}'.format(zipfname))
        sys.exit()    

    # For each section/test
    files2zip = []
    err = False

    for s in config.sections(): 
        
        string = "." + "." * 80 + "\n"
        string += "Data Quality Checker [check={}]\n".format(s)        
        string += "." + "." * 80 + "\n"
        print(string)        

        # Each section is a different test available  
        try:
            test_res = None

            ## TEST 00
            if s == 'file_format':
                from dqc_wrapper.test00_file_format import FileFormatCheck
                file_format_ck = FileFormatCheck(files)
                test_res = file_format_ck.main()

            ## TEST 01
            elif s == 'standard_compliance':
                from dqc_wrapper.test01_standard_compliance import StandardComplianceTest
                standard_ck = StandardComplianceTest(files, forms_dir, cds_dataset, cds_variable)
                test_res = standard_ck.main()

            ## TEST 02
            elif s == 'spatial_completeness':                                            
                mf = check_opt_param_config(config, s, 'mask_file', default='') # default is without mask
                mv = check_opt_param_config(config, s, 'mask_var', default='lsm') # default is lsm (land_sea_mask)
                from dqc_wrapper.test02_spatial_completeness import SpatialCompletenessTest
                spatial_completeness_ck = SpatialCompletenessTest(files, varname, dt, cz, mf, mv)
                test_res = spatial_completeness_ck.main()                               

            ## TEST 03
            elif s == 'temporal_completeness':
                origin = check_param_config(config, s, 'origin')
                system = check_param_config(config, s, 'system')
                is_s = check_opt_param_config(config, s, 'is_seasonal', default='no') # default is not seasonal      
                from dqc_wrapper.test03_temporal_completeness import TemporalCompletenessTest
                temporal_completeness_ck = TemporalCompletenessTest(
                    files, forms_dir,
                    cds_dataset, cds_variable, origin, system, is_s)
                test_res = temporal_completeness_ck.main()

            ## TEST 04
            elif s == 'spatial_consistency':
                grid_int = check_param_config(config, s, 'grid_interval')
                grid_typ = check_param_config(config, s, 'grid_type')
                grid_map = check_opt_param_config(config, s, 'grid_mapping_name', default='') # default is empty
                dual_res = check_opt_param_config(config, s, 'dual_resolution', default='no') # default is one resolution
                from dqc_wrapper.test04_spatial_consistency import SpatialConsistencyTest
                spatial_consistency_ck = SpatialConsistencyTest(
                    files, grid_typ, grid_int, grid_map, dual_res)
                test_res = spatial_consistency_ck.main()

            ## TEST 05
            elif s == 'temporal_consistency':
                time_s = check_param_config(config, s, 'time_step')
                time_g = check_param_config(config, s, 'time_granularity')
                from dqc_wrapper.test05_temporal_consistency import TemporalConsistencyTest
                temporal_consistency_ck = TemporalConsistencyTest(
                    files, time_s, time_g)
                test_res = temporal_consistency_ck.main()          

            ## TEST 06
            elif s == 'valid_ranges':
                vmin = check_param_config(config, s, 'valid_min')
                vmax = check_param_config(config, s, 'valid_max')
                from dqc_wrapper.test06_valid_ranges import ValidRangesTest
                valid_ranges_ck = ValidRangesTest(
                    res_dir, config_fname, files, varname, vmin, vmax, dt, cz,
                    forms_dir, cds_dataset, cds_variable)
                test_res = valid_ranges_ck.main()          
                files2zip.append(test_res['plot'])

            ## TEST 07 - Extra (NetCDF converter - CF compliance)
            elif s == 'netcdf_converter':
                from dqc_wrapper.test01_standard_compliance import StandardComplianceTest
                standard_ck = StandardComplianceTest(files_nc, forms_dir, cds_dataset, cds_variable)
                test_res = standard_ck.main()

            else:
                raise Exception('Test {} is not available in the current checker implementation. Please check your config syntax')
        
        # Exceptions/Errors
        except KeyboardInterrupt:
            print_progress(100, 100)
            err=True
            print('Cancelled, exiting...')
            exit()
        except:            
            print_progress(100, 100)
            err=True
            print('ERROR: Processing section {}'.format(s))
            print('ERROR: {}'.format(traceback.format_exc()))

        # Append result
        finally:
            config = save_config(config, s, test_res)

    try:
        # Save TXT result object
        res_file = save_result(config, res_dir, config_fname)

        # Save PDF report (result + log)        
        if os.path.exists(res_file) and os.path.exists(log_file) and not(err):
            for s in config.sections():
                if not('result' in s):
                    files2zip.append(save_pdf_report(s, res_file, log_file))


    except:
        print('ERROR: Could not save results. Check res_dir and log_dir permissions')

    # Zip all results
    if not(err):         
        zip_results(zipfname, files2zip)


if __name__ == "__main__":
    args = sys.argv[1:]
    sys.exit(main(args))