#!/usr/bin/env python
# https://github.com/jgrelet/oceano2python

import argparse
import sys
import re
import PySimpleGUI as sg
import toml
import logging
from profile import Profile
from trajectory import Trajectory
from pathlib import Path
from configparser import ConfigParser
from glob import glob
import distutils.util as du
from physical_parameter import Roscop
import os.path as path


EXIT_SUCCESS = 0
EXIT_FAILURE = 1

dict_type = {'PROFILE': ['CTD', 'BTL','XBT','LADCP'], 'TRAJECTORY': ['TSG','MTO', 'CASINO']}

# typeInstrument is a dictionary as key: files extension
typeInstrument = {'CTD': ('cnv', 'CNV'), 'XBT': (
    'EDF', 'edf'), 'LADCP': ('lad', 'LAD'), 'TSG': ('colcor','COLCOR'), 'CASINO': ('csv'),
    'BTL': ('btl', 'BTL')}
#variables_1D = ['TIME', 'LATITUDE', 'LONGITUDE','BATH']
ti = typeInstrument  # an alias
filesBrowsePosition_row = 2
filesBrowsePosition_column = 1

# initialize filename use to save GUI configuration
configfile = 'oceano.cfg'
# default file name for ROSCOP csv
defaultRoscop = 'code_roscop.csv'

# example, PIRATA cruises:
# FR30:
# python oceano.py /m/PIRATA-FR30/data-processing/CTD/data/cnv/d*.cnv -i CTD -k PRES TEMP PSAL DOX2 FLU2 -r code_roscop.csv


def processArgs():
    # get the path to the module oceano2python, use to file defaut roscop_code
    roscop_file_path = path.dirname(path.abspath(__file__))

    parser = argparse.ArgumentParser(
        description='This program read multiple ASCII file, extract physical parameter \
                    following ROSCOP codification at the given column, fill arrays, write header file ',
        usage='\npython oceano.py data/CTD/cnv/dfr2900[1-3].cnv -i CTD -d\n'
        'python oceano.py data/CTD/cnv/dfr2900[1-3].cnv -i CTD -k PRES TEMP PSAL DOX2 DENS\n'
        'python oceano.py data/CTD/cnv/dfr29*.cnv -i CTD -d\n'
        'python oceano.py data/XBT/T7_0000*.EDF -i XBT -k DEPTH TEMP SVEL\n'
        'python oceano.py data/LADCP/*.lad -i LADCP -k DEPTH EWCT NSCT\n'
        'python oceano.py data/CTD/btl/fr290*.btl -i BTL -k BOTL PRES DEPTH ETDD TE01 TE02 PSA1 PSA2 DO11 DO12 DO21 DO22 FLU2'
        ' \n',
        epilog='J. Grelet IRD US191 - March 2019 / Feb 2020')
    parser.add_argument('-d', '--debug', help='display debug informations',
                        action='store_true')
    parser.add_argument('--demo', nargs='?', choices=ti.keys(),
                        help='specify the commande line for instrument, eg CTD, XBT, TSG, LADCP')
    parser.add_argument('-c', '--config', help="toml configuration file, (default: %(default)s)",
                        default='config.toml')
    parser.add_argument('-i', '--instrument', nargs='?', choices=ti.keys(),
                        help='specify the instrument that produce files, eg CTD, XBT, TSG, LADCP')
    parser.add_argument('-r', '--roscop', nargs='?',
                        help="specify location and ROSCOP file name,  (default: %(default)s)",
                        default=roscop_file_path +'/code_roscop.csv')
    parser.add_argument('-k', '--keys', nargs='+',
                        help='display dictionary for key(s), (default: %(default)s)')
    parser.add_argument('-g', '--gui', action='store_true',
                        help='use GUI interface')
    parser.add_argument('--database', help='save sqlite3 database in test.db instead of memory',
                        action='store_true')
    # type=argparse.FileType('r') don't work with under DOS 
    parser.add_argument('files', nargs='*',
                        help='ASCII file(s) to parse')                        
    return parser

# TODOS:
# DEPTH is missing
# file name is not clear at startup
# if no file selected, don't leave the program


