/*!
 * @file libharkio3_transferfunction.c
 *
 * @author Takeshi Mizumoto
 * @version 0.1
 * @brief  harkio_TransferFunction の API 群。
 * 内部に harkio_Config, harkio_Neighbors, harkio_Positions などを持ち、1つで伝達関数の読み込み・書き込みなどを受け持つ。
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>

#include "libharkio3.h"
#include <zip.h>

char* readZippedText(struct zip *handle, char *path);
harkio_XML* readZippedXML(struct zip *handle, char *path);
// harkio_Matrix* readZippedMatrix(struct zip *handle, char *path);
int countTFs(struct zip *handle, char *mode, harkio_Positions *poses);

/*----------------------------------------------------------------------------*/
/* Generation                                                                 */
/*----------------------------------------------------------------------------*/

/*!
 * @brief コンストラクタ。空の伝達関数構造体 harkio_TransferFunctionを作成する。
 * @return harkio_TransferFunction 型のポインタ。失敗すると NULL を返す。
 * @details 使用が終わったら、 harkio_TransferFunction_delete でメモリ解放を行う。
 */
harkio_TransferFunction* harkio_TransferFunction_new(void) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_TransferFunction *tf = (harkio_TransferFunction*) calloc(sizeof(harkio_TransferFunction), 1);

    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
        return NULL;
    }

    tf->poses = harkio_Positions_new(NULL, 0, 0, NULL);
    tf->mics = harkio_Positions_new(NULL, 0, 0, NULL);
    tf->cfg = harkio_Config_new();
    tf->nbrs = harkio_Neighbors_new(NULL);

    return tf;
}

/*!
 * @brief コンストラクタ。与えられた 音源位置群、マイク位置群、コンフィグ、隣接音源位置から構造体を生成する。
 * @param[in] poses  　　　音源位置群。伝達関数測定位置のセットを表す。
 * @param[in] mics   　　　マイク位置群。マイクアレイの各マイクの位置を表す。
 * @param[in] cfg    　　　コンフィグ。伝達関数ファイルの設定を表す。
 * @param[in] nbrs　 　　　近接音源位置。poses の隣接関係を表す。
 * @return harkio_TransferFunction 型のポインタ。失敗すると NULL を返す。
 * @details 使用が終わったら、 harkio_TransferFunction_delete でメモリ解放を行う。
 */
harkio_TransferFunction* harkio_TransferFunction_fromData(harkio_Positions *poses, harkio_Positions *mics,
        harkio_Config *cfg, harkio_Neighbors *nbrs) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    harkio_TransferFunction *tf;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (poses == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Positions is NULL");
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    tf = (harkio_TransferFunction*) calloc(sizeof(harkio_TransferFunction), 1);

    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
        return NULL;
    }

    tf->poses = poses;
    tf->mics = mics;
    tf->cfg = cfg;
    tf->nbrs = nbrs;

    return tf;
}

/*!
 * @brief コンストラクタ。与えられたファイルをロードし、構造体を構成する。
 * @param[in] path 　　　読み込みたいファイルのパス。
 * @param[in] signature TransferFunction の シグネチャ。 NULL の場合はデフォルトの "transfer function" と解釈される。
 *            この値と、開く zip ファイルの whatisthis.txt を比較し、異なれば NULL を返す。
 * @return harkio_TransferFunction 型のポインタ。失敗すると NULL を返す。
 * @details 使用が終わったら、 harkio_TransferFunction_delete でメモリ解放を行う。
 */
