#!/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)
#       Ivan Jose Cernicharo Ortiz (ivan.cernicharo@bsc.es)

import sys
import logging
import datetime
import subprocess
import os
import configparser
import traceback

# Local imports
from utils.utils_files import *
from 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
    try:
        if os.path.exists(args[0]):        
            config_fname = os.path.basename(args[0])
            config = configparser.ConfigParser()
            config.read(args[0])
        else:
            raise Exception('Configuration file does not exist')

        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')
        
        # Optional parameters                
        varname = check_opt_param_config(config, 'general', 'variable')
        dt = check_opt_param_config(config, 'general', 'datatype')
        cz = int(check_opt_param_config(config, 'general', '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')

        # Init logging and list files
        log_file = initiate_logging(log_dir, path_files, config_fname)
        files = dir_or_file(path_files, fpattern)

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

    except Exception as e:
        print('ERROR: {}'.format(e))
        sys.exit()
        
    # For each section/test
    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':
                import test00_file_format as test
                file_format_ck = test.file_format_check(log_dir, files)
                test_res = file_format_ck.main()

            ## TEST 01
            elif s == 'standard_compliance':            
                import test01_standard_compliance as test            
                standard_ck = test.standard_compliance_test(log_dir, files)
                test_res = standard_ck.main()

            ## TEST 02
            elif s == 'spatial_completeness':            
                mf = check_param_config(config, s, 'mask_file')
                mv = check_param_config(config, s, 'mask_var')
                md = check_param_config(config, s, 'mask_dim')                
                import test02_spatial_completeness as test
                spatial_completeness_ck = test.spatial_completeness_test(log_dir, files, varname, dt, cz, mf, mv, md)
                test_res = spatial_completeness_ck.main()                               

            ## TEST 03
            elif s == 'temporal_completeness': 
                forms_dir = check_param_config(config, s, 'forms_dir')
                dataset = check_param_config(config, s, 'cds_dataset')
                variable = check_param_config(config, s, 'cds_variable')
                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      
                import test03_temporal_completeness as test            
                temporal_completeness_ck = test.temporal_completeness_test(log_dir, files, forms_dir, dataset, 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')
                import test04_spatial_consistency as test
                spatial_consistency_ck = test.spatial_consistency_test(log_dir, files, grid_typ, grid_int)
                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')
                import test05_temporal_consistency as test
                temporal_consistency_ck = test.temporal_consistency_test(log_dir, 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')
                import test06_valid_ranges as test
                valid_ranges_ck = test.valid_ranges_test(log_dir, files, varname, vmin, vmax, dt, cz)
                test_res = valid_ranges_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)
            print('Cancelled, exiting...')
            exit()
        except:            
            print_progress(100, 100)
            print('ERROR: Processing section {}'.format(s))
            print('ERROR: {}'.format(traceback.format_exc()))

        # Append result
        finally:            
            if test_res != None:
                config.add_section(s+'_result')
                config.set(s+'_result', 'res', test_res['res'])
                config.set(s+'_result', 'msg', test_res['msg'])
                config.set(s+'_result', 'log', log_file)                         
                if test_res['res'] == 'ok':
                    print('INFO: {} -> {}'.format('PASS', test_res['msg']))
                else:                    
                    print('ERROR: {} -> {}'.format('FAIL', test_res['msg']))

    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):
            for s in config.sections():
                if not('result' in s):
                    save_pdf_report(s, res_file, log_file)
    except Except:
        print('ERROR: Could not save results. Check res_dir and log_dir permissions')

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