def defineGUI():
    '''
    function to define and create the graphical interface, written with PySimpleGUI
    '''
    # check if GUI config file exist
    if args.instrument != None:
        instrument_default_value = args.instrument
    else:
        instrument_default_value = 'CTD'

    # get all devices
    devices = list(ti.keys())

    # change look and feel color scheme
    sg.ChangeLookAndFeel('SandyBeach')
    frameLayout = {}

    # define a frame layout for each instrument (device) with composite key as PRES_CTD, TEMP_XBT
    for d in devices:
        keys = cfg[d.lower()]['split'].keys()
        # List comprehensions
        frameLayout[d] = [[sg.Checkbox(k, key='{:s}_{:s}'.format(k, d),
                                       tooltip='Select the extract the physical parameter {}'.format(k))] for k in keys]

    # define GUI layout
    layout = ([[sg.Text('File(s) to read and convert')],                              # row 0
               [sg.Multiline(size=(40, 5), key='_IN_'),                               # row 1, col 0
                sg.Input(key='_HIDDEN_', visible=False,                               # row 1, col 1
                         enable_events=True),
                sg.FilesBrowse(key='_HIDDEN_', initial_folder=None,                   # row 1, col 2
                               tooltip='Choose one or more files')],
               [sg.Combo(list(ti.keys()), enable_events=True, size=(8, 1),          # row 2
                         default_value=instrument_default_value,
                         key='_DEVICE_', tooltip='Select the instrument')],
               [sg.Frame(d, frameLayout[d], key='_FRAME_{:s}'.format(d), visible=True)          # row 3
                for d in devices],
               [sg.OK(), sg.CloseButton('Cancel')]])                                  # row 4
    # [sg.CloseButton('Run'), sg.CloseButton('Cancel')]])

    # create a local instance windows used to reload the saved config from file
    window = sg.Window('Oceano converter', layout)
    window.Finalize()
    window.LoadFromDisk(configfile)

    return window


def updateFilesBrowseCombo(window, extentions, x, y):
    '''
    special function used to update the FilesBrowseCombo with canvas poisition
    instead of key because the same key is assign to shadow input object
    '''
    e = window.Rows[x][y]    # hardcoded
    e.FileTypes = []         # init to empty list
    for ext in extentions:
        e.FileTypes.append(("{} files".format(ext), "*.{}".format(ext)))

    e.initial_folder = 'data/{}'.format(extentions[0])
    window.Finalize

def process_gui():
          # setup the GUI windows Layout
        window = defineGUI()
        device = window.find_element('_DEVICE_').DefaultValue
        keys = cfg[device.lower()]['split'].keys()

        # can't update combo with FindElement('_HIDDEN_').Update(), we use this function
        # with hardcoded FilesBrowseCombo position
        updateFilesBrowseCombo(window,
            ti[device], filesBrowsePosition_column, filesBrowsePosition_row)

        # set the rigth frame for device visible, dosn't work
        #  File "C:\git\python\PySimpleGUI\PySimpleGUI.py", line 2362, in Update
        #  self.TKFrame.pack()
        #  AttributeError: 'NoneType' object has no attribute 'pack'
        # for d in list(ti.keys()):
        #     print(d)
        #     if d == device:
        #         window.FindElement(
        #             '_FRAME_{:s}'.format(d)).Update(visible=True)

        # main GUI loop
        while True:
           # display the main windows
            event, values = window.Read()

            #print(event, values)

            if event == 'Cancel' or event == None:
                raise SystemExit("Cancelling: user exit")

            if event == 'OK':  # end of initialization, process data now
                # values['_HIDDEN_'] is a string with files separated by ';' and Profile need a list
                files = values['_HIDDEN_'].split(';')
                args.files = files

                # test if a or more file are selected
                if not all(args.files):
                    sg.Popup("Cancel", "No filename supplied")
                    # raise SystemExit("Cancelling: no filename supplied")
                    continue
                break

            if event == '_DEVICE_':
                # you have to go into the bowels of the pygi code, to get the instance of the Combo
                # by the line and column number of the window to update its "fileType" property.
                device = values['_DEVICE_']
                updateFilesBrowseCombo(window,
                    ti[device], filesBrowsePosition_column, filesBrowsePosition_row)
                # TODOS: reset checkbox

            # update the Multilines instance from FilesBrowse return
            if event == '_HIDDEN_':
                window.Element('_IN_').Update(
                    values['_HIDDEN_'].split(';'))

        # save program configuration
        window.SaveToDisk(configfile)

        # debug return values from GUI
        logging.debug("Event: {}, Values: {}".format(event, values))

        # extract selected parameters (true) from dict values only for selected device
        args.keys = []
        for k in values.keys():
            if k[0] == '_' or values[k] == False:
                continue
            # check if device == device
            if not device in k:
                continue
            else:
                args.keys.append(re.sub('_\w+$', '', k))

        '''
        # process of files start here
        fe = process(args, cfg, device)

        # display result in popup GUI
        dims = "Dimensions: {} x {}".format(fe.n, fe.m)
        sg.PopupScrolled('Oceano2python', dims,
                         fe.disp(),  size=(80, 40))
        '''
        # It will output to a debug window. Bug ? debug windows xas closed before exiting program
        # print = sg.Print
        # or
        # print = sg.Print(size=(80,40))

        return window, fe, device