harkio_TransferFunction* harkio_TransferFunction_fromFile(char *path, char *signature) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);
    char *sign = "transfer function";

    int numLocTFs = 0;
    int numSepTFs = 0;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (path == NULL) {
        harkio_Log_printf(harkio_Log_Error, "path is NULL");
        return NULL;
    }

    if (signature != NULL) {
        sign = signature;
    }
    harkio_Log_printf(harkio_Log_Debug, "whatisthis: %s", sign);

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    // harkio_TransferFunction *tf = (harkio_TransferFunction*) calloc(sizeof(harkio_TransferFunction), 1);
    harkio_TransferFunction *tf = harkio_TransferFunction_new();

    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
        harkio_TransferFunction_delete(&tf);
        return NULL;
    }

    harkio_XML *xml;
    int errorp;
    int i;
    char *buffer;

    if ((tf->handle = zip_open(path, ZIP_CHECKCONS, &errorp)) == NULL) {
        if (errorp == ZIP_ER_OPEN) { // NO FILE
            harkio_Log_printf(harkio_Log_Error, "zip_open failed: cannot find or open '%s'", path);
            return NULL;

        } else if (errorp == ZIP_ER_NOZIP) { // NOT IN ZIP FORMAT
            harkio_Log_printf(harkio_Log_Error, "zip_open failed: file '%s' is not in zip format", path);

            // COMPAT HARK FILE CHECK
            FILE* fpHARK = NULL;
            if ((fpHARK = fopen(path, "rb")) != NULL) {
                char head[4] = { '\0' };
                int fread_ret = fread(head, 1, 4, fpHARK);
                fclose(fpHARK);
                if (fread_ret == 4 && strncmp(head, "HARK", 4) == 0) {
                    harkio_Log_printf(harkio_Log_Error, "\n"
                            "   If you want to convert your binary-format TFs into a zip-format (HARK>=2.1),\n"
                            "   see 'harktoolcli-conv-tf --help' (after installing harktool5).\n");
                    harkio_TransferFunction_delete(&tf);
                    return NULL;
                }
            }

        } else {
            harkio_Log_printf(harkio_Log_Error, "zip_open failed: file '%s' unexpected error", path);
        }
        harkio_TransferFunction_delete(&tf);
        return NULL;
    }

    // if transferFunction/whatisthis.txt != transfer function, return NULL
    buffer = readZippedText(tf->handle, "transferFunction/whatisthis.txt");
    if (buffer == NULL) {
        harkio_Log_printf(harkio_Log_Error, "readZippedText failed");
        harkio_TransferFunction_delete(&tf);
        return NULL;
    }
    harkio_Log_printf(harkio_Log_Debug, buffer);

    if (strncmp(buffer, sign, strlen(sign)) != 0) {
        char tmp[4192];
        sprintf(tmp, "[transferFunction/whatisthis.txt] is not [%s]", sign);
        harkio_Log_printf(harkio_Log_Warning, tmp);
        harkio_TransferFunction_delete(&tf);
        free(buffer);
        return NULL;
    }

    free(buffer);

    // read sources.xml
    harkio_Log_printf(harkio_Log_Debug, "reading sources.xml");
    xml = readZippedXML(tf->handle, "transferFunction/source.xml");
    if (xml == NULL) {
        harkio_Log_printf(harkio_Log_Error, "source.xml is not found in the file");
        harkio_TransferFunction_delete(&tf);
        return NULL;
    }
    if (xml->poses) {
        harkio_Log_printf(harkio_Log_Debug, "copying xml->poses");
        // tf->poses = harkio_Positions_copy(xml->poses);
        harkio_TransferFunction_setPositions(tf, harkio_Positions_copy(xml->poses));
    }

    if (xml->cfg) {
        harkio_Log_printf(harkio_Log_Debug, "copying xml->cfg");
        // tf->cfg   = harkio_Config_copy(xml->cfg);
        harkio_TransferFunction_setConfig(tf, harkio_Config_copy(xml->cfg));
    }

    if (xml->nbrs) {
        harkio_Log_printf(harkio_Log_Debug, "copying xml->nbrs");
        // tf->nbrs  = harkio_Neighbors_copy(xml->nbrs);
        harkio_TransferFunction_setNeighbors(tf, harkio_Neighbors_copy(xml->nbrs));
    }

    harkio_Log_printf(harkio_Log_Debug, "reading sources.xml");
    if (tf->nbrs != NULL)
        tf->nbrs->poses = tf->poses;
    harkio_XML_delete(&xml);

    // read microphones.xml
    harkio_Log_printf(harkio_Log_Debug, "reading microphones.xml");
    xml = readZippedXML(tf->handle, "transferFunction/microphones.xml");
    if (xml) {
        harkio_TransferFunction_setMicrophones(tf, harkio_Positions_copy(xml->poses));
        // tf->mics = harkio_Positions_copy(xml->poses);
    } else {
        tf->mics = NULL;
    }

    harkio_XML_delete(&xml);

    // read localization/separation tfs
    harkio_Log_printf(harkio_Log_Debug, "reading loc/sep tfs");
    tf->loctfs = tf->septfs = NULL;
    numLocTFs = countTFs(tf->handle, "localization", tf->poses);
    numSepTFs = countTFs(tf->handle, "separation", tf->poses);

    if (numLocTFs < 0) {
        harkio_Log_printf(harkio_Log_Error, "Reading localization TFs failed.");
        return NULL;
    }

    if (numSepTFs < 0) {
        harkio_Log_printf(harkio_Log_Error, "Reading separation TFs failed.");
        return NULL;
    }

    if (numLocTFs > 0) {
        //tf->loctfs = (harkio_Matrix **) malloc(sizeof(harkio_Matrix *) * tf->poses->size);
        harkio_TransferFunction_setLocTFs(tf, NULL);
    }
    if (numSepTFs > 0) {
        // tf->septfs = (harkio_Matrix **) malloc(sizeof(harkio_Matrix *) * tf->poses->size);
        harkio_TransferFunction_setSepTFs(tf, NULL);
    }

    for (i = 0; i < tf->poses->size; i++) {
        char tmppath[4192];

        if (numLocTFs > 0) {
            sprintf(tmppath, "transferFunction/localization/tf%05d.mat", tf->poses->pos[i]->id);
            // tf->loctfs[i] = harkio_Matrix_fromZip(tf->handle, tmppath);
            harkio_Matrix* mat = harkio_Matrix_fromZip(tf->handle, tmppath);
            harkio_TransferFunction_appendLocTF(tf, mat);
        }
        if (numSepTFs > 0) {
            sprintf(tmppath, "transferFunction/separation/tf%05d.mat", tf->poses->pos[i]->id);
            // tf->septfs[i] = harkio_Matrix_fromZip(tf->handle, tmppath);
            harkio_Matrix* mat = harkio_Matrix_fromZip(tf->handle, tmppath);
            harkio_TransferFunction_appendSepTF(tf, mat);
        }
    }

    return tf;
}

