/*!
 * @file libharkio3_positions.c
 *
 * @author Takeshi Mizumoto
 * @version 0.1
 * @brief  harkio_Positions の API 群。 複数の位置 (マイクアレイ、音源定位結果群など) を表現する。 内部に harkio_Position を持つ。
 */

#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "libharkio3.h"
#include <libxml/xmlreader.h>

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

/*!
 * @brief コンストラクタ。空の位置セットのオブジェクト harkio_Positions を作成する。
 * @return harkio_Positions 型のポインタ。失敗すると NULL を返す。
 * @param[in]       type    harkio_Positions のタイプ。 tsp, impulse, noise, microphone, soundsource のいずれか、あるいは NULL
 * @param[in]       frame   harkio_Positions に対応するフレーム番号。
 * @param[in]       size    harkio_Positions が保持する harkio_Position の数 (= pos の長さ)
 * @param[in]       pos     harkio_Positions に保持した harkio_Position 配列へのポインタ。
 NULLが渡された場合、内部で空の配列を持ち、harkio_Positions_appendによって追加可能。
 NULL以外が渡された場合、harkio_Positions_appendによる追加は不可。
 * @details 使用が終わったら、 harkio_Positions_delete でメモリ解放を行う。
 type の説明は下記の通り。
 tsp:         TSP測定 の音源位置を表すときに使用
 impulse:     インパルス応答の音源位置を表すときに使用
 noise:       ノイズの位置を表すときに使用
 micorphone:  マイク位置を表すときに使用
 soundsource: 音源定位結果の位置を表すときに使用
 NULL の場合は保存時に出力されない。
 */
harkio_Positions* harkio_Positions_new(char *type, int frame, int size, harkio_Position **pos) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    // type check
    if (type != NULL) {
        int i;
        const int numAcceptableTypes = 5;
        for (i = 0; i < numAcceptableTypes; i++) {
            const char *acceptableTypes[] = { "tsp", "impulse", "noise", "microphone", "soundsource" };
            if (strcmp(acceptableTypes[i], type) == 0) {
                break;
            }
        }

        if (i == numAcceptableTypes) {
            harkio_Log_printf(harkio_Log_Error, "unknown type (%s)", type);
            return NULL;
        }

    }

    // size check (must be positive)
    if (size < 0) {
        harkio_Log_printf(harkio_Log_Error, "size must be positive");
        return NULL;
    }

    enum InternalAllocFlag allocflag = NO_INTERNAL_ALLOC;
    if (pos == NULL) {
        if (size == 0) {
            allocflag = INTERNAL_ALLOC;
        } else {
            harkio_Log_printf(harkio_Log_Error, "size should be 0 when no position input");
            return NULL;
        }
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    // allocate memory
    harkio_Positions *p = (harkio_Positions*) malloc(sizeof(harkio_Positions));

    if (p == NULL) {
        harkio_Log_printf(harkio_Log_Error, "memory allocation failed.");
        return NULL;
    }

    if (type != NULL) {
        p->type = (char*) malloc(sizeof(char) * (strlen(type) + 1));
        if (p->type == NULL) {
            harkio_Log_printf(harkio_Log_Error, "memory allocation failed.");
            return NULL;
        }
        // copy data
        strcpy(p->type, type);
    } else {
        //fprintf(stderr, "type is NULL");
        p->type = NULL;
    }

    p->size = size;
    p->frame = frame;
    p->pos = pos;
    p->allocflag = allocflag;

    //first id is set "0"
    if (size > 0) {
        if (p->pos[0]->id == -1) {
            p->pos[0]->id = 0;
        }
    }

    return p;
}

static int harkio_Positions_internal_processNode(xmlTextReaderPtr reader, harkio_Positions *poses);

/*!
 * @brief コンストラクタ。XMLが格納された文字列へのポインタから、harkio_Positions オブジェクトを生成する
 * @param[in] buffer   XML が格納された文字列へのポインタ。
 * @param[in] len      buffer の長さ。
 * @return harkio_Positions へのポインタ。失敗すると NULL ポインタを返す。
 * @details harkio_Positions_delete でメモリ解放を行う必要がある。メンバは NULL でもよい。
 */