if __name__ == "__main__":
    '''
    usage:
    > python oceano.py data/CTD/cnv/dfr2900[1-3].cnv -i CTD
    > python oceano.py data/CTD/cnv/dfr29*.cnv -i CTD -k PRES TEMP PSAL DOX2 DENS -d
    > python oceano.py data/CTD/cnv/dfr2900[1-3].cnv -i CTD -k PRES TEMP PSAL DOX2 DENS
    > python oceano.py data/CTD/cnv/dfr29*.cnv -i CTD -d
    > python oceano.py data/XBT/T7_0000*.EDF -i XBT -k DEPTH TEMP SVEL
    > python oceano.py data/LADCP/*.lad -i LADCP - k DEPTH EWCT NSCT
    > python oceano.py data/CTD/btl/fr290*.btl -i BTL -k BOTL PRES DEPTH ETDD TE01 TE02 PSA1 PSA2 DO11 DO12 DO21 DO22 FLU2
    '''

    # recover and process line arguments
    parser = processArgs()
    args = parser.parse_args()

    # set looging mode if debug
    if args.debug:
        logging.basicConfig(
            format='%(levelname)s:%(message)s', level=logging.DEBUG)

    # read config Toml file and get the physical parameter list (Roscop code) for the specified instrument
    cfg = toml.load(args.config)
    # this the select device from command line !
    device = str(args.instrument)  # convert one element list to str

    # get roscop file
    if cfg['global']['codeRoscop'] != None:
        defaultRoscop = Path(cfg['global']['codeRoscop'])
    if args.roscop != None:
        defaultRoscop = args.roscop
    roscop = Roscop(defaultRoscop)

    # test arguments from sys.argv, args is never to None with default option set, without option
    # or -g, call GUI
    if args.gui or len(sys.argv) == 1:
        # define graphical interface
        window, fe, device = process_gui()

    else:
        # demo mode, only in command line
        if args.demo != None:
            print('demo mode: {}'.format(args.demo))
            sys.exit(1)
        # test if a or more file are selected
        else:
            if args.files == []:
                print(
                    'Error, you need to specify one or more files to process !!!', end='\n\n')
                parser.print_help(sys.stderr)
                sys.exit(1)
            
            # work with DOs, Git bash and Linux
            files = []
            for file in args.files:  
                files += glob(file)  
            args.files = files
   
        if device == 'None':
            print(
                'Error: missing option -i or --instrument, instrument = {}\n'.format(device))
            parser.print_help(sys.stderr)
            sys.exit(1)

    def search_dict(dict_type, instrument):
        for k,i in dict_type.items():
            if instrument in i:
                return(k)

    type = search_dict(dict_type, args.instrument)
    print(type)

    if type == 'PROFILE':
        context = Profile(args.files, roscop, args.keys)
    elif type == 'TRAJECTORY':
        context = Trajectory(args.files, roscop, args.keys)
    else:
        print(f"Invalide type: {type}")
        sys.exit()
        
    context.process(args, cfg, device)


    # write ASCII hdr and data files
    #ascii.writeAscii(cfg, device, fe, r)

    # write the NetCDF file
    #netcdf_profile.writeNetCDF(cfg, device, fe, r)