/*!
 * @file libharkio3_conversion.c
 *
 * @version 0.1
 * @brief  後方互換性用のAPI群。 libharkio2 で生成されたファイルを libharkio3 形式に変換する。
 */

#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>
#include <float.h>

/*! @defgroup Compatibility
 *  @brief libharkio2 のファイルを libharkio3 へ変換するための構造体
 */

/*!
 *  @ingroup Compatibility
 *  @brief libharkio2 で座標を表す構造体。
 */
typedef struct {
    float x; /*!< X座標 */
    float y; /*!< Y座標 */
    float z; /*!< Z座標 */
} coord_t;

/*!
 *  @ingroup Compatibility
 *  @brief libharkio2 でヘッダ情報を表す構造体
 */
typedef struct {
    char id[4]; /*!< ファイルタイプを表す文字列 (HGTF など)*/
    int size; /*!< ファイルサイズ */
    char type[4]; /*!< ファイルタイプ */
} header_t;

/*!
 *  @ingroup Compatibility
 *  @brief libharkio2 でヘッダ情報を表す構造体
 */
typedef struct {
    char ident[4]; /*!<  */
    int size; /*!<  */
    short ver[2]; /*!<  */
    int formatID; /*!<  */
    int Nmic; /*!<  */
    int Nfft; /*!<  */
    int Nir; /*!<  */
    int samplingRate; /*!< サンプリングレート */
    coord_t *mic; /*!< マイク位置座標 */
} fmt_chunk;

/*!
 *  @ingroup Compatibility
 *  @brief libharkio2 の M2PG (定位用伝達関数) を表す構造体
 */
typedef struct {
    char id[4]; /*!< */
    int size; /*!< */
    short ver[2]; /*!< */
    int Nd; /*!< */
    int Nr; /*!< */
    coord_t *src; /*! 音源位置座標 */
    COMPLEX_TYPE *tf; /*!< */
} m2pg_chunk;

/*!
 *  @ingroup Compatibility
 *  @brief libharkio2 の GTF (分離用伝達関数) を表す構造体
 */
typedef struct {
    char id[4]; /*!< */
    int size; /*!< */
    short ver[2]; /*!< */
    int Ns; /*!< */
    coord_t *src; /*! 音源位置座標 */
    int Nf; /*!< */
    int *nfmap; /*!< */
    COMPLEX_TYPE *tf; /*!< */
} gtf_chunk;

/*!
 *  @ingroup Compatibility
 *  @brief libharkio2 の全伝達関数を表す構造体
 */
typedef struct {
    int type; //0:non 1:m2pg 2:gtf 3:m2pg+gtf 4:sm
    FILE *m2pgfp;
    FILE *gtffp;
    char *locpath;
    char *seppath;
    header_t header; //header
    fmt_chunk fmt; //fmt chunk
    m2pg_chunk m2pg; //m2pg chunk
    gtf_chunk gtf; //gtf  chunk (OR sm chunk)

    harkio_Position **src_p;
    harkio_Positions *src_ps;
    harkio_Position **mic_p;
    harkio_Positions *mic_ps;
    harkio_Matrix **loctf_p;
    harkio_Matrix **septf_p;
    harkio_Neighbors *nbrs;
    harkio_Config *cfg;
} param_t;

#define FMT_LEN     32 //fmt_chunk  size :sizeof(ident[4]+size+ver[2]+formatID+Nmic+Nfft+Nir+samplingRate)
#define M2PG_LEN    20 //M2PG_chunk size :sizeof(id+size+ver+Nd+Nr)
#define GTF_LEN     16 //GTF_CHUNK  size :sizeof(id[4]+size+ver[2]+Ns)
#define HEADER_LEN  12 //header_t   size :sizeof(id[4]+size+type[4])
#define COORD_LEN   12 //coord_t    size :sizeof(coord_t)

int sub_SetHeader(param_t *param);
int sub_SetLocalization(param_t *param);
int sub_SetSeparation(param_t *param);
int sub_SetSrcPositions(param_t *param);

char* readZippedText(struct zip *handle, char *path);
harkio_XML* readZippedXML(struct zip *handle, char *path);
void freeParamTF(param_t *m, harkio_TransferFunction **tf);
// harkio_Matrix* readZippedMatrix(struct zip *handle, char *path);

int sub_SetSrcPositions_byPositionsId(param_t *param, harkio_Positions *poses);
int convertFile_readlibharkio2(harkio_TransferFunction **arg_tf, char *loctfpath, char *septfpath, char* septfzippath);

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

/*!
 * @brief コンストラクタ。harkio2フォーマットの伝達関数ファイルを読み込み、構造体を構成する。
 * @param[in] loctfpath     読み込む定位伝達関数ファイルのパス。
 * @param[in] septfpath     読み込む分離伝達関数ファイルのパス。
 * @return harkio_TransferFunction 型のポインタ。失敗すると NULL を返す。
 * @details 使用が終わったら、 harkio_TransferFunction_delete でメモリ解放を行う。
 */
harkio_TransferFunction* harkio_Compat_TransferFunction_readlibharkio2(char *loctfpath, char *septfpath) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    harkio_TransferFunction* tf = NULL;
    int ret = convertFile_readlibharkio2(&tf, loctfpath, septfpath, NULL);
    return (ret == EXIT_SUCCESS) ? tf : NULL;
}

/*!
 * @brief コンストラクタ。harkio2フォーマットの分離行列ファイルを読み込み、構造体を構成する。
 * @param[in] sepmatpath     読み込む分離行列ファイルのパス。
 * @param[in] septfzippath   対応する伝達関数(zip)ファイルのパス。
 * @return harkio_TransferFunction 型のポインタ。失敗すると NULL を返す。
 * @details 使用が終わったら、 harkio_TransferFunction_delete でメモリ解放を行う。
 */
harkio_TransferFunction* harkio_Compat_SeparationMatrix_readlibharkio2(char *sepmatpath, char *septfzippath) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    /* 引数チェック */
    if (septfzippath == NULL || sepmatpath == NULL) {
        harkio_Log_printf(harkio_Log_Error, "argument error");
        return NULL;
    }

    harkio_TransferFunction* tf = NULL;
    int ret = convertFile_readlibharkio2(&tf, NULL, sepmatpath, septfzippath);
    return (ret == EXIT_SUCCESS) ? tf : NULL;
}

/*!
 * @brief harkio2フォーマットの伝達関数ファイルを読み込み、構造体を構成する。(deprecated)
 */
int libharkio3_TransferFunction_readlibhakio2(harkio_TransferFunction **arg_tf, char *loctfpath, char *septfpath) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);
    harkio_Log_printf(harkio_Log_Warning, "  (use harkio_Compat_TransferFunction_readlibharkio2 instead)");

    return convertFile_readlibharkio2(arg_tf, loctfpath, septfpath, NULL);
}

/*!
 * @brief harkio2フォーマットの分離行列ファイルを読み込み、構造体を構成する。(deprecated)
 */
int libharkio3_SeparationMatrix_readlibhakio2(harkio_TransferFunction **arg_tf, char *sepmatpath, char *septfzippath) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);
    harkio_Log_printf(harkio_Log_Warning, "  (use harkio_Compat_SeparationMatrix_readlibharkio2 instead)");

    /* 引数チェック */
    if (septfzippath == NULL || sepmatpath == NULL) {
        harkio_Log_printf(harkio_Log_Error, "argument error");
        return (EXIT_FAILURE);
    }

    return convertFile_readlibharkio2(arg_tf, NULL, sepmatpath, septfzippath);
}

/*----------------------------------------------------------*/
/*  libhark2->libhark3                                      */
/*----------------------------------------------------------*/
int convertFile_readlibharkio2(harkio_TransferFunction **arg_tf, char *loctfpath, char *septfpath, char* septfzippath) {
    param_t param;
    int i;

    memset(&param.header, '\0', sizeof(param.header));
    memset(&param.fmt, '\0', sizeof(param.fmt));
    memset(&param.m2pg, '\0', sizeof(param.m2pg));
    memset(&param.gtf, '\0', sizeof(param.gtf));

    param.src_p = NULL;
    param.src_ps = NULL;
    param.mic_p = NULL;
    param.mic_ps = NULL;
    param.loctf_p = NULL;
    param.septf_p = NULL;
    param.nbrs = NULL;
    param.cfg = NULL;
    param.m2pgfp = NULL;
    param.gtffp = NULL;
    param.locpath = NULL;
    param.seppath = NULL;

    harkio_TransferFunction *tf = NULL;

    param.locpath = loctfpath;
    param.seppath = septfpath;

    if (septfzippath == NULL) {
        /* set type */
        if (loctfpath != NULL && septfpath == NULL) {
            param.type = 1;
        } else if (loctfpath == NULL && septfpath != NULL) {
            param.type = 2;
        } else if (loctfpath != NULL && septfpath != NULL) {
            param.type = 3;
        } else {
            harkio_Log_printf(harkio_Log_Error, "loctfpath & septfpath is null");
            return (EXIT_FAILURE);
        }
    } else {
        harkio_Log_printf(harkio_Log_Debug, "%s = septfpath\n", septfpath);
        if (loctfpath == NULL && septfpath != NULL) {
            param.type = 4;
        } else {
            harkio_Log_printf(harkio_Log_Error, "septfpath is null");
            return (EXIT_FAILURE);
        }
    }

    switch (param.type) {
    case 1: //1:m2pg
        if (sub_SetHeader(&param) == EXIT_FAILURE) {
            harkio_Log_printf(harkio_Log_Error, "sub_SetHeader Failed");
            freeParamTF(&param, &tf);
            return (EXIT_FAILURE);
        }
        if (sub_SetLocalization(&param) == EXIT_FAILURE) {
            harkio_Log_printf(harkio_Log_Error, "sub_SetLocalization Failed");
            freeParamTF(&param, &tf);
            return (EXIT_FAILURE);
        }
        break;

    case 2: //2:gtf
    case 4: //4:sm
        if (sub_SetHeader(&param) == EXIT_FAILURE) {
            harkio_Log_printf(harkio_Log_Error, "sub_SetHeader Failed");
            freeParamTF(&param, &tf);
            return (EXIT_FAILURE);
        }

        if (sub_SetSeparation(&param) == EXIT_FAILURE) {
            harkio_Log_printf(harkio_Log_Error, "sub_SetSeparation Failed");
            freeParamTF(&param, &tf);
            return (EXIT_FAILURE);
        }
        break;

    case 3: //3:m2pg+gtf
        if (sub_SetHeader(&param) == EXIT_FAILURE) {
            harkio_Log_printf(harkio_Log_Error, "sub_SetHeader Failed");
            freeParamTF(&param, &tf);
            return (EXIT_FAILURE);
        }

        if (sub_SetLocalization(&param) == EXIT_FAILURE) {
            harkio_Log_printf(harkio_Log_Error, "sub_SetLocalization Failed");
            freeParamTF(&param, &tf);
            return (EXIT_FAILURE);
        }

        if (sub_SetSeparation(&param) == EXIT_FAILURE) {
            harkio_Log_printf(harkio_Log_Error, "sub_SetSeparation Failed");
            freeParamTF(&param, &tf);
            return (EXIT_FAILURE);
        }
        break;

    }

    /*-----------------------------------------*/
    /* 音源位置座標をharkio_Positionsにセット  */
    /* 音源位置座標から近接IDセット            */
    /*-----------------------------------------*/
    if (param.type != 4) {
        if (sub_SetSrcPositions(&param) == EXIT_FAILURE) {
            freeParamTF(&param, &tf);
            return (EXIT_FAILURE);
        }
    } else {
        /* 入力のsource.xmlからpositionsを取得する */
        struct zip * handle = NULL;
        harkio_XML* xml = NULL;
        int errorp;
        char* sourcepath = "transferFunction/source.xml";

        if ((handle = zip_open(septfzippath, ZIP_CHECKCONS, &errorp)) == NULL) {
            harkio_Log_printf(harkio_Log_Error, "zip file open error");
            freeParamTF(&param, &tf);
            return (EXIT_FAILURE);
        }

        xml = readZippedXML(handle, sourcepath);
        if (xml == NULL) {
            harkio_Log_printf(harkio_Log_Error, "source.xml is not found in the file");
            freeParamTF(&param, &tf);
            harkio_XML_delete(&xml);
            zip_close(handle);
            handle = NULL;
            return (EXIT_FAILURE);
        }
        harkio_Positions* poses = harkio_XML_getPositions(xml);

        /* zipを読み込み、引数に与える */
        if (sub_SetSrcPositions_byPositionsId(&param, poses) == EXIT_FAILURE) {
            harkio_Log_printf(harkio_Log_Error, "sub_SetSeparation Failed");
            freeParamTF(&param, &tf);
            harkio_XML_delete(&xml);
            zip_close(handle);
            handle = NULL;
            return (EXIT_FAILURE);
        }

        // zip のクローズ処理
        harkio_XML_delete(&xml);
        zip_close(handle);
        handle = NULL;
    }

    /*-----------------------------------------*/
    /* 伝達関数Matrixにセット                  */
    /*-----------------------------------------*/
    tf = harkio_TransferFunction_fromData(param.src_ps, param.mic_ps, param.cfg, param.nbrs);
    if (tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction_fromData Failed");
        freeParamTF(&param, &tf);
        return (EXIT_FAILURE);
    }

    if (param.locpath != NULL) {
        /*-----------------------------------------*/
        /*定位伝達関数セット                       */
        /*-----------------------------------------*/
        // if( harkio_TransferFunction_setLocTFs( tf, param.loctf_p) == EXIT_FAILURE )
        if (harkio_TransferFunction_setLocTFs(tf, NULL) == EXIT_FAILURE) {
            harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction_setLocTFs Failed");
            freeParamTF(&param, &tf);
            return (EXIT_FAILURE);
        }
        for (i = 0; i < param.m2pg.Nd; i++) {
            if (harkio_TransferFunction_appendLocTF(tf, param.loctf_p[i]) == EXIT_FAILURE) {
                harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction_appendLocTF Failed");
                freeParamTF(&param, &tf);
                return (EXIT_FAILURE);
            }
        }
        free(param.loctf_p);
        param.loctf_p = NULL;

    }

    if (param.seppath != NULL) {
        /*-----------------------------------------*/
        /* 分離伝達関数セット                                */
        /*-----------------------------------------*/
        // ret = harkio_TransferFunction_setSepTFs( tf, param.septf_p);
        if (harkio_TransferFunction_setSepTFs(tf, NULL) == EXIT_FAILURE) {
            harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction_setSepTFs Failed");
            freeParamTF(&param, &tf);
            return (EXIT_FAILURE);
        }
        for (i = 0; i < param.gtf.Ns; i++) {
            if (harkio_TransferFunction_appendSepTF(tf, param.septf_p[i]) == EXIT_FAILURE) {
                harkio_Log_printf(harkio_Log_Error, "harkio_TransferFunction_appendLocTF Failed");
                freeParamTF(&param, &tf);
                return (EXIT_FAILURE);
            }
        }
        free(param.septf_p);
        param.septf_p = NULL;
    }

    if (arg_tf != NULL) {
        // 2015/08/17 add
        if (*arg_tf != NULL) {
            harkio_TransferFunction_delete(arg_tf);
        }

        *arg_tf = tf;
    }

    if (param.fmt.mic != NULL) {
        free(param.fmt.mic);
        param.fmt.mic = NULL;
    }

    freeParamTF(&param, NULL);
    return EXIT_SUCCESS;
} //end

int sub_SetHeader(param_t *param) {

    int len;
    int i;
    FILE *fp = NULL;
    float coord[3];
    int ret;

    /*-----------------------------------------*/
    /* ファイルのオープン                      */
    /*-----------------------------------------*/
    switch (param->type) {
    case 1: //1:m2pg
        if ((fp = fopen(param->locpath, "rb")) == NULL) {
            harkio_Log_FileOpenFailed(param->locpath);
            return (EXIT_FAILURE);
        }
        param->m2pgfp = fp;
        break;

    case 2: //2:gtf
    case 4: //4:sm
        if ((fp = fopen(param->seppath, "rb")) == NULL) {
            harkio_Log_FileOpenFailed(param->seppath);
            return (EXIT_FAILURE);
        }
        param->gtffp = fp;
        break;

    case 3:
        if ((fp = fopen(param->seppath, "rb")) == NULL) {
            harkio_Log_FileOpenFailed(param->seppath);
            return (EXIT_FAILURE);
        }
        param->gtffp = fp;

        if ((fp = fopen(param->locpath, "rb")) == NULL) {
            harkio_Log_FileOpenFailed(param->locpath);
            return (EXIT_FAILURE);
        }
        param->m2pgfp = fp;
        break;

    default:
        return (EXIT_FAILURE);
    }

    /*-----------------------------------------*/
    /* Header 読み込む                                  */
    /*-----------------------------------------*/
    fread(&param->header, 1, HEADER_LEN, fp);

    if (strncmp(param->header.id, "HARK", 4) != 0) {
        harkio_Log_printf(harkio_Log_Error, "Not HARK File: [%4s]", param->header.id);
        return (EXIT_FAILURE);
    }

    /*-----------------------------------------*/
    /* fmt 読み込む                            */
    /*-----------------------------------------*/
    fread(&param->fmt, 1, FMT_LEN, fp);

    /*-----------------------------------------*/
    /* Mic位置座標                             */
    /*-----------------------------------------*/
    len = param->fmt.Nmic * COORD_LEN;

    param->fmt.mic = (coord_t*) malloc(len);
    if (param->fmt.mic == NULL) {
        harkio_Log_printf(harkio_Log_Error, "malloc Failed");
        return (EXIT_FAILURE);
    }

    fread(param->fmt.mic, 1, len, fp);

    /*-----------------------------------------*/
    /* Mic位置座標をharkio_Positionsに変換     */
    /*-----------------------------------------*/
    len = param->fmt.Nmic; //要素数

    param->mic_p = (harkio_Position**) malloc(sizeof(harkio_Position*) * len);
    if (param->mic_p == NULL) {
        harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
        return (EXIT_FAILURE);
    }

    for (i = 0; i < len; i++) {
        coord[0] = param->fmt.mic[i].x;
        coord[1] = param->fmt.mic[i].y;
        coord[2] = param->fmt.mic[i].z;

        param->mic_p[i] = harkio_Position_new(i, Cartesian, coord, NULL);

        if (param->mic_p[i] == NULL) {
            harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
            return (EXIT_FAILURE);
        }
    }

    // param->mic_ps = harkio_Positions_new("microphone", 1, len, param->mic_p);
    param->mic_ps = harkio_Positions_new("microphone", -1, 0, NULL);
    if (param->mic_ps == NULL) {
        harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
        return (EXIT_FAILURE);
    }
    for (i = 0; i < len; i++) {
        if (harkio_Positions_append(param->mic_ps, param->mic_p[i]) == EXIT_FAILURE) {
            harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
            return (EXIT_FAILURE);
        }
    }
    free(param->mic_p);
    param->mic_p = NULL;

    //-----------------------------------------//
    // 設定値をharkio_Configに変換
    //-----------------------------------------//
    param->cfg = harkio_Config_new();
    if (param->cfg == NULL) {
        harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
        return (EXIT_FAILURE);
    }

    ret = harkio_Config_setComment(param->cfg, "conversion from libharkio2 to libharkio3");
    if (ret == EXIT_FAILURE) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Config_setComment Failed");
        return (EXIT_FAILURE);
    }

    ret = harkio_Config_setSynchronousAverage(param->cfg, 16);
    if (ret == EXIT_FAILURE) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Config_setSynchronousAverage Failed");
        return (EXIT_FAILURE);
    }

    ret = harkio_Config_setTSPpath(param->cfg, "");
    if (ret == EXIT_FAILURE) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Config_setTSPpath Failed");
        return (EXIT_FAILURE);
    }

    ret = harkio_Config_setTSPoffset(param->cfg, 0);
    if (ret == EXIT_FAILURE) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Config_setTSPoffset Failed");
        return (EXIT_FAILURE);
    }

    ret = harkio_Config_setPeakSearchFrom(param->cfg, 0);
    if (ret == EXIT_FAILURE) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Config_setPeakSearchFrom Failed");
        return (EXIT_FAILURE);
    }

    ret = harkio_Config_setPeakSearchTo(param->cfg, 0);
    if (ret == EXIT_FAILURE) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Config_setPeakSearchTo Failed");
        return (EXIT_FAILURE);
    }

    ret = harkio_Config_setNfft(param->cfg, param->fmt.Nfft);
    if (ret == EXIT_FAILURE) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Config_setNfft Failed");
        return (EXIT_FAILURE);
    }

    ret = harkio_Config_setSamplingRate(param->cfg, param->fmt.samplingRate);
    if (ret == EXIT_FAILURE) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Config_setSamplingRate Failed");
        return (EXIT_FAILURE);
    }

    int value = 32768;
    ret = harkio_Config_setSignalMax(param->cfg, value);
    if (ret == EXIT_FAILURE) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Config_setSignalMax Failed");
        return (EXIT_FAILURE);
    }

    value = 16384;
    ret = harkio_Config_setTSPlength(param->cfg, value);
    if (ret == EXIT_FAILURE) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Config_setTSPlength Failed");
        return (EXIT_FAILURE);
    }

    return (EXIT_SUCCESS);

}    //end

/*--------------------------------------------------------------------------*/
/*  定位用                                                                   */
/*--------------------------------------------------------------------------*/
int sub_SetLocalization(param_t *param) {
    int i;
    int len, len1;

    /*-----------------------------------------*/
    /* M2PG 読み込む                                     */
    /*-----------------------------------------*/
    fread(&param->m2pg, 1, M2PG_LEN, param->m2pgfp);

    if (strncmp(param->m2pg.id, "M2PG", 4) != 0) {
        harkio_Log_printf(harkio_Log_Error, "file format error");
        return (EXIT_FAILURE);
    }

    /*-----------------------------------------*/
    //音源位置座標                             */
    /*-----------------------------------------*/
    len = param->m2pg.Nd * param->m2pg.Nr * COORD_LEN;

    if ((param->m2pg.src = (coord_t*) malloc(len)) == NULL) {
        harkio_Log_printf(harkio_Log_Error, "malloc Failed");
        return (EXIT_FAILURE);
    }

    fread(param->m2pg.src, 1, len, param->m2pgfp);

    /*-----------------------------------------*/
    /* 伝達関数                                */
    /*-----------------------------------------*/
    len1 = param->m2pg.Nr * param->fmt.Nmic * (param->fmt.Nfft / 2 + 1);
    len = param->m2pg.Nd * len1 * sizeof(COMPLEX_TYPE);

    if ((param->m2pg.tf = (COMPLEX_TYPE*) malloc(len)) == NULL) {
        harkio_Log_printf(harkio_Log_Error, "malloc Failed");
        return (EXIT_FAILURE);
    }

    fread(param->m2pg.tf, 1, len, param->m2pgfp);

    /*-----------------------------------------*/
    /*  Nd 個 harkio_matrix を作成             */
    /*-----------------------------------------*/
    if ((param->loctf_p = (harkio_Matrix**) malloc(sizeof(harkio_Matrix*) * param->m2pg.Nd)) == NULL) {
        harkio_Log_printf(harkio_Log_Error, "malloc Failed");
        return (EXIT_FAILURE);
    }

    for (i = 0; i < param->m2pg.Nd; i++) {
        param->loctf_p[i] = harkio_Matrix_new("complex", param->fmt.Nmic, (param->fmt.Nfft / 2 + 1));  //1 source bun

        if (param->loctf_p[i] == NULL) {
            harkio_Log_printf(harkio_Log_Error, "harkio_Matrix_new Failed");
            return (EXIT_FAILURE);
        }

        memset(param->loctf_p[i]->data.complexdata, '\0', len1 * sizeof(COMPLEX_TYPE));
        memcpy(param->loctf_p[i]->data.complexdata, &param->m2pg.tf[len1 * i], len1 * sizeof(COMPLEX_TYPE));
    }

    if (param->m2pg.tf != NULL) {
        free(param->m2pg.tf);
        param->m2pg.tf = NULL;
    }
    return (EXIT_SUCCESS);

}  //end

/*--------------------------------------------------------------------------*/
/*  分離用                                                                  */
/*--------------------------------------------------------------------------*/
int sub_SetSeparation(param_t *param) {
    int len, len1;
    int i;
    long seek_len;

    if (param->gtffp == NULL) {
        return (EXIT_FAILURE);
    }

    switch (param->type) {
    case 2: //2:gtf
    case 3: //3:m2pg+gtf
    case 4: //4:sm
        /* move gtf chunk */
        seek_len = HEADER_LEN  // header
        + FMT_LEN + (param->fmt.Nmic * COORD_LEN);  // fmt_chunk

        fseek(param->gtffp, seek_len, SEEK_SET);
        break;

    default:
        return (EXIT_FAILURE);
    }

    /*-----------------------------------------*/
    /* gtf chunk 読み込む                              */
    /*-----------------------------------------*/
    fread(&param->gtf, 1, GTF_LEN, param->gtffp);

    if (strncmp(param->gtf.id, "GTF", 3) != 0 && strncmp(param->gtf.id, "SM", 2) != 0) {
        harkio_Log_printf(harkio_Log_Error, "file format error [%4s]", param->gtf.id);
        return (EXIT_FAILURE);
    }

    /*-----------------------------------------*/
    /* 音源位置座標                              */
    /*-----------------------------------------*/
    len = param->gtf.Ns * COORD_LEN;

    param->gtf.src = (coord_t*) malloc(len);
    if (param->gtf.src == NULL) {
        harkio_Log_printf(harkio_Log_Error, "malloc Failed");
        return (EXIT_FAILURE);
    }

    fread(param->gtf.src, 1, len, param->gtffp);

    /*-----------------------------------------*/
    /* Nf                                      */
    /*-----------------------------------------*/
    fread(&param->gtf.Nf, 1, sizeof(int), param->gtffp);

    /*-----------------------------------------*/
    /* map 数                                  */
    /*-----------------------------------------*/
    len = param->gtf.Nf * sizeof(int);

    param->gtf.nfmap = (int*) malloc(len);
    if (param->gtf.nfmap == NULL) {
        harkio_Log_printf(harkio_Log_Error, "malloc Failed");
        return (EXIT_FAILURE);
    }

    fread(param->gtf.nfmap, 1, len, param->gtffp);

    /*-----------------------------------------*/
    /* 伝達関数                                */
    /*-----------------------------------------*/
    len1 = param->fmt.Nmic * param->gtf.Nf;
    len = param->gtf.Ns * len1 * sizeof(COMPLEX_TYPE);

    param->gtf.tf = (COMPLEX_TYPE*) malloc(len);
    if (param->gtf.tf == NULL) {
        harkio_Log_printf(harkio_Log_Error, "malloc Failed");
        return (EXIT_FAILURE);
    }

    fread(param->gtf.tf, 1, len, param->gtffp);

    /*-----------------------------------------*/
    /*  Ns個 harkio_matrix を作る              */
    /*-----------------------------------------*/
    param->septf_p = (harkio_Matrix**) malloc(sizeof(harkio_Matrix*) * param->gtf.Ns);
    if (param->septf_p == NULL) {
        harkio_Log_printf(harkio_Log_Error, "malloc Failed");
        return (EXIT_FAILURE);
    }

    /*-----------------------------------------*/
    /* Ns個の harkio_matrixに伝達関数セット    */
    /*-----------------------------------------*/
    for (i = 0; i < param->gtf.Ns; i++) {
        param->septf_p[i] = harkio_Matrix_new("complex", param->fmt.Nmic, param->gtf.Nf);  //1 source bun

        if (param->septf_p[i] == NULL) {
            harkio_Log_printf(harkio_Log_Error, "harkio_Matrix_new Failed");
            return (EXIT_FAILURE);
        }

        memset(param->septf_p[i]->data.complexdata, '\0', len1 * sizeof(COMPLEX_TYPE));   //len1=8*257
        memcpy(param->septf_p[i]->data.complexdata, &param->gtf.tf[len1 * i], len1 * sizeof(COMPLEX_TYPE));
    }

    if (param->gtf.tf != NULL) {
        free(param->gtf.tf);
        param->gtf.tf = NULL;
    }

    if (param->gtf.nfmap != NULL) {
        free(param->gtf.nfmap);
        param->gtf.nfmap = NULL;
    }

    return (EXIT_SUCCESS);

}   //end

int sub_SetSrcPositions(param_t *param) {
    return sub_SetSrcPositions_byPositionsId(param, NULL);
}

/*-----------------------------------------------*/
/* 音源位置座標をharkio_Positionsに変換 (内部関数)             */
/* @param poses ID決定の際に参照する座標群 (sm変換の場合に利用) */
/*-----------------------------------------------*/
int sub_SetSrcPositions_byPositionsId(param_t *param, harkio_Positions *poses) {

    int poscnt;
    int i;
    int ret;
    float coord[3];

    float distcur;
    int tfposcnt;
    int id;

    switch (param->type) {
    case 1: //1:m2pg
    case 3: //3:m2pg+gtf
        /*-----------------------------------------*/
        /* 音源位置座標をharkio_Positionsに変換     */
        /*-----------------------------------------*/
        poscnt = param->m2pg.Nd * param->m2pg.Nr;
        param->src_p = (harkio_Position**) malloc(sizeof(harkio_Position*) * poscnt);

        if (param->src_p == NULL) {
            harkio_Log_printf(harkio_Log_Error, "malloc Failed");
            return (EXIT_FAILURE);
        }

        for (i = 0; i < poscnt; i++) {
            coord[0] = param->m2pg.src[i].x;
            coord[1] = param->m2pg.src[i].y;
            coord[2] = param->m2pg.src[i].z;

            param->src_p[i] = harkio_Position_new(i, Cartesian, coord, NULL);

            if (param->src_p[i] == NULL) {
                harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
                return (EXIT_FAILURE);
            }
        }

        free(param->m2pg.src);
        param->m2pg.src = NULL;
        break;

    case 2: //2:gtf
        /*-----------------------------------------*/
        /* 音源位置座標をharkio_Positionsに変換       */
        /*-----------------------------------------*/
        poscnt = param->gtf.Ns;
        param->src_p = (harkio_Position**) malloc(sizeof(harkio_Position*) * poscnt);

        if (param->src_p == NULL) {
            harkio_Log_printf(harkio_Log_Error, "malloc Failed");
            return (EXIT_FAILURE);
        }

        for (i = 0; i < poscnt; i++) {
            coord[0] = param->gtf.src[i].x;
            coord[1] = param->gtf.src[i].y;
            coord[2] = param->gtf.src[i].z;

            param->src_p[i] = harkio_Position_new(i, Cartesian, coord, NULL);
            if (param->src_p[i] == NULL) {
                harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
                return (EXIT_FAILURE);
            }

        }
        free(param->gtf.src);
        param->gtf.src = NULL;
        break;

    case 4: //4:sm
        /*-----------------------------------------*/
        /* 音源位置座標をharkio_Positionsに変換     */
        /*-----------------------------------------*/
        /* 分離行列の座標数 */
        poscnt = param->gtf.Ns;
        param->src_p = (harkio_Position**) malloc(sizeof(harkio_Position*) * poscnt);
        if (param->src_p == NULL) {
            harkio_Log_printf(harkio_Log_Error, "malloc Failed");
            return (EXIT_FAILURE);
        }

        /* TFのID数を取得 */
        if (poses == NULL) {
            harkio_Log_printf(harkio_Log_Error, "Positions read error");
            return (EXIT_FAILURE);
        }
        tfposcnt = harkio_Positions_getSize(poses);
        if (tfposcnt <= 0) {
            harkio_Log_printf(harkio_Log_Error, "Positions read error");
            return (EXIT_FAILURE);
        }

        /* 各音源位置に割り当てるべき最近傍の位置のIDを付してPositionsへの追加 */
        for (i = 0; i < poscnt; i++) {
            coord[0] = param->gtf.src[i].x;
            coord[1] = param->gtf.src[i].y;
            coord[2] = param->gtf.src[i].z;

            /* TFの位置座標の中で最も近い座標を持つIDを取得 */
            float distmin = FLT_MAX;
            int distminid = -1;
            for (id = 0; id < tfposcnt; id++) {
                harkio_Position* pos = harkio_Positions_getById(poses, id);
                distcur = ((coord[0] - pos->coord[0]) * (coord[0] - pos->coord[0])
                        + (coord[1] - pos->coord[1]) * (coord[1] - pos->coord[1])
                        + (coord[2] - pos->coord[2]) * (coord[2] - pos->coord[2]));
                /* distcur = sqrt(distcur); */
                if (distmin > distcur) {
                    distmin = distcur;
                    distminid = id;
                }
            }
            if (distminid < 0) {
                harkio_Log_printf(harkio_Log_Error, "position calc error");
                return (EXIT_FAILURE);
            }

            /* ID付けしたPositionを生成 */
            param->src_p[i] = harkio_Position_new(distminid, Cartesian, coord, NULL);

            if (param->src_p[i] == NULL) {
                harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
                return (EXIT_FAILURE);
            }

        }
        free(param->gtf.src);
        param->gtf.src = NULL;
        break;

    default:
        return (EXIT_FAILURE);

    }

    param->src_ps = harkio_Positions_new("soundsource", -1, 0, NULL);
    if (param->src_ps == NULL) {
        harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
        return (EXIT_FAILURE);
    }
    for (i = 0; i < poscnt; i++) {
        if (harkio_Positions_append(param->src_ps, param->src_p[i]) == EXIT_FAILURE) {
            harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
            return (EXIT_FAILURE);
        }
    }
    free(param->src_p);
    param->src_p = NULL;

    /*-----------------------------------------*/
    /* 近接ID                                  */
    /*-----------------------------------------*/
    param->nbrs = harkio_Neighbors_new(param->src_ps);
    if (param->nbrs == NULL) {
        harkio_Log_printf(harkio_Log_Error, "can not allocated memory");
        return (EXIT_FAILURE);
    }

    ret = harkio_Neighbors_calcNeighbors(param->nbrs, NearestNeighbor, 3, 0.5); //近接数=3 threshold=0.5
    if (ret == EXIT_FAILURE) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Neighbors_calcNeighbors Failed");
        return (EXIT_FAILURE);
    }

    // harkio_Neighbors_print(param->nbrs);

    return (EXIT_SUCCESS);

} //end

void freeParamTF(param_t *m, harkio_TransferFunction **tf) {
    harkio_TransferFunction_delete(tf);

    m->src_ps = NULL;
    m->mic_ps = NULL;
    m->cfg = NULL;
    m->nbrs = NULL;
    m->locpath = NULL;
    m->seppath = NULL;

    m->src_p = NULL;
    m->mic_p = NULL;
    m->loctf_p = NULL;
    m->septf_p = NULL;

    if (m->fmt.mic != NULL)
        free(m->fmt.mic);
    if (m->m2pg.src != NULL)
        free(m->m2pg.src);
    if (m->gtf.src != NULL)
        free(m->gtf.src);
    if (m->gtf.nfmap != NULL)
        free(m->gtf.nfmap);
    if (m->m2pg.tf != NULL)
        free(m->m2pg.tf);
    if (m->gtf.tf != NULL)
        free(m->gtf.tf);
    if (m->m2pgfp != NULL)
        fclose(m->m2pgfp);
    if (m->gtffp != NULL)
        fclose(m->gtffp);

    m->fmt.mic = NULL;
    m->m2pg.src = NULL;
    m->gtf.src = NULL;
    m->gtf.nfmap = NULL;
    m->m2pg.tf = NULL;
    m->gtf.tf = NULL;
    m->m2pgfp = NULL;
    m->gtffp = NULL;
}