harkio_Positions* harkio_Positions_newFromXMLbuffer(char *buffer, int len, char *dtdpath) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int ret;
    xmlTextReaderPtr reader;

    harkio_Positions* poses = harkio_Positions_new(NULL, 0, 0, NULL);
    if (!poses) {
        harkio_Log_printf(harkio_Log_Error, "cannot create harkio_Positions object.");
        return NULL;
    }

    reader = xmlReaderForMemory(buffer, len, "", NULL, 0);
    if (!reader) {
        harkio_Log_printf(harkio_Log_Error, "xmlReaderForMemory error");
        xmlFreeTextReader(reader);
        free(poses);
        return NULL;
    }

    ret = xmlTextReaderRead(reader);

    while (ret == 1) {
        harkio_Positions_internal_processNode(reader, poses);
        ret = xmlTextReaderRead(reader);
    }

    xmlFreeTextReader(reader);
    if (ret == -1) {
        harkio_Log_printf(harkio_Log_Error, "xmlTextReaderRead error");
        return NULL;
    }

    return poses;
}

/**
 internal function
 */
static int harkio_Positions_internal_processNode(xmlTextReaderPtr reader, harkio_Positions *poses) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    static enum Coordinate sys;
//    int nodeType;
    xmlReaderTypes nodeType;
    xmlChar *name, *value;
    const char *constValue;

    name = xmlTextReaderName(reader);
    nodeType = xmlTextReaderNodeType(reader);

    if (nodeType == XML_READER_TYPE_ELEMENT) {
        if (xmlStrcmp(name, BAD_CAST "positions") == 0) {
            xmlTextReaderMoveToFirstAttribute(reader);
            xmlFree(name);

            do {
                name = xmlTextReaderName(reader);
                value = xmlTextReaderValue(reader);
                constValue = (const char*) value;

                if (xmlStrcmp(name, BAD_CAST "type") == 0) {
                    if (poses->type) {
                        strcpy(poses->type, constValue);
                    } else {
                        poses->type = strdup(constValue);
                    }
                } else if (xmlStrcmp(name, BAD_CAST "coordinate") == 0) {
                    if (xmlStrcmp(value, BAD_CAST "cartesian") == 0) {
                        sys = Cartesian;
                    } else {
                        xmlFree(name);
                        xmlFree(value);
                        return EXIT_FAILURE;
                    }
                }
                xmlFree(name);
                xmlFree(value);
            } while (xmlTextReaderMoveToNextAttribute(reader) == 1);
        } else if (xmlStrcmp(name, BAD_CAST "position") == 0) {
            int id;
            float coord[3];
            char path[4192] = "";

            xmlFree(name);
            xmlTextReaderMoveToFirstAttribute(reader);
            do {
                name = xmlTextReaderName(reader);
                value = xmlTextReaderValue(reader);
                constValue = (const char*) value;
                // fprintf(stderr, "%s=%s\n", name, value);

                if (xmlStrcmp(name, BAD_CAST "x") == 0) {
                    coord[0] = atof(constValue);
                } else if (xmlStrcmp(name, BAD_CAST "y") == 0) {
                    coord[1] = atof(constValue);
                } else if (xmlStrcmp(name, BAD_CAST "z") == 0) {
                    coord[2] = atof(constValue);
                }
                if (xmlStrcmp(name, BAD_CAST "azimuth") == 0) {
                    coord[0] = atof(constValue);
                } else if (xmlStrcmp(name, BAD_CAST "elevation") == 0) {
                    coord[1] = atof(constValue);
                } else if (xmlStrcmp(name, BAD_CAST "radius") == 0) {
                    coord[2] = atof(constValue);
                } else if (xmlStrcmp(name, BAD_CAST "path") == 0) {
                    strncpy(path, constValue, 4192);
                } else if (xmlStrcmp(name, BAD_CAST "id") == 0) {
                    id = atoi(constValue);
                }
                xmlFree(name);
                xmlFree(value);
            } while (xmlTextReaderMoveToNextAttribute(reader) == 1);
            harkio_Positions_append(poses, harkio_Position_new(id, sys, coord, path));
        }
    } else {
        xmlFree(name);
    }
    name = xmlTextReaderName(reader);

    xmlFree(name);
    return EXIT_SUCCESS;
}

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

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (pos == NULL || *pos == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Position is NULL");
        return;
    }

    if ((*pos)->allocflag == INTERNAL_ALLOC) {
        if ((*pos)->pos) {
            int i = 0;
            for (i = 0; i < (*pos)->size; i++) {
                harkio_Position_delete((*pos)->pos + i);
            }
            free((*pos)->pos);
        }
    }
    if ((*pos)->type) {
        free((*pos)->type);
    }

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

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

