"""
 @file   01_test.py
 @brief  Script for test
 @author Toshiki Nakamura, Yuki Nikaido, and Yohei Kawaguchi (Hitachi Ltd.)
 Copyright (C) 2020 Hitachi, Ltd. All right reserved.
"""

########################################################################
# import default python-library
########################################################################
import os
import glob
import csv
import re
import itertools
import sys
########################################################################


########################################################################
# import additional python-library
########################################################################
import numpy
# from import
from tqdm import tqdm
from sklearn import metrics
# original lib
import common as com
import keras_model
########################################################################


########################################################################
# load parameter.yaml
########################################################################
param = com.yaml_load()
#######################################################################


########################################################################
# def
########################################################################
def save_csv(save_file_path,
             save_data):
    with open(save_file_path, "w", newline="") as f:
        writer = csv.writer(f, lineterminator='\n')
        writer.writerows(save_data)


def get_machine_id_list_for_test(target_dir,
                                 dir_name="test",
                                 ext="json"):
    """
    target_dir : str
        base directory path of "dev_data" or "eval_data"
    test_dir_name : str (default="test")
        directory containing test data
    ext : str (default="wav)
        file extension of audio files

    return :
        machine_id_list : list [ str ]
            list of machine IDs extracted from the names of test files
    """
    # create test files
    dir_path = os.path.abspath("{dir}/{dir_name}/*.{ext}".format(dir=target_dir, dir_name=dir_name, ext=ext))
    file_paths = sorted(glob.glob(dir_path))
    # extract id
    machine_id_list = sorted(list(set(itertools.chain.from_iterable(
        [re.findall('id_[0-9][0-9]', ext_id) for ext_id in file_paths]))))
    return machine_id_list


def test_file_list_generator(target_dir,
                             id_name,
                             dir_name="test",
                             prefix_normal="normal",
                             prefix_anomaly="anomaly",
                             ext="json"):
    """
    target_dir : str
        base directory path of the dev_data or eval_data
    id_name : str
        id of wav file in <<test_dir_name>> directory
    dir_name : str (default="test")
        directory containing test data
    prefix_normal : str (default="normal")
        normal directory name
    prefix_anomaly : str (default="anomaly")
        anomaly directory name
    ext : str (default="wav")
        file extension of audio files

    return :
        if the mode is "development":
            test_files : list [ str ]
                file list for test
            test_labels : list [ boolean ]
                label info. list for test
                * normal/anomaly = 0/1
        if the mode is "evaluation":
            test_files : list [ str ]
                file list for test
    """
    com.logger.info("target_dir : {}".format(target_dir+"_"+id_name))

    # development
    if mode:
        normal_files = sorted(
            glob.glob("{dir}/{dir_name}/{prefix_normal}_{id_name}*.{ext}".format(dir=target_dir,
                                                                                 dir_name=dir_name,
                                                                                 prefix_normal=prefix_normal,
                                                                                 id_name=id_name,
                                                                                 ext=ext)))
        normal_labels = numpy.zeros(len(normal_files))
        anomaly_files = sorted(
            glob.glob("{dir}/{dir_name}/{prefix_anomaly}_{id_name}*.{ext}".format(dir=target_dir,
                                                                                  dir_name=dir_name,
                                                                                  prefix_anomaly=prefix_anomaly,
                                                                                  id_name=id_name,
                                                                                  ext=ext)))
        anomaly_labels = numpy.ones(len(anomaly_files))
        files = numpy.concatenate((normal_files, anomaly_files), axis=0)
        labels = numpy.concatenate((normal_labels, anomaly_labels), axis=0)
        com.logger.info("test_file  num : {num}".format(num=len(files)))
        if len(files) == 0:
            com.logger.exception("no_wav_file!!")
        print("\n========================================")

    # evaluation
    else:
        files = sorted(
            glob.glob("{dir}/{dir_name}/*{id_name}*.{ext}".format(dir=target_dir,
                                                                  dir_name=dir_name,
                                                                  id_name=id_name,
                                                                  ext=ext)))
        labels = None
        com.logger.info("test_file  num : {num}".format(num=len(files)))
        if len(files) == 0:
            com.logger.exception("no_wav_file!!")
        print("\n=========================================")

    return files, labels
