"""

purpose: assorted function, which are a bit untested


Functions:
----------
importDataFromFolder - IF the user already has a data folder with certain
specifications
>> from supplychainmodelhelper import datahandling as dh
>> help(dh.importDataFromFolder)
this function may be used to create a hdf5 database without much effort

decBSList - if a list of strings or bytestrings are given, this function
returns a list of strings. No need to check if some string is one or the
other, just apply this to your list.

decBS - if a string or bytestring is given, this function returns a string.
No need to check if some string is one or the other, just apply this to your
list.
"""

import csv #1.0
import h5py # 2.10.0
import pandas as pd # 0.25.2
import os
import warnings
warnings.filterwarnings("ignore")



def decBSList(inputList: list) -> list:
    """
    purpose:convert list of bytestrings to list of strings
    if it is already a string, do nothing

    :param inputList: a list of strings to be converted
    (may be str or bytestring)

    :return: list of strings converted to str

    example:
    from supplychainmodelhelper import datahandling as dh
    blist = [b'test',b'another test',b'not another test']
    strlist = dh.decBSlist(blist)

    """
    definiteList = []
    for i in range(len(inputList)):  
        try:
            definiteList.append(inputList[i].decode())
        except (UnicodeDecodeError, AttributeError):
            definiteList.append(inputList[i])
   
    return definiteList


def decBS(inputString: str) -> str:
    """
    purpose: convert list of bytestrings to list of strings
    if it is already a string, do nothing

    :param inputString: a str or bytestring

    :return: string

    example:
    from supplychainmodelhelper import datahandling as dh
    blist = b'test'
    strlist = dh.decBSlist(blist)

    """
    try:
        definiteString = inputString.decode()
    except (UnicodeDecodeError, AttributeError):
        definiteString = inputString
   
    return definiteString