/*!
 * @brief 内部用関数。
 * @param[in] handle   zip ファイルのハンドル
 * @param[in] mode     伝達関数ファイルのうち matrix の数を数える対象。 "localization" あるいは "separation" のみ。
 * @param[in] poses    伝達関数ファイルの poses へのポインタ (harkio_.
 * @return int         伝達関数ファイルが含む matrix ファイルの数。読み込みに失敗すれば -1.
 */
int countTFs(struct zip *handle, char *mode, harkio_Positions *poses) {
    if (handle == NULL) {
        harkio_Log_printf(harkio_Log_Error, "zip file hanel is NULL");
        return -1;
    }

    harkio_Log_printf(harkio_Log_Debug, "size: %d", poses->size);
    if (poses->size < 0) {
        harkio_Log_printf(harkio_Log_Error, "maximum TF index is not positive value");
        return -1;
    }

    if (strncmp(mode, "localization", 12) == 0) {
        //intmode = MODE_LOC;
    } else if (strncmp(mode, "separation", 10) == 0) {
        //intmode = MODE_SEP;
    } else {
        harkio_Log_printf(harkio_Log_Error, "Unknown mode: %s", mode);
        return -1;
    }

    int i;
    for (i = 0; i < poses->size; i++) {
        char tmppath[4192];
        sprintf(tmppath, "transferFunction/%s/tf%05d.mat", mode, poses->pos[i]->id);
        harkio_Log_printf(harkio_Log_Debug, tmppath);
//        harkio_Matrix* m;
//        if ((m = harkio_Matrix_fromZip(handle, tmppath)) == NULL) {
//            break;
//        } else {
//            harkio_Matrix_delete(&m);
//        }
        if (zip_name_locate(handle, tmppath, 0) == -1)
            break;
    }
    return i;
}

/*!
 * @brief デストラクタ。メンバになっている構造体を含めて全てのメモリを開放する。
 * @param[in] tf     開放したい伝達関数。解放後は NULL が代入される。
 */