/*!
 * @brief Getter。harkio_Positions が持つ harkio_Position のから、与えられた id と同じものを返す。
 * @param[in] poses    harkio_Position を取り出したい構造体へのポインタ
 * @param[in] id       取り出したい ID
 * @return 与えられた ID を持つ harkio_Position へのポインタ
 */
harkio_Position* harkio_Positions_getById(harkio_Positions *poses, int id) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

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

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    int i;
    for (i = 0; i < poses->size; i++) {
        if (poses->pos[i]->id == id) {
            return poses->pos[i];
        }
    }

    harkio_Log_printf(harkio_Log_Error, "id is not found");
    return NULL;
}

/*!
 * @brief Getter。harkio_Positions が持つ harkio_Position のから、与えられた位置に近いものを返す。
 * @param[in] poses         harkio_Position を取り出したい構造体へのポインタ
 * @param[in] coordinate    coord で与える座標の座標系
 * @param[in] coord         取り出したい位置の座標
 * @param[in] tolerance     許容誤差
 * @return 与えられた位置に最も近い harkio_Position へのポインタ
 */
harkio_Position* harkio_Positions_getByCoord(harkio_Positions *poses, enum Coordinate coordinate, float *coord,
        float tolerance) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, j;
    float pos_coord[3];

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

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    for (i = 0; i < poses->size; i++) {
        float dist = 0;
        if (coordinate == Cartesian) {
            for (j = 0; j < 3; j++) {
                dist += pow(fabsf(poses->pos[i]->coord[j] - coord[j]), 2);
            }

            dist = sqrt(dist);

            if (dist < tolerance) {
                return poses->pos[i];
            }
        } else if (coordinate == Polar) {
            harkio_Position_Convert(poses->pos[i], pos_coord, Cartesian);

            for (j = 0; j < 3; j++) {
                dist += pow(fabsf(pos_coord[j] - coord[j]), 2);
            }

            dist = sqrt(dist);

            if (dist < tolerance) {
                return poses->pos[i];
            }
        }
    }

    harkio_Log_printf(harkio_Log_Error, "position is not found.");
    return NULL;
}

/*!
 * @brief Getter。harkio_Positions のタイプを返す。
 * @param[in] poses         harkio_Position を取り出したい構造体へのポインタ
 * @return タイプを表す文字列へのポインタ
 */
char* harkio_Positions_getType(harkio_Positions *poses) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

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

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

/*!
 * @brief Getter。harkio_Positions の harkio_Position の数を返す。
 * @param[in] poses         harkio_Position を取り出したい構造体へのポインタ
 * @return harkio_Positions が保持する位置の数。失敗すれば-1を返す。
 */
int harkio_Positions_getSize(harkio_Positions *poses) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

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

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

/*!
 * @brief Getter。harkio_Positions のフレーム番号を返す。
 * @param[in] poses         harkio_Position を取り出したい構造体へのポインタ
 * @return harkio_Positions のフレーム番号。失敗すれば-1を返す。
 */
int harkio_Positions_getFrame(harkio_Positions *poses) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

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

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

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

/*!
 * @brief　harkio_Positions に新たな harkio_Position を追加する。
 * @param[in,out] poses    位置を追加する先の harkio_Positions
 * @param[in] pos         追加する harkio_Position
 * @return 追加に成功すれば EXIT_SUCCESS, 失敗すれば EXIT_FAILURE を返す。
 * @details pos はメモリをコピーせずに、ポインタが poses に追加される。
 *         pos が 正のID を持つ場合はその ID のまま append される。
 *         pos の ID が -1 の場合は、現在の harkio_Positions の ID の最大値 + 1 を自動的に与えて append される。
 */
int harkio_Positions_append(harkio_Positions *poses, harkio_Position *pos) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    // do nothing if either is NULL.
    if (!poses || !pos) {
        harkio_Log_printf(harkio_Log_Error, "invalid harkio_Positions or harkio_Position");
        return EXIT_FAILURE;
    }
    if (poses->allocflag != INTERNAL_ALLOC) {
        harkio_Log_printf(harkio_Log_Error,
                "Position array given in harkio_Positions_new initialization is not editable");
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    if (pos->id == -1) {
        if (poses->size <= 0) {
            pos->id = 0;
        } else {
            int max = 0;
            int i;
            max = poses->pos[0]->id;

            for (i = 0; i < poses->size; i++) {
                if (max < poses->pos[i]->id) {
                    max = poses->pos[i]->id;
                }
            }
            pos->id = max + 1;
        }
    }

    // new size
    poses->size++;

    // reallocate pos
    harkio_Position **newpointer = (harkio_Position **) realloc(poses->pos, sizeof(harkio_Positions*) * poses->size);

    if (newpointer == NULL) {
        harkio_Log_printf(harkio_Log_Error, "new harkio_Position failed");
        return EXIT_FAILURE;
    }

    // append the position to the last
    newpointer[poses->size - 1] = pos;

    // free(poses->pos);
    // overwrite a new pointer to Positions.
    poses->pos = newpointer;

    return EXIT_SUCCESS;
}

/*!
 * @brief　harkio_Positions から、与えられた ID の harkio_Position を削除する
 * @param[in,out] poses    削除したい harkio_Positions
 * @param[out] id          削除したい harkio_Position　の ID.
 * @return 削除に成功すれば EXIT_SUCCESS, 失敗すれば EXIT_FAILURE を返す。
 * @details 削除された　pos はメモリ解放も自動で行われる。
 *         メモリはまるごとfreeされるので注意。
 */
int harkio_Positions_removeById(harkio_Positions *poses, int id) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i, counter = 0;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (poses == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Positions is NULL");
        return EXIT_FAILURE;
    }
    if (poses->allocflag != INTERNAL_ALLOC) {
        harkio_Log_printf(harkio_Log_Error,
                "2 Position array given in harkio_Positions_new initialization is not editable");
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_Position *pos = harkio_Positions_getById(poses, id);

    if (pos == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Positions_getById error");
        return EXIT_FAILURE;
    }

    // allocate positions with length of size-1
    harkio_Position **newpos = (harkio_Position **) malloc(sizeof(harkio_Position*) * (poses->size - 1));

    for (i = 0; i < poses->size; i++) {
        if (pos != poses->pos[i]) {
            newpos[counter++] = poses->pos[i];
        }
    }

    harkio_Position_delete(&pos); //free the found position

    if (poses->pos) {
        free(poses->pos);
        poses->pos = NULL;
    }

    poses->pos = newpos;
    poses->size--;

    return EXIT_SUCCESS;
}

/*!
 * @brief　harkio_Positions から、与えられた座標の harkio_Position を削除する
 * @param[in,out] poses    削除したい harkio_Positions
 * @param[in] poses         harkio_Position を取り出したい構造体へのポインタ
 * @param[in] coordinate    coord で与える座標の座標系
 * @param[in] coord         取り出したい位置の座標
 * @param[in] tolerance     許容誤差
 * @return 削除に成功すれば EXIT_SUCCESS, 失敗すれば EXIT_FAILURE を返す。
 * @details 削除された　pos はメモリ解放も自動で行われる。
 */
int harkio_Positions_removeByCoord(harkio_Positions *poses, enum Coordinate coordinate, float *coord, float tolerance) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (poses == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Positions is NULL");
        return EXIT_FAILURE;
    }
    if (poses->allocflag != INTERNAL_ALLOC) {
        harkio_Log_printf(harkio_Log_Error,
                "Position array given in harkio_Positions_new initialization is not editable");
        return EXIT_FAILURE;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_Position *p = harkio_Positions_getByCoord(poses, coordinate, coord, tolerance);

    if (p == NULL) {
        return EXIT_FAILURE;
    }

    return harkio_Positions_removeById(poses, p->id);
}

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

/*!
 * @brief　harkio_Positions をコピーする
 * @param[in] p1         コピーしたい harkio_Positions へのポインタ
 * @return コピーに成功すれば コピーした harkio_Positions へのポインタ, 失敗すれば NULL を返す。
 */
harkio_Positions* harkio_Positions_copy(harkio_Positions *p1) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i;

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

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    harkio_Positions *p2 = harkio_Positions_new(p1->type, p1->frame, 0, NULL);

    if (p2 == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Positions_new error");
        return NULL;
    }

    for (i = 0; i < p1->size; i++) {
        harkio_Positions_append(p2, harkio_Position_copy(p1->pos[i]));
    }

    return p2;
}

/*!
 * @brief　harkio_Positions をテキストで表示する
 * @param[in] p     テキストで表示したい harkio_Positions へのポインタ
 */
void harkio_Positions_print(harkio_Positions *p) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    char *buffer = harkio_Positions_printBuffer(p);
    if (buffer)
        fprintf(stderr, "%s", buffer);
    free(buffer);
}

/*!
 * @brief　harkio_Positions_printBuffer のヘルパ関数。文字列を追加。
 * @param[in] buffer   追記されるバッファ。メモリの確保は関数内でされるので不要。
 * @param[in] str      追記する文字列。
 * @param[in] bufsize  buffer の最大サイズ。これを超えると追記されない。
 */
int harkio_Positions_addstring(char **buffer, char *str, int bufsize) {
    int ret = bufsize;
    if (strlen(*buffer) + strlen(str) + 1 >= bufsize) {
        int step = 4192;
        char* tmp = (char*) realloc(*buffer, bufsize + step);
        *buffer = tmp;
        ret += step;
    }

    strcat(*buffer, str);
    return ret;
}

/*!
 * @brief harkio_Positions の文字列表現を格納したバッファを返す。
 * @param[in] p harkio_Positions オブジェクト
 * @return      char* (利用者による free が必要)
 */
char* harkio_Positions_printBuffer(harkio_Positions *p) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);
    char *buffer = (char*) malloc(MSG_LEN * sizeof(char));
    int buflen = MSG_LEN;
    int i;
    for (i = 0; i < MSG_LEN; i++)
        buffer[i] = '\0';

    if (p == NULL) {
        harkio_Log_printf(harkio_Log_Error, "harkio_Positions is NULL");
        free(buffer);
        return NULL;
    }

    sprintf(buffer, "Positions: size = %d\n", p->size);
    for (i = 0; i < p->size; i++) {
        char *tmp = harkio_Position_printBuffer(p->pos[i]);
        buflen = harkio_Positions_addstring(&buffer, tmp, buflen);
        free(tmp);
    }
    return buffer;
}

/*!
 * @brief　2つの harkio_Positions が等しいか否かを判定する
 * @param[in] a   比較したい harkio_Positions へのポインタ
 * @param[in] b   比較したい harkio_Positions へのポインタ
 * @return 等しければ 1, 等しくなければ 0 を返す
 * @details サイズ、フレーム番号、タイプが等しく、かつ、保持する harkio_Position が順番を含めて
 すべて等しいことが条件。 harkio_Position が等しいか否かは harkio_Position_isEqual で判定する。
 */
int harkio_Positions_isEqual(harkio_Positions *a, harkio_Positions *b) {
    harkio_Log_printf(harkio_Log_Trace, "@ %s", __func__);

    int i;

    //--------------------------------------------//
    // Argument Check                             //
    //--------------------------------------------//
    if (a == NULL || b == NULL) {
        return 0;
    }

    if (a->size != b->size) {
        return 0;
    }

    if (a->frame != b->frame) {
        return 0;
    }

    if ((a->type == NULL && b->type != NULL) || (a->type != NULL && b->type == NULL)) {
        return 0;
    }
    if (a->type && b->type && strcmp(a->type, b->type) != 0) {
        return 0;
    }

    //--------------------------------------------//
    // main                                       //
    //--------------------------------------------//
    for (i = 0; i < a->size; i++) {
        if (harkio_Position_isEqual(a->pos[i], b->pos[i]) == 0) {
            return 0;
        }
    }

    return 1;
}