########################################################################


########################################################################
# main 01_test.py
########################################################################
if __name__ == "__main__":
    # check mode
    # "development": mode == True
    # "evaluation": mode == False
    mode = com.command_line_chk()
    if mode is None:
        sys.exit(-1)

    # make output result directory
    os.makedirs(param["result_directory"], exist_ok=True)

    # load base directory
    dirs = com.select_dirs(param=param, mode=mode)

    # initialize lines in csv for AUC and pAUC
    csv_lines = []

    # loop of the base directory
    for idx, target_dir in enumerate(dirs):
        print("\n===========================")
        print("[{idx}/{total}] {dirname}".format(dirname=target_dir, idx=idx+1, total=len(dirs)))
        machine_type = os.path.split(target_dir)[1]

        print("============== MODEL LOAD ==============")
        # set model path
        model_file = "{model}/model_{machine_type}.hdf5".format(model=param["model_directory"],
                                                                machine_type=machine_type)

        # load model file
        if not os.path.exists(model_file):
            com.logger.error("{} model not found ".format(machine_type))
            sys.exit(-1)
        model = keras_model.load_model(model_file)
        model.summary()

        if mode:
            # results by type
            csv_lines.append([machine_type])
            csv_lines.append(["id", "AUC", "pAUC"])
            performance = []

        machine_id_list = get_machine_id_list_for_test(target_dir)

        print(machine_id_list)
        for id_str in machine_id_list:
            # load test file
            test_files, y_true = test_file_list_generator(target_dir, id_str)

            # setup anomaly score file path
            anomaly_score_csv = "{result}/anomaly_score_{machine_type}_{id_str}.csv".format(
                                                                                     result=param["result_directory"],
                                                                                     machine_type=machine_type,
                                                                                     id_str=id_str)
            anomaly_score_list = []

            print("\n============== BEGIN TEST FOR A MACHINE ID ==============")
            y_pred = [0. for k in test_files]
            for file_idx, file_path in tqdm(enumerate(test_files), total=len(test_files)):
                try:
                    data = com.file_to_vector_array(file_path,
                                                    n_mels=param["feature"]["n_mels"],
                                                    frames=param["feature"]["frames"],
                                                    n_fft=param["feature"]["n_fft"],
                                                    hop_length=param["feature"]["hop_length"],
                                                    power=param["feature"]["power"])
                    errors = numpy.mean(numpy.square(data - model.predict(data)), axis=1)
                    y_pred[file_idx] = numpy.mean(errors)
                    anomaly_score_list.append([os.path.basename(file_path), y_pred[file_idx]])
                except:
                    com.logger.error("file broken!!: {}".format(file_path))

            # save anomaly score
            save_csv(save_file_path=anomaly_score_csv, save_data=anomaly_score_list)
            com.logger.info("anomaly score result ->  {}".format(anomaly_score_csv))

            if mode:
                # append AUC and pAUC to lists
                auc = metrics.roc_auc_score(y_true, y_pred)
                p_auc = metrics.roc_auc_score(y_true, y_pred, max_fpr=param["max_fpr"])
                csv_lines.append([id_str.split("_", 1)[1], auc, p_auc])
                performance.append([auc, p_auc])
                com.logger.info("AUC : {}".format(auc))
                com.logger.info("pAUC : {}".format(p_auc))

            print("\n============ END OF TEST FOR A MACHINE ID ============")

        if mode:
            # calculate averages for AUCs and pAUCs
            averaged_performance = numpy.mean(numpy.array(performance, dtype=float), axis=0)
            csv_lines.append(["Average"] + list(averaged_performance))
            csv_lines.append([])

    if mode:
        # output results
        result_path = "{result}/{file_name}".format(result=param["result_directory"], file_name=param["result_file"])
        com.logger.info("AUC and pAUC results -> {}".format(result_path))
        save_csv(save_file_path=result_path, save_data=csv_lines)

