
#if !defined(_HARKLIB_TF_H_INCLUDED_)
#define _HARKLIB_TF_H_INCLUDED_ 1

#include <string>
#include <complex>
#include <map>
#include <vector>
#include "matrix.h"

#include "hark-lib/hark-lib.h"
#include "hark-lib/object.h"

namespace hark
{
    class TransferFunction;

    enum TF_Coordinate {
        Polar,    // Polar coordination system
        Cartesian // Cartesian coordination system
    };

    enum TF_NeighborAlgorithm {
        Undefined,      // Undefined (default)
        NearestNeighbor // Nearest Neighbor method, Judge that multiple Positions closer than the threshold are adjacent.
    };

    class Position : public object {
    public:
        int id;                       // Position ID
        TF_Coordinate sys;            // Coordinate system of coord[]
        float coord[3];               // Coordination
        std::string path;             // The file path that corresponds to the position. e.g.) TSP recording file, etc...
        Matrix<std::complex<float> > data; // Matrix data corresponding to the position. e.g.) Transfer function, etc...
        int channelsUse;              // Channel-set information corresponding to the position (numChannels, channels), 0: Unused / 1: Used
        std::vector<int> channels;    // Number of channels corresponding to the position.

        Position(){
        }
        Position(const Position &data){
            *this = data;
        }

        Position& operator = (const Position &src){
            id = src.id;
            sys = src.sys;
            coord[0] = src.coord[0];
            coord[1] = src.coord[1];
            coord[2] = src.coord[2];
            path = src.path;
            data.resize(src.data.rows(), src.data.cols());
            for(int i=0; i<src.data.rows(); i++){
                for(int j=0; j<src.data.cols(); j++){
                    data(i, j) = src.data(i, j);
                }
            }
            channelsUse = src.channelsUse;
            channels = src.channels;
            return *this;
        }

        virtual std::ostream& write(std::ostream &os) const;
    };

    std::istream &operator >> (std::istream &in, Position &data);

    class Neighbor : public object {
    public:
        std::vector<int> ids;                      // A list of Position ids that you want to represent adjacencies.
        std::vector<std::vector<int> > neighbors;  // List of adjacent Position ids by Position.
        std::vector<std::vector<Position> > poses; // A list of Posisitions represented by adjacencies.
        TF_NeighborAlgorithm alg;                  // Algorithm for finding adjacency.

        Neighbor(){
            clear();
        }
        Neighbor(const Neighbor &data){
            *this = data;
        }

        void clear(){
            ids.clear();
            neighbors.clear();
            poses.clear();
            alg = NearestNeighbor;
        }
        Neighbor& operator = (const Neighbor &src){
            ids = src.ids;
            neighbors = src.neighbors;
            poses = src.poses;
            alg = src.alg;
            return *this;
        }

        virtual std::ostream& write(std::ostream &os) const;
    };

    std::istream &operator >> (std::istream &in, Neighbor &data);

    class Config : public object {
    public:
        std::string comment;     // Any string to use for comments.
        int synchronousAverage;  // Number of TSP synchronous additions.
        std::string TSPpath;     // File path to the original signal of TSP.
        int TSPoffset;           // Offset until TSP starts [samples]
        int TSPLength;           // Length of TSP signal to be recorded [samples]
        int peakSearchFrom;      // The starting position of the peak search range of the impulse response.
        int peakSearchTo;        // The end position of the peak search range of the impulse response.
        int nfft;                // FFT length.
        int samplingRate;        // Sampling frequency of acoustic signal file.
        int signalMax;           // Maximum amplitude of the acoustic signal file.

        Config(){
            clear();
        }
        Config(const Config &data){
            *this = data;
        }

        void clear(){
            comment = "";
            synchronousAverage = 0;
            TSPpath = "";
            TSPoffset = 0;
            TSPLength = 0;
            peakSearchFrom = 0;
            peakSearchTo = 0;
            nfft = 0;
            samplingRate = 0;
            signalMax = 0;
        }

        virtual std::ostream& write(std::ostream &os) const;
    };

    std::istream &operator >> (std::istream &in, Config &data);

    class TransferFunction : public object {
    private:
        bool is_valid;
        std::string m_WhatIsThis;
        std::vector<Position> m_Sources;
        Neighbor m_Neighbors;
        std::vector<Position> m_Microphones;
        Config m_Config;
        std::map<int, Matrix<std::complex<float> > > m_Localization;
        std::map<int, Matrix<std::complex<float> > > m_Separation;

    public:
        TransferFunction(){
            m_WhatIsThis = "";
            m_Sources.clear();
            m_Neighbors.clear();
            m_Microphones.clear();
            m_Config.clear();
            m_Localization.clear();
            m_Separation.clear();
            is_valid = false;
        }
        TransferFunction(const TransferFunction &data){
            *this = data;
        }

        void CopyMap(const std::map<int, Matrix<std::complex<float> > > &src, std::map<int, Matrix<std::complex<float> > > &dst) const {
            dst.clear();
            for(const auto &mp : src){
                Matrix<std::complex<float> > mt;
                CopyMatrix(mp.second, mt);
                dst.insert(make_pair(mp.first, mt));
            }
        }

        void CopyMatrix(const Matrix<std::complex<float> > &src, Matrix<std::complex<float> > &dst) const {
            dst.resize(src.rows(), src.cols());
            for(int i=0; i<src.rows(); i++){
                for(int j=0; j<src.cols(); j++){
                    dst(i, j) = src(i, j);
                }
            }
        }

        const bool& IsValid() const {
            return is_valid;
        }

        void SetValid(const bool &valid){
            is_valid = valid;
        }

        const std::string& GetWhatIsThis() const {
            return m_WhatIsThis;
        }
        void SetWhatIsThis(const std::string &whatIsThis){
            m_WhatIsThis = whatIsThis;
        }

        const std::vector<Position>& GetSources() const {
            return m_Sources;
        }
        void SetSources(const std::vector<Position> &sources){
            m_Sources = sources;
        }

        const Neighbor& GetNeighbors() const {
            return m_Neighbors;
        }
        void SetNeighbors(const Neighbor &neighbors){
            m_Neighbors = neighbors;
        }

        const std::vector<Position>& GetMicrophones() const {
            return m_Microphones;
        }
        void SetMicrophones(const std::vector<Position> &microphones){
            m_Microphones = microphones;
        }

        const Config& GetConfig() const {
            return m_Config;
        }
        void SetConfig(const Config &config){
            m_Config = config;
        }

        const std::map<int, Matrix<std::complex<float> > >& GetLocalization() const {
            return m_Localization;
        }
        void SetLocalization(const std::map<int, Matrix<std::complex<float> > > &localization){
            CopyMap(localization, m_Localization);
        }

        const std::map<int, Matrix<std::complex<float> > >& GetSeparation() const {
            return m_Separation;
        }
        void SetSeparation(const std::map<int, Matrix<std::complex<float> > > &separation){
            CopyMap(separation, m_Separation);
        }

        virtual std::ostream& write(std::ostream &os) const;
    };

    std::istream &operator >> (std::istream &in, TransferFunction &data);

}//namespace end

#endif /* #if !defined(_HARKLIB_TF_H_INCLUDED_) */

/* end of file */