def importDataFromFolder(
        fileOutputName: str,
        fileInputPath: str,
        fileInMetadataName='folderMD.csv'
) -> bool:
    """
    purpose: import files from existing folder structure
    folder needs to consists of folderMD.csv:
        - minimum entries for headers of folderMD.csv:
            'Folder', 'ID', 'Title', 'Description' optional to enter more
            categories, will be automatically integrated into database
        - fill this csv file with naming the data containing folders,
            give unique IDs and proper title and description
        - every subfolder mentioned in folderMD.csv should contain another
            csv file named as the subfolder (e.g. subfolder is 'Test',
            so the csv file in 'Test' should be named 'TestMD.csv')
        - minimum entries for headers of 'subfolder'MD.csv:
            'Dataset',
            'Filename',
            'Format',
            'ID',
            'Title',
            'Rows',
            'Columns',
            'Encoding'
            ('Dataset' is the identifier you give when retrieving dataset
            from db)
        - put file with 'Filename' into this subfolder, this file will be
            added to database (with header!)

    TODO maybe an example folder?
    current implementation: 1D / 2D dataset only

    :param fileOutputPath:
    :param fileInputPath: the path of the folder 
    :param fileInMetadataName(optional): filename of folderMD.csv(default), maybe different filename
    
    :return: true if input file existed and consistent with all data in folder

    example: 
    from supplychainmodelhelper import datahandling as dh
    path2DataFolder = '../data/'#folder where your data set lies
    myfname = 'test.hdf5'

    didItWork4 = dh.importDataFromFolder(
        fileOutputName=myfname,
        fileInputPath=path2DataFolder
    )
    if didItWork4:
        print('yes!')
    else:
        print('no!')

    """
    done = []
    

    done.append(True) \
        if isinstance(fileInputPath, str) \
        else done.append(False)
    done.append(True) \
        if isinstance(fileInMetadataName, str) \
        else done.append(False)
    done.append(True) \
        if isinstance(fileOutputName, str) \
        else done.append(False)

    # start reading metadata structure
    folderMD = csv.DictReader(
        open(fileInputPath + fileInMetadataName),
        delimiter=';'
    )
    done.append(True) \
        if(os.path.exists(fileInputPath + fileInMetadataName)) \
        else done.append(False)
    
    listOfContent = []
    # writing the hdf5 structure
    with h5py.File(fileOutputName,'w') as db:
        ####################################################
        # creating the structure from meta data schema table
        ####################################################
        # getting the metadata from each metadata table in
        # each of the folders as described in fileFolderLoc
        for row in folderMD:
            listOfContent.append(row['Folder'])
            myAttrs = list(row.keys())        
            # creating the folders and look out for subfolders
            if not row['Subfolder']:
                db.create_group(row['Folder'])
                for run in myAttrs:
                    # attach all info to metadata schema
                    if run != 'Folder' and run != 'Subfolder':
                        db[row['Folder']].attrs[run] = row[run]

            # creating the subfolders(aka subgroups) inside hdf5 db
            # if existing in metadata file
            if row['Subfolder']:
                db[row['Folder']].create_group(row['Subfolder'])

                for run in myAttrs:
                    if run != 'Folder' and run != 'Subfolder':
                        db[
                            row['Folder'] +
                            '/' +
                            row['Subfolder']
                        ].attrs[run] = row[run]
        
    ####################################################
    # incorporating the datasets with corresponding metadata
    ####################################################
        for foldername in db:
    
            #print(foldername)
            # code assumes
            #   that meta datafile is located in file folder and
            #   last 2 letters of filename is "MD" and
            #   is in csv-format
            metaDataFile = csv.DictReader(
                open(
                    fileInputPath +
                    foldername +
                    "/" +
                    foldername +
                    "MD.csv"
                ),
                delimiter=';'
            )
            done.append(True) if(os.path.exists(
                fileInputPath +
                foldername +
                "/" +
                foldername +
                "MD.csv")
            ) else done.append(False)
            
            for row in metaDataFile:
            #     # read in the data set:
            #     NEEDS to have index and column header in csv file
            #     #print(row)
                dataSet = pd.read_csv(
                    fileInputPath + foldername + "/" + row['Filename'],
                    header=0,
                    sep=';',
                    index_col=0,
                    encoding=row['Encoding'],
                    dtype='str'
                )
                done.append(True) if(os.path.exists(
                    fileInputPath+foldername+"/"+row['Filename'])
                ) else done.append(False)
                dataSet = dataSet.fillna(value=0)

            #     # create the hdf5 dataset and store it in the hdf5-file
                dt = h5py.string_dtype(encoding='utf-8')
                d5 = db[foldername].create_dataset(
                    row['Dataset'],
                    data=dataSet,
                    dtype=dt
                )
                
                # attach metadata information to newly created data set
                myAttrs = list(row.keys())
                for run in myAttrs:
                    if run != 'Dataset':
                        db[
                            foldername+
                            '/'+
                            row['Dataset']
                        ].attrs[run] = row[run]
                

                # attach dimension scale from csv file,
                # ASSUMING it's always a 2d table...
                # TODO make it dynamical, so that the reader recognizes
                #  the format!
                # has a unique ID, given that in the MD-files
                # this ID is unique (filewise), as well!
                # TODO currently unique ID is given in filename
                #  --> make it internal UID!
                try:
                    dtIndex = 'i'
                    db[foldername].create_dataset(
                        "ScaleRowOfDataSet"+row['ID'],
                        data=dataSet.index.astype(int).values,
                        dtype=dtIndex
                    )
                except TypeError:
                    # this only works for py3!
                    dtIndex = h5py.string_dtype(encoding='utf-8')
                    db[foldername].create_dataset(
                        "ScaleRowOfDataSet"+row['ID'],
                        data=dataSet.index.values,
                        dtype=dtIndex
                    )
        
                # attach the header row and header columne of the
                # table as meta data info
                # TODO automatic recognition of header
                #  -> cell data type analysis (header = string, data=not string)
                d5.dims[0].label = row['Rows']
                d5.dims[0].attach_scale(
                    db[foldername +
                       "/ScaleRowOfDataSet" +
                       row['ID']
                    ]
                )
                

                try:
                    dtCol = 'i'
                    db[foldername].create_dataset(
                        "ScaleColOfDataSet" + row['ID'],
                        data=dataSet.columns.astype(int).values,
                        dtype=dtCol
                    )
                except TypeError:
                    # this only works for py3!
                    dtCol = h5py.string_dtype(encoding='utf-8')
                    db[foldername].create_dataset(
                        "ScaleColOfDataSet" + row['ID'],
                        data=dataSet.columns.values,
                        dtype=dtCol
                    )
                
                # encoding list of strings to list of b-strings (py3 necessity)
                d5.dims[1].label = row['Columns']
                d5.dims[1].attach_scale(
                    db[
                        foldername +
                        "/ScaleColOfDataSet" +
                        row['ID']
                    ]
                )

        # DATABASE IS WRITTEN AND FILE IS CLOSED!
    #done = True
    return all(done)