[((36, 8, 36, 23), 'common.yaml_load', 'com.yaml_load', ({}, {}), '()', True, 'import common as com\n'), ((154, 11, 154, 33), 'common.command_line_chk', 'com.command_line_chk', ({}, {}), '()', True, 'import common as com\n'), ((159, 4, 159, 57), 'os.makedirs', 'os.makedirs', (), '', False, 'import os\n'), ((162, 11, 162, 50), 'common.select_dirs', 'com.select_dirs', (), '', True, 'import common as com\n'), ((46, 17, 46, 51), 'csv.writer', 'csv.writer', (), '', False, 'import csv\n'), ((67, 24, 67, 43), 'glob.glob', 'glob.glob', ({(67, 34, 67, 42): 'dir_path'}, {}), '(dir_path)', False, 'import glob\n'), ((123, 16, 123, 72), 'numpy.concatenate', 'numpy.concatenate', (), '', False, 'import numpy\n'), ((124, 17, 124, 75), 'numpy.concatenate', 'numpy.concatenate', (), '', False, 'import numpy\n'), ((156, 8, 156, 20), 'sys.exit', 'sys.exit', ({(156, 17, 156, 19): '(-1)'}, {}), '(-1)', False, 'import sys\n'), ((182, 16, 182, 50), 'keras_model.load_model', 'keras_model.load_model', ({(182, 39, 182, 49): 'model_file'}, {}), '(model_file)', False, 'import keras_model\n'), ((127, 12, 127, 49), 'common.logger.exception', 'com.logger.exception', ({(127, 33, 127, 48): '"""no_wav_file!!"""'}, {}), "('no_wav_file!!')", True, 'import common as com\n'), ((140, 12, 140, 49), 'common.logger.exception', 'com.logger.exception', ({(140, 33, 140, 48): '"""no_wav_file!!"""'}, {}), "('no_wav_file!!')", True, 'import common as com\n'), ((171, 23, 171, 48), 'os.path.split', 'os.path.split', ({(171, 37, 171, 47): 'target_dir'}, {}), '(target_dir)', False, 'import os\n'), ((179, 15, 179, 41), 'os.path.exists', 'os.path.exists', ({(179, 30, 179, 40): 'model_file'}, {}), '(model_file)', False, 'import os\n'), ((181, 12, 181, 24), 'sys.exit', 'sys.exit', ({(181, 21, 181, 23): '(-1)'}, {}), '(-1)', False, 'import sys\n'), ((227, 22, 227, 59), 'sklearn.metrics.roc_auc_score', 'metrics.roc_auc_score', ({(227, 44, 227, 50): 'y_true', (227, 52, 227, 58): 'y_pred'}, {}), '(y_true, y_pred)', False, 'from sklearn import metrics\n'), ((228, 24, 228, 87), 'sklearn.metrics.roc_auc_score', 'metrics.roc_auc_score', (), '', False, 'from sklearn import metrics\n'), ((238, 46, 238, 83), 'numpy.array', 'numpy.array', (), '', False, 'import numpy\n'), ((209, 27, 214, 84), 'common.file_to_vector_array', 'com.file_to_vector_array', (), '', True, 'import common as com\n'), ((216, 39, 216, 57), 'numpy.mean', 'numpy.mean', ({(216, 50, 216, 56): 'errors'}, {}), '(errors)', False, 'import numpy\n'), ((70, 9, 70, 44), 're.findall', 're.findall', ({(70, 20, 70, 35): '"""id_[0-9][0-9]"""', (70, 37, 70, 43): 'ext_id'}, {}), "('id_[0-9][0-9]', ext_id)", False, 'import re\n'), ((217, 47, 217, 74), 'os.path.basename', 'os.path.basename', ({(217, 64, 217, 73): 'file_path'}, {}), '(file_path)', False, 'import os\n')]