void harkio_TransferFunction_delete(harkio_TransferFunction **tf) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL || *tf == NULL) {
        return;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    if ((*tf)->poses) {
        int i;
        if ((*tf)->loctfs != NULL) {
            // char tmpb[512]; sprintf(tmpb, "tf->poses = %d %d", (*tf)->loctfs ? 1 : 0, (*tf)->poses->size); harkio_Log_printf(harkio_Log_Debug, tmpb);
            for (i = 0; i < (*tf)->poses->size; i++) {
                harkio_Matrix_delete(&((*tf)->loctfs[i]));
            }
            if ((*tf)->internal_locsize > 0) {
                free((*tf)->loctfs);
            }
        }

        if ((*tf)->septfs != NULL) {
            for (i = 0; i < (*tf)->poses->size; i++) {
                harkio_Matrix_delete(&((*tf)->septfs[i]));
            }
            if ((*tf)->internal_sepsize > 0) {
                free((*tf)->septfs);
            }
        }

        harkio_Positions_delete(&((*tf)->poses));
    }

    if ((*tf)->handle) {
        zip_close((*tf)->handle);
        (*tf)->handle = NULL;
    }

    if ((*tf)->cfg) {
        harkio_Config_delete(&((*tf)->cfg));
    }

    if ((*tf)->mics) {
        harkio_Positions_delete(&((*tf)->mics));
    }

    if ((*tf)->nbrs) {
        harkio_Neighbors_delete(&((*tf)->nbrs));
    }

    free(*tf);
    *tf = NULL;
}

/*----------------------------------------------------------------------------*/
/* accessor                                                                   */
/*----------------------------------------------------------------------------*/

/*!
 * @brief Getter。harkio_TransferFunction の harkio_Config を返す
 * @param[in] tf   伝達関数構造体 harkio_TransferFunction。
 * @return harkio_Config メンバへのポインタ。 アクセスに失敗すると NULL を返す。
 */
harkio_Config* harkio_TransferFunction_getConfig(harkio_TransferFunction *tf) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    return tf->cfg;
}

/*!
 * @brief Getter。harkio_TransferFunction の harkio_Neighbor を返す
 * @param[in] tf   伝達関数構造体 harkio_TransferFunction。
 * @return harkio_Neighbor メンバへのポインタ。 アクセスに失敗すると NULL を返す。
 */
harkio_Neighbors* harkio_TransferFunction_getNeighbors(harkio_TransferFunction *tf) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    return tf->nbrs;
}

/*!
 * @brief Getter。harkio_TransferFunction の マイク位置を表す harkio_Positions を返す
 * @param[in] tf   伝達関数構造体 harkio_TransferFunction。
 * @return マイク位置を表す harkio_Positions メンバへのポインタ。 アクセスに失敗すると NULL を返す。
 */
harkio_Positions* harkio_TransferFunction_getMicrophones(harkio_TransferFunction *tf) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    return tf->mics;
}

/*!
 * @brief Getter。harkio_TransferFunction の 音源位置を表す harkio_Positions を返す
 * @param[in] tf   伝達関数構造体 harkio_TransferFunction。
 * @return 音源位置を表す harkio_Positions メンバへのポインタ。 アクセスに失敗すると NULL を返す。
 */
harkio_Positions* harkio_TransferFunction_getPositions(harkio_TransferFunction *tf) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    return tf->poses;
}

/*!
 * @brief Setter。harkio_TransferFunction の harkio_Config を設定する
 * @param[in,out] tf   伝達関数構造体 harkio_TransferFunction。
 * @param[in] value   更新したい harkio_Config へのポインタ
 * @return 成功すると EXIT_SUCCESS, 失敗すると EXIT_FAILURE を返す。
 */
int harkio_TransferFunction_setConfig(harkio_TransferFunction *tf, harkio_Config *value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return EXIT_FAILURE;
    }

    if (value == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Config is NULL");
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    if (tf->cfg != NULL) {
        harkio_Config_delete(&tf->cfg);
    }
    tf->cfg = value;
    return EXIT_SUCCESS;
}

/*!
 * @brief Setter。harkio_TransferFunction の harkio_Neighbors を設定する
 * @param[in,out] tf   伝達関数構造体 harkio_TransferFunction。
 * @param[in] value   更新したい harkio_Neighbors へのポインタ
 * @return 成功すると EXIT_SUCCESS, 失敗すると EXIT_FAILURE を返す。
 */int harkio_TransferFunction_setNeighbors(harkio_TransferFunction *tf, harkio_Neighbors *value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return EXIT_FAILURE;
    }

    if (value == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Neighbors is NULL");
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    if (tf->nbrs != NULL) {
        harkio_Neighbors_delete(&tf->nbrs);
    }
    tf->nbrs = value;
    return EXIT_SUCCESS;
}

/*!
 * @brief Setter。harkio_TransferFunction のマイク位置に対応する harkio_Positions を設定する
 * @param[in,out] tf   伝達関数構造体 harkio_TransferFunction。
 * @param[in] value   更新したい マイク位置に対応する　harkio_Positions へのポインタ
 * @return 成功すると EXIT_SUCCESS, 失敗すると EXIT_FAILURE を返す。
 */
int harkio_TransferFunction_setMicrophones(harkio_TransferFunction *tf, harkio_Positions *value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return EXIT_FAILURE;
    }

    if (value == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Positions is NULL");
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    if (tf->mics != NULL) {
        harkio_Positions_delete(&tf->mics);
    }
    tf->mics = value;
    return EXIT_SUCCESS;
}

/*!
 * @brief Setter。harkio_TransferFunction の音源位置に対応する harkio_Positions を設定する
 * @param[in,out] tf   伝達関数構造体 harkio_TransferFunction。
 * @param[in] value   更新したい 音源位置に対応する　harkio_Positions へのポインタ
 * @return 成功すると EXIT_SUCCESS, 失敗すると EXIT_FAILURE を返す。
 */
int harkio_TransferFunction_setPositions(harkio_TransferFunction *tf, harkio_Positions *value) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return EXIT_FAILURE;
    }

    if (value == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Positions is NULL");
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    if (tf->poses != NULL) {
        harkio_Positions_delete(&tf->poses);
    }
    tf->poses = value;
    return EXIT_SUCCESS;
}

/*!
 * @brief Setter。harkio_TransferFunction の音源位置ごとの定位伝達関数を表す harkio_Matrix 配列へのポインタを設定する
 * @param[in,out] tf   伝達関数構造体 harkio_TransferFunction。
 * @param[in] value   更新したい 音源位置ごとの定位伝達関数を表す harkio_Matrix 配列へのポインタ (NULLの場合は関数内部で動的確保)
 * @return 成功すると EXIT_SUCCESS, 失敗すると EXIT_FAILURE を返す。
 */
int harkio_TransferFunction_setLocTFs(harkio_TransferFunction *tf, harkio_Matrix **tfs) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return EXIT_FAILURE;
    }

    if (tfs != NULL && *tfs == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Matrix is NULL");
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    if (tfs == NULL) {
        tf->loctfs = NULL; // = malloc(0 * sizeof(harkio_Matrix*));
        tf->internal_locsize = -1;
    } else {
        tf->loctfs = tfs;
        tf->internal_locsize = 0;
    }
    return EXIT_SUCCESS;
}

/*!
 * @brief Setter。harkio_TransferFunction の音源位置ごとの分離伝達関数を表す harkio_Matrix 配列へのポインタを設定する
 * @param[in,out] tf   伝達関数構造体 harkio_TransferFunction。
 * @param[in] value   更新したい 音源位置ごとの分離伝達関数を表す harkio_Matrix 配列へのポインタ (NULLの場合は関数内部で動的確保)
 * @return 成功すると EXIT_SUCCESS, 失敗すると EXIT_FAILURE を返す。
 */
int harkio_TransferFunction_setSepTFs(harkio_TransferFunction *tf, harkio_Matrix **tfs) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return EXIT_FAILURE;
    }

    if (tfs != NULL && *tfs == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Matrix is NULL");
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    if (tfs == NULL) {
        tf->septfs = NULL; // = malloc(0 * sizeof(harkio_Matrix*));
        tf->internal_sepsize = -1;
    } else {
        tf->septfs = tfs;
        tf->internal_sepsize = 0;
    }
    return EXIT_SUCCESS;
}

/*!
 * @brief harkio_TransferFunction の定位伝達関数を表す harkio_Matrix 配列に要素を追加する。
 * @param[in,out] tf   伝達関数構造体 harkio_TransferFunction。
 * @param[in] mat      音源位置の定位伝達関数を表す 追加のharkio_Matrix へのポインタ
 * @return 成功すると EXIT_SUCCESS, 失敗すると EXIT_FAILURE を返す。
 */
int harkio_TransferFunction_appendLocTF(harkio_TransferFunction *tf, harkio_Matrix *mat) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return EXIT_FAILURE;
    }

    if (mat == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Matrix is NULL");
        return EXIT_FAILURE;
    }
    if (tf->internal_locsize == 0) {
        harkio_Log_printf(harkio_Log_Error, "append not allowed in this loctfs");
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//

    if (tf->internal_locsize == -1) {
        tf->internal_locsize = 1;
    } else {
        tf->internal_locsize += 1;
    }
    // reallocate matrix array
    harkio_Matrix **newpointer = (harkio_Matrix **) realloc(tf->loctfs, sizeof(harkio_Matrix *) * tf->internal_locsize);
    if (newpointer == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Matrix* memory alloc failed");
        return EXIT_FAILURE;
    }
    // append the matrix to the last
    newpointer[tf->internal_locsize - 1] = mat;
    // overwrite a new pointer to loctfs.
    tf->loctfs = newpointer;
    return EXIT_SUCCESS;
}
/*!
 * @brief harkio_TransferFunction の分離伝達関数を表す harkio_Matrix 配列に要素を追加する。
 * @param[in,out] tf   伝達関数構造体 harkio_TransferFunction。
 * @param[in] mat      分離伝達関数を表す 追加のharkio_Matrix へのポインタ
 * @return 成功すると EXIT_SUCCESS, 失敗すると EXIT_FAILURE を返す。
 */
int harkio_TransferFunction_appendSepTF(harkio_TransferFunction *tf, harkio_Matrix *mat) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return EXIT_FAILURE;
    }

    if (mat == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Matrix is NULL");
        return EXIT_FAILURE;
    }
    if (tf->internal_sepsize == 0) {
        harkio_Log_printf(harkio_Log_Error, "append not allowed in this septfs");
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    if (tf->internal_sepsize == -1) {
        tf->internal_sepsize = 1;
    } else {
        tf->internal_sepsize += 1;
    }
    // reallocate matrix array
    harkio_Matrix **newpointer = (harkio_Matrix **) realloc(tf->septfs, sizeof(harkio_Matrix *) * tf->internal_sepsize);
    if (newpointer == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Matrix* memory alloc failed");
        return EXIT_FAILURE;
    }
    // append the matrix to the last
    newpointer[tf->internal_sepsize - 1] = mat;
    // overwrite a new pointer to septfs.
    tf->septfs = newpointer;
    return EXIT_SUCCESS;
}

/*----------------------------------------------------------------------------*/
/* Operation                                                                  */
/*----------------------------------------------------------------------------*/

/*!
 * @brief harkio_TransferFunction 構造体の情報をファイルに書き込む
 * @param[in] tf   伝達関数構造体 harkio_TransferFunction。
 * @param[in] path 保存したいファイルへのパス
 * @param[in] signature TransferFunction の シグネチャ。 NULL の場合はデフォルトの "transfer function" が保存される。
 *            この値は whatisthis.txt に書き込まれる。
 * @return 成功すると EXIT_SUCCESS, 失敗すると EXIT_FAILURE を返す。NULL が代入されているメンバはスキップして保存される。
 */
int harkio_TransferFunction_write(harkio_TransferFunction *tf, char *path, char *signature) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    char *root = "transferFunction";
    char *loc = "transferFunction/localization";
    char *sep = "transferFunction/separation";
    char *sign = "transfer function";

    harkio_XML *xml;

    int errorp;
    struct zip_source *src;

    int i;
    char *buffer;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "TF is null");
        return EXIT_FAILURE;
    }

    if (path == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction_write: File path to write is NULL");
        return EXIT_FAILURE;
    }
    unlink(path);
    if ((tf->handle = zip_open(path, ZIP_CREATE, &errorp)) == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction_write error: %s, %d", path, errorp);
        return EXIT_FAILURE;
    }

    if (signature != NULL) {
        sign = signature;
    }

    // make directory
    zip_add_dir(tf->handle, root);
    harkio_Log_printf(harkio_Log_Debug, "TF Directory created");

    // whatisthis.txt -> transferfunction
    char tmp[4192];
    sprintf(tmp, "%s/whatisthis.txt", root);
    src = zip_source_buffer(tf->handle, sign, strlen(sign), 0);

    if (src == NULL) {
        harkio_Log_printf(harkio_Log_Error, "zip_source_buffer is failed");
        return EXIT_FAILURE;
    }

    zip_add(tf->handle, tmp, src);

    //mic.xml    mics
    harkio_Log_printf(harkio_Log_Debug, "making mic.xml");
    xml = harkio_XML_new();
    xml->poses = harkio_TransferFunction_getMicrophones(tf);
    sprintf(tmp, "%s/microphones.xml", root);
    buffer = harkio_XML_writeBuffer(xml);
    src = zip_source_buffer(tf->handle, buffer, strlen(buffer), 1);
    // harkio_Log_printf(harkio_Log_Debug, "%s\n", buffer);
    zip_add(tf->handle, tmp, src);
    xml->poses = NULL;

    free(xml);
    //    harkio_XML_delete( &xml );

    //source.xml poses
    harkio_Log_printf(harkio_Log_Debug, "making source.xml");
    xml = harkio_XML_newFromData(harkio_TransferFunction_getPositions(tf), harkio_TransferFunction_getNeighbors(tf),
            harkio_TransferFunction_getConfig(tf));

    sprintf(tmp, "%s/source.xml", root);
    buffer = harkio_XML_writeBuffer(xml);
    src = zip_source_buffer(tf->handle, buffer, strlen(buffer), 1);

    if (src == NULL) {
        harkio_Log_printf(harkio_Log_Error, "zip_source_buffer is failed");
        return EXIT_FAILURE;
    }

    zip_add(tf->handle, tmp, src);

    //localization
    harkio_Log_printf(harkio_Log_Debug, "making tf matrices");
    zip_add_dir(tf->handle, loc);
    zip_add_dir(tf->handle, sep);

    harkio_Log_printf(harkio_Log_Debug, "xml->poses->size for localization: %d", xml->poses->size);
    for (i = 0; i < xml->poses->size; i++) {
        size_t size;
        harkio_Log_printf(harkio_Log_Info, "Making TF %d\n", i);

        if (tf->loctfs != NULL) {
            char tmp[1024];
            sprintf(tmp, "%s/tf%05d.mat", loc, xml->poses->pos[i]->id);
            buffer = harkio_Matrix_writeBuffer(tf->loctfs[i], &size);
            src = zip_source_buffer(tf->handle, buffer, size, 1);
            zip_add(tf->handle, tmp, src);
        }

        if (tf->septfs != NULL) {
            sprintf(tmp, "%s/tf%05d.mat", sep, xml->poses->pos[i]->id);
            buffer = harkio_Matrix_writeBuffer(tf->septfs[i], &size);
            src = zip_source_buffer(tf->handle, buffer, size, 1);
            zip_add(tf->handle, tmp, src);
        }

    }
    zip_close(tf->handle);
    tf->handle = NULL;

    free(xml);

//    harkio_XML_delete( &xml );

//	harkio_TransferFunction_delete( &tf );

    return EXIT_SUCCESS;
}

/*----------------------------------------------------------------------------*/
/* Utility                                                                    */
/*----------------------------------------------------------------------------*/

/*!
 * @brief harkio_TransferFunction 構造体に含まれる伝達関数群から特定の伝達関数を取り出す
 * @param[in] tf   伝達関数構造体 harkio_TransferFunction
 * @param[in] pos  取り出したい伝達関数の位置
 * @param[in] tolerance 位置の許容誤差
 * @param[in] tf_type   取り出すのが定位伝達関数か分離伝達関数かを選択
 * @return 成功すると 対応する harkio_Matrix ポインタ、失敗すると NULL を返す。
 * @details harkio_TransferFunction から、ある位置の伝達関数を取り出したい場合に使用する。
 pos と距離が tolerance より小さい最初の位置に対応する伝達関数を返す。
 返す伝達関数が定位か分離かはtf_type で与えられる。
 */
harkio_Matrix* harkio_TransferFunction_getTFByPos(harkio_TransferFunction *tf, harkio_Position *pos, float tolerance,
        enum harkio_TFType tf_type) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i;
    harkio_Position *p;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return NULL;
    }

    if (pos == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Position is NULL");
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//

    //coord -> position
    p = harkio_Positions_getByCoord(tf->poses, pos->sys, pos->coord, tolerance);

    //position id check
    for (i = 0; i < tf->poses->size; i++) {
        if (tf->poses->pos[i]->id == p->id) {
            switch (tf_type) {
            case loctf: //localization
                return tf->loctfs[i];

            case septf: //separation
                return tf->septfs[i];
            }
        }
    }

    return NULL;

}

/*!
 * @brief harkio_TransferFunction 構造体に含まれる伝達関数群から特定の伝達関数を取り出す
 * @param[in] tf  伝達関数構造体 harkio_TransferFunction
 * @param[in] id  取り出したい伝達関数の ID
 * @param[in] tf_type   取り出すのが定位伝達関数か分離伝達関数かを選択
 * @return 成功すると 対応する harkio_Matrix ポインタ、失敗すると NULL を返す。
 * @details harkio_TransferFunction から、あるIDの伝達関数を取り出したい場合に使用する。
 対応する ID の伝達関数を返す。
 返す伝達関数が定位か分離かはtf_type で与えられる。
 */
harkio_Matrix* harkio_TransferFunction_getTFById(harkio_TransferFunction *tf, int id, enum harkio_TFType tf_type) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction is NULL");
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    //position id check
    for (i = 0; i < tf->poses->size; i++) {
        if (tf->poses->pos[i]->id == id) {
            switch (tf_type) {
            case loctf: //localization
                return tf->loctfs[i];

            case septf: //separation
                return tf->septfs[i];
            }
        }
    }

    return NULL;

}

/*----------------------------------------------------------*/
/*  Internal functions                                      */
/*----------------------------------------------------------*/
/*!
 * @brief 内部用関数。 zip ファイルからXMLを読み出す
 * @param[in] handle 　　zip ファイルへのハンドル
 * @param[in] path 　　　開きたい zip 内 xml ファイルのパス
 * @return harkio_XML 型のポインタ。失敗すると NULL を返す。
 */
harkio_XML* readZippedXML(struct zip *handle, char *path) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    char *buffer;
    struct zip_file *fp;
    struct zip_stat zs;
    harkio_XML *xml;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (path == NULL) {
        harkio_Log_printf(harkio_Log_Error, "path is NULL");
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    if ((fp = zip_fopen(handle, path, 0)) == NULL) {
        harkio_Log_FileOpenFailed(path);
        return NULL;
    }
    zip_stat_init(&zs);
    zip_stat(handle, path, ZIP_STAT_SIZE, &zs);

    buffer = (char *) malloc(zs.size + 1);
    zip_fread(fp, buffer, zs.size);
    buffer[zs.size] = '\0';

    xml = harkio_XML_newFromXMLbuffer(buffer, zs.size, NULL);

    free(buffer);
    zip_fclose(fp);

    return xml;
}

/*!
 * @brief 内部用関数。 zip ファイルからテキストファイルを読み出す
 * @param[in] handle  zip ファイルへのハンドル
 * @param[in] path    開きたい zip 内 テキストファイルのパス
 * @return char       型のポインタ。失敗すると NULL を返す。
 */
char* readZippedText(struct zip *handle, char *path) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    char *buffer;
    struct zip_file *fp;
    struct zip_stat zs;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (path == NULL) {
        harkio_Log_printf(harkio_Log_Error, "path is NULL");
        return NULL;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    if ((fp = zip_fopen(handle, path, 0)) == NULL) {
        harkio_Log_FileOpenFailed(path);
        return NULL;
    }
    zip_stat_init(&zs);
    zip_stat(handle, path, ZIP_STAT_SIZE, &zs);
    buffer = (char *) malloc(zs.size + 1);
    zip_fread(fp, buffer, zs.size);
    buffer[zs.size] = '\0';

    zip_fclose(fp);

    return buffer;

}
