/*
 * ControlObject.h
 *
 */

#ifndef HA_CONTROLOBJECT_H_
#define HA_CONTROLOBJECT_H_

#define NEW_ID 0
#define EXISTING_ID 1
#define TERMINATED_ID 2

#include "HA_config.h"

#include "FLAC/metadata.h"
#include "FLAC/stream_encoder.h"

#ifdef __cplusplus

#ifdef ENABLE_JNI_FOR_ANDROID
#include <jni.h>
#endif // ENABLE_JNI_FOR_ANDROID
#include <semaphore.h>
#include <vector>
#include <map>
#include <algorithm>
#include <string>
#include <sstream>
#include <iomanip>
#include <iostream>
#include <pthread.h>
#include <unistd.h>

#include "config.h" // for get macro definition of ENABLE_* from configure
#include "HA_Object.h"
#include "HA_log.h"
#include "eventflag.h"
#include "HA_WavFileObject.h"
#include "HA_USBDeviceObject.h"
#include "HA_SocketDeviceObject.h"
//#include "HA_RaspLCObject.h"
//#include "HA_RaspZXObject.h"
#ifdef ENABLE_RASPMX
#include "HA_RaspMXObject.h"
#endif // ENABLE_RASPMX
#include "HA_buffer.h"
#include "HA_port.h"
#include "HarkData.h"
#include "MultiFFT.h"
#include "julius.h"
#ifdef ENABLE_VEHICLE
#include "vsclient.h"
#endif // ENABLE_VEHICLE

#include "mode_config.h"

namespace HARKAndroid {

struct flac_mic_data {
  int port;
  int fd;
  ::FLAC__StreamEncoder *encoder;
  ::FLAC__StreamMetadata *metadata[2];
  ::FLAC__StreamMetadata_VorbisComment_Entry entry;

flac_mic_data() : port(0), fd(-1), encoder(NULL)  {
  metadata[0] = metadata[1] = NULL;
  entry.length = 0;
  entry.entry = NULL;
}

virtual ~flac_mic_data() {

}

  int alloc(const int a_port,
	    const int a_fd,
	    const unsigned int channel,
	    const unsigned int samplerate,
	    const unsigned int blocksize,
	    const unsigned int max_lpc_order,
	    const unsigned int max_residual_partition_order,
	    void (*progress_callback)(const ::FLAC__StreamEncoder *encoder,
				      FLAC__uint64 bytes_written,
				      FLAC__uint64 samples_written,
				      unsigned frames_written,
				      unsigned total_frames_estimate,
				      void *client_data)
	    ) {

    ::FLAC__StreamEncoderInitStatus init_status;
    int i;

    port = a_port;
    fd   = a_fd;

    encoder = ::FLAC__stream_encoder_new();
    if (encoder == NULL) {
      std::cerr << "ERROR: allocating encoder\n" << std::endl;
      goto exit_func_0;
    }

    if(
       (metadata[0] = ::FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT)) == NULL ||
       (metadata[1] = ::FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING)) == NULL ||
       /* there are many tag (vorbiscomment) functions but these are convenient for this particular use: */
       !::FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "CREATOR", "HARK") ||
       !::FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, /*copy=*/false) /* copy=false: let metadata object take control of entry's allocated string */
       ) {
      
      std::cerr << "ERROR: FLAC out of memory or tag error\n" << std::endl;

      goto exit_func_1;

    } else {
      
      metadata[1]->length = 1234; /* set the padding length */
	      
      FLAC__bool ok = ::FLAC__stream_encoder_set_metadata(encoder, metadata, 2);
      if (!ok) {
	
	fprintf(stderr, "ERROR: FLAC out of memory or tag error2\n");
	goto exit_func_1;

      }

    }

    ::FLAC__stream_encoder_set_compression_level(encoder, 5);
    ::FLAC__stream_encoder_set_channels(encoder, channel);
    ::FLAC__stream_encoder_set_bits_per_sample(encoder, 24);
    ::FLAC__stream_encoder_set_sample_rate(encoder, samplerate);
    ::FLAC__stream_encoder_set_total_samples_estimate(encoder, 0);
    ::FLAC__stream_encoder_set_blocksize(encoder, blocksize);
    ::FLAC__stream_encoder_set_max_lpc_order(encoder, max_lpc_order);
    ::FLAC__stream_encoder_set_max_residual_partition_order(encoder, max_residual_partition_order);
    
    init_status = ::FLAC__stream_encoder_init_socket(encoder, 
						     fd,
						     progress_callback, 
						     /*client_data=*/NULL);
    if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
      std::cerr << "ERROR: FLAC initializing encoder: "
		<< ::FLAC__StreamEncoderInitStatusString[init_status]
		<< std::endl;

      goto exit_func_2;
    }

    return(0);

  exit_func_2:
    FLAC__metadata_object_delete(metadata[0]);
    FLAC__metadata_object_delete(metadata[1]);
  exit_func_1:
    ::FLAC__stream_encoder_delete(encoder);
  exit_func_0:
    return(-1);
  }


  int dealloc() {

    if (encoder != NULL) {
      ::FLAC__stream_encoder_finish(encoder);
      ::FLAC__stream_encoder_delete(encoder);
      encoder = NULL;
    }

    if (metadata[0] != NULL) {
      ::FLAC__metadata_object_delete(metadata[0]);
      metadata[0] = NULL;
    }
    if (metadata[1] != NULL) {
      ::FLAC__metadata_object_delete(metadata[1]);
      metadata[1] = NULL;
    }

    if (fd != -1) {
      ::close(fd);
      fd = -1;
    }

    return(0);
  }


};

struct port_fd_pair {
  int port;
  int fd;
port_fd_pair() : port(-1), fd(-1) {}
};

struct flac_sep_data {
  int port;
  int fd;
  int id;
  std::string id_string;
  ::FLAC__StreamEncoder *encoder;
  ::FLAC__StreamMetadata *metadata[2];
  ::FLAC__StreamMetadata_VorbisComment_Entry entry;

flac_sep_data() : port(-1), fd(-1), id(-1), id_string("-1"), encoder(NULL)  {
  metadata[0] = metadata[1] = NULL;
  entry.length = 0;
  entry.entry = NULL;
}

virtual ~flac_sep_data() {
}

  int alloc(const int a_fd,
	    const int a_id,
	    const unsigned int samplerate,
	    const unsigned int blocksize,
	    const unsigned int max_lpc_order,
	    const unsigned int max_residual_partition_order,
	    void (*progress_callback)(const ::FLAC__StreamEncoder *encoder,
				      FLAC__uint64 bytes_written,
				      FLAC__uint64 samples_written,
				      unsigned frames_written,
				      unsigned total_frames_estimate,
				      void *client_data)
	    ) {

    ::FLAC__StreamEncoderInitStatus init_status;
    int i;

    port = HA_PORT_F_FLAC_DATA;
    fd   = a_fd;
    id   = a_id;

    std::ostringstream os;
    os << id;
    id_string = os.str();

    encoder = ::FLAC__stream_encoder_new();
    if (encoder == NULL) {
      std::cerr << "ERROR: allocating encoder\n" << std::endl;
      goto exit_func_0;
    }

    if(
       (metadata[0] = ::FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT)) == NULL ||
       (metadata[1] = ::FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING)) == NULL ||
       /* there are many tag (vorbiscomment) functions but these are convenient for this particular use: */
       !::FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "CREATOR", "HARK") ||
       !::FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, /*copy=*/false) || /* copy=false: let metadata object take control of entry's allocated string */
       !::FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "ID", id_string.c_str()) ||
       !::FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, /*copy=*/false)
       ) {
      
      std::cerr << "ERROR: FLAC out of memory or tag error\n" << std::endl;

      goto exit_func_1;

    } else {
      
      metadata[1]->length = 1234; /* set the padding length */
	      
      FLAC__bool ok = ::FLAC__stream_encoder_set_metadata(encoder, metadata, 2);
      if (!ok) {
	
	fprintf(stderr, "ERROR: FLAC out of memory or tag error2\n");
	goto exit_func_1;

      }

    }

    ::FLAC__stream_encoder_set_compression_level(encoder, 5);
    ::FLAC__stream_encoder_set_channels(encoder, 1);
    ::FLAC__stream_encoder_set_bits_per_sample(encoder, 24);
    ::FLAC__stream_encoder_set_sample_rate(encoder, samplerate);
    ::FLAC__stream_encoder_set_total_samples_estimate(encoder, 0);
    ::FLAC__stream_encoder_set_blocksize(encoder, blocksize);
    ::FLAC__stream_encoder_set_max_lpc_order(encoder, max_lpc_order);
    ::FLAC__stream_encoder_set_max_residual_partition_order(encoder, max_residual_partition_order);
    
    init_status = ::FLAC__stream_encoder_init_socket(encoder, 
						     fd,
						     progress_callback, 
						     /*client_data=*/NULL);
    if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
      std::cerr << "ERROR: FLAC initializing encoder: "
		<< ::FLAC__StreamEncoderInitStatusString[init_status]
		<< std::endl;

      goto exit_func_2;
    }

    return(0);

  exit_func_2:
    FLAC__metadata_object_delete(metadata[0]);
    FLAC__metadata_object_delete(metadata[1]);
  exit_func_1:
    ::FLAC__stream_encoder_delete(encoder);
  exit_func_0:
    return(-1);
  }


  int dealloc() {

    if (encoder != NULL) {
      ::FLAC__stream_encoder_finish(encoder);
      ::FLAC__stream_encoder_delete(encoder);
      encoder = NULL;
    }

    if (metadata[0] != NULL) {
      ::FLAC__metadata_object_delete(metadata[0]);
      metadata[0] = NULL;
    }
    if (metadata[1] != NULL) {
      ::FLAC__metadata_object_delete(metadata[1]);
      metadata[1] = NULL;
    }
    if (fd != -1) {
      ::close(fd);
      fd = -1;
    }

    return(0);
  }


};


class ControlObject : public HAObject {

private:

    bool valid;

    ::pthread_mutex_t Started_mutex;
    bool Started;

    ::pthread_mutex_t Being_Stopped_mutex;
    bool BeingStopped;

    ::pthread_mutex_t Start_Stop_mutex;

    ::pthread_mutex_t RaspLc_Stop_mutex;
    bool DeviceAvailable;

    ::pthread_mutex_t PortASockets_mutex;
    ::pthread_mutex_t PortBSockets_mutex;
    ::pthread_mutex_t PortCSockets_mutex;
    ::pthread_mutex_t PortDSockets_mutex;
    ::pthread_mutex_t PortESockets_mutex;
    ::pthread_mutex_t PortFSockets_mutex;
    std::vector<int> PortASockets;
    std::vector<int> PortBSockets;
    std::vector<int> PortCSockets;
    std::vector<int> PortDSockets;
    std::vector<int> PortESockets;
    std::vector<int> PortFSockets;

    /* FLAC mic begin */

    ::pthread_mutex_t PortA_FLAC_Sockets_mutex;
    std::vector<int> PortA_FLAC_Sockets;

    ::pthread_mutex_t PortA_FLAC_DATA_Sockets_mutex;
    std::vector<flac_mic_data> PortA_FLAC_DATA_Sockets[HA_PORT_A_FLAC_DATA_NUM];

    unsigned int flac_blocksize;

    unsigned int flac_mic_channel;
    unsigned int flac_mic_port[HA_PORT_A_FLAC_DATA_NUM];
    unsigned int flac_mic_port_use;
    const unsigned int flac_mic_max_channel;
 public:
    unsigned int flac_mic_get_port_u(const unsigned int port) const {
      if (port < HA_PORT_A_FLAC_DATA_NUM) {
	return(flac_mic_port[port]);
      } else {
	return(0);
      }
    }
 private:
    int flac_mic_set_channel(const unsigned int channel) {
      if (channel <= flac_mic_max_channel) {

	flac_mic_channel = channel;
	std::fill(&flac_mic_port[0], &flac_mic_port[HA_PORT_A_FLAC_DATA_NUM], 8);
	if (channel % 8) {
	  flac_mic_port[channel / 8] = (channel % 8);
	}
	flac_mic_port_use = (channel + 7) / 8;

	return(0);
      } else {
	std::cerr << __func__ << " channel not set" << std::endl;
	return(-1);
      }
    }

    int  flac_mic_max_lpc_order;
    int  flac_mic_rice_partition_order;
 public:
    int SetFlacParamBlocksize(const int bs) {
      flac_blocksize = bs;
      return(0);
    }
    int GetFlacParamBlocksize() const {
      return(flac_blocksize);
    }
    int SetFlacParamMicMaxLpcOrder(const int mlo) {
      flac_mic_max_lpc_order = mlo;
      return(0);
    }
    int GetFlacParamMicMaxLpcOrder() const {
      return(flac_mic_max_lpc_order);
    }
    int SetFlacParamMicRicePartitionOrder(const int rpo) {
      flac_mic_rice_partition_order = rpo;
      return(0);
    }

    int GetFlacParamMicRicePartitionOrder() const {
      return(flac_mic_rice_partition_order);
    }

 private:

    /* FLAC mic end */

    /* FLAC Sep begin */

    std::string flac_sep_host;
    int flac_sep_sock_fd;
    std::map<int, flac_sep_data> flac_sep_map;

    int flac_sep_connect(const std::string& flac_sep_host, int& fd, const int port);

    int flac_sep_h_connect();
    int flac_sep_h_close();

    int flac_sep_d_connect(int& fd);

    int flac_connect_with_timeout(const int socket, 
				  const struct sockaddr *address, 
				  const socklen_t address_len, 
				  const int timeout_ms);

    int flac_sep_register(const int num, const ::HDH_SrcInfo *srcinfo);
    int flac_sep_erase(const int num, const ::HDH_SrcInfo *srcinfo);

    int flac_sep_clear();

    int  flac_sep_max_lpc_order;
    int  flac_sep_rice_partition_order;
 public:

    int SetFlacParamSepHostname(const char *const hname) {
      flac_sep_host = static_cast<std::string>(hname);
      return(0);
    }
    int SetFlacParamSepMaxLpcOrder(const int mlo) {
      flac_sep_max_lpc_order = mlo;
      return(0);
    }
    int SetFlacParamSepRicePartitionOrder(const int rpo) {
      flac_sep_rice_partition_order = rpo;
      return(0);
    }
 private:
    /* FLAC Sep end */

    // for dynamic reconfiguration by network commands
    ::pthread_mutex_t CommandPortSockets_mutex;
    ::pthread_mutex_t ImportParamPortSockets_mutex;
    ::pthread_mutex_t ExportParamPortSockets_mutex;
    std::vector<int> CommandPortSockets;
    std::vector<int> ImportParamPortSockets;
    std::vector<int> ExportParamPortSockets;
    int UpdateRequestType;
      // 0x0000:no request
      // 0x0001:dynamic parameter only              (no need stop)
      // 0x0002:include static parameter postponed  (need stop but will be apply next time restart)
      // 0x0006:include static parameter force      (need stop and will be restart to force for apply)
      // check mask guide : 0x0001 before start each frame, 0x0002 before start hark, 0x0004 restart request
    int UseDynamicReconf;   // 0:don't accept the dynamic reconfiguration. (for need fast calculation) 255:all
    int EffectReconfFrame;  // Frame number the effect of dynamic reconfiguration are applied. Measures of multi-threaded.

    std::map<int, int> ServerSockets;

    //MULTI_FFT_INFO_t *fft_info;

    int RawCount;
    int FftCount;
    int LmCount;
    int GHDSSCount;
    int OutECount;

    void ResetCount() {
        RawCount = 0;
        FftCount = 0;
        LmCount = 0;
        GHDSSCount = 0;
        OutECount = 0;
        ls = 0;
    }

    int MicWavFileHeader[11];
    int SepWavFileHeader[11];

    // Mode Flags
    int AndroidCompatible; // If AndroidCompatible is true, will read the same file again and again as File had never terminated if when it detects the end of file.
    int TerminateInputFile;
    int MicInMultiGain;
    int UseReadWavFile;
    int SendPort;
    int UseTimeStamp;
    int LocalizeAlgorithm;
    int UseSeparation;
    int UseFilter;
    int UseSynthesize;
    int UseSourceFilter;
    int UseCalcFeature;
    int UseZeroCross;
    int UseSID;
    int SaveMicSpec;
    int SaveMicRaw;
    int SaveLocPowSpec;
    int SaveSepSpec;
    int SaveSepRaw;
    int SaveSepFeature;
    int SaveFeatureType;
    int SaveSource;

    int SelectChannel;

    /* ユーザパラメータ管理用変数 */
    ::Param_AudioStreamFromMic_t UserParamConfigAudio;
    ::Param_LocalizeMUSIC_t UserParamConfigLocalizeMusic;
    ::Param_ConstantLocalization_t UserParamConstantLocalization;
    //::Param_ConstantLocalization_t UserParamConstantLocalizationFilter;
    ::Param_SourceTracker_t UserParamConfigSourceTracker;
    ::Param_SourceIntervalExtender_t UserParamConfigSourceIntervalExtender;
    ::Param_EstimateLeak_t UserParamConfigEstimateLeak;
    ::Param_HRLE_t UserParamConfigHrle;
    ::Param_WhiteNoiseAdder_t UserParamConfigWhiteNoiseAdder;
    ::Param_PreEmphasis_t UserParamConfigPreEmphasis;
    ::Param_MelFilterBank_t UserParamConfigMelFilterBank;

    char UserParamConfigGhdss_sep_tf_file_name[ARY_PATH_LENGTH];

    char UserParamConfigInput_pcm_file_name[ARY_PATH_LENGTH];
    char UserParamConfigSave_input_pcm_file_name[ARY_PATH_LENGTH];
    char UserParamConfigSave_output_pow_spec_file_name[ARY_PATH_LENGTH];
    char UserParamConfigSave_output_feature_file_name[ARY_PATH_LENGTH];
    char UserParamConfigSave_output_pcm_file_name[ARY_PATH_LENGTH];
    char UserParamConfigSave_output_pcm_tmp_file_name[ARY_PATH_LENGTH];
    char UserParamConfigSave_output_source_file_name[ARY_PATH_LENGTH];

    ::Param_ExtendedSourceTracker_t UserParamConfigExtendedSourceTracker;
    ::Param_NormalizeMUSIC_t UserParamConfigNormalizeMusic;
    ::Param_BF_t UserParamConfigBeamforming;

    char UserParamConfigBeamforming_sep_tf_file_name[ARY_PATH_LENGTH];
    
    int UserParamMicChannelOffset;
    int UserParamBypassOutE;

//  static const int WorkerThreadNum = 3; /* PS-EYE */
//  static const int WorkerThreadNum = 10; /* original of embeddedhark_20130313 */
//  static const int WorkerThreadNum = 6;
//  static const int WorkerThreadNum = 5;
//  static const int WorkerThreadNum = 4;
//  static const int WorkerThreadNum = 2;
//  static const int WorkerThreadNum = 1;
  static const int WorkerThreadNum = 3; // HARK-CPU

    pthread_t thread_id[WorkerThreadNum];

    EventFlag launch[WorkerThreadNum];

    //EventFlag ParamGo[WorkerThreadNum];
    //EventFlag ParamRecvGo[WorkerThreadNum];
    EventFlag RawGo[WorkerThreadNum];
    EventFlag RawSendGo[WorkerThreadNum];
    EventFlag FftGo[WorkerThreadNum];
    EventFlag FftSendGo[WorkerThreadNum];
    EventFlag LmGo[WorkerThreadNum];
    EventFlag LmSendGo[WorkerThreadNum];
    EventFlag GHDSSGo[WorkerThreadNum];
    EventFlag GHDSSSendGo[WorkerThreadNum];
    EventFlag OutEGo[WorkerThreadNum];
    EventFlag OutESendGo[WorkerThreadNum];

    void ResetEventFlag() {
        //ParamGo[0].Set();
        //for(int i = 1; i < WorkerThreadNum; i++){
        //    ParamGo[i].Reset();
        //}
        //ParamRecvGo[0].Set();
        //for(int i = 1; i < WorkerThreadNum; i++){
        //    ParamRecvGo[i].Reset();
        //}
        RawGo[0].Set();
        for(int i = 1; i < WorkerThreadNum; i++){
            RawGo[i].Reset();
        }
        RawSendGo[0].Set();
        for(int i = 1; i < WorkerThreadNum; i++){
            RawSendGo[i].Reset();
        }
        FftGo[0].Set();
        for(int i = 1; i < WorkerThreadNum; i++){
            FftGo[i].Reset();
        }
        FftSendGo[0].Set();
        for(int i = 1; i < WorkerThreadNum; i++){
            FftSendGo[i].Reset();
        }
        LmGo[0].Set();
        for(int i = 1; i < WorkerThreadNum; i++){
            LmGo[i].Reset();
        }
        LmSendGo[0].Set();
        for(int i = 1; i < WorkerThreadNum; i++){
            LmSendGo[i].Reset();
        }
        GHDSSGo[0].Set();
        for(int i = 1; i < WorkerThreadNum; i++){
            GHDSSGo[i].Reset();
        }
        GHDSSSendGo[0].Set();
        for(int i = 1; i < WorkerThreadNum; i++){
            GHDSSSendGo[i].Reset();
        }
        OutEGo[0].Set();
        for(int i = 1; i < WorkerThreadNum; i++){
            OutEGo[i].Reset();
        }
        OutESendGo[0].Set();
        for(int i = 1; i < WorkerThreadNum; i++){
            OutESendGo[i].Reset();
        }
    }
    //
    //
    //
    //int WaitParam(const int thread_no) {
    //    return(ParamGo[thread_no].Wait());
    //}
    //int GoNextParam(const int thread_no) {
    //    return(ParamGo[GetNextGo(thread_no)].Set());
    //}
    //
    //int WaitRecvParam(const int thread_no) {
    //    return(ParamRecvGo[thread_no].Wait());
    //}
    //int GoNextRecvParam(const int thread_no) {
    //    return(ParamRecvGo[GetNextGo(thread_no)].Set());
    //}
    //
    //
    //
    int WaitRaw(const int thread_no) {
        return(RawGo[thread_no].Wait());
    }
    int GoNextRaw(const int thread_no) {
        return(RawGo[GetNextGo(thread_no)].Set());
    }

    int WaitSendRaw(const int thread_no) {
        return(RawSendGo[thread_no].Wait());
    }
    int GoNextSendRaw(const int thread_no) {
        return(RawSendGo[GetNextGo(thread_no)].Set());
    }
    //
    //
    //
    int WaitFft(const int thread_no) {
        return(FftGo[thread_no].Wait());
    }
    int GoNextFft(const int thread_no) {
        return(FftGo[GetNextGo(thread_no)].Set());
    }

    int WaitSendFft(const int thread_no) {
        return(FftSendGo[thread_no].Wait());
    }
    int GoNextSendFft(const int thread_no) {
        return(FftSendGo[GetNextGo(thread_no)].Set());
    }
    //
    //
    //
    int WaitLm(const int thread_no) {
        return(LmGo[thread_no].Wait());
    }
    int GoNextLm(const int thread_no) {
        return(LmGo[GetNextGo(thread_no)].Set());
    }

    int WaitSendLm(const int thread_no) {
        return(LmSendGo[thread_no].Wait());
    }
    int GoNextSendLm(const int thread_no) {
        return(LmSendGo[GetNextGo(thread_no)].Set());
    }
    //
    //
    //
    int WaitGHDSS(const int thread_no) {
        return(GHDSSGo[thread_no].Wait());
    }
    int GoNextGHDSS(const int thread_no) {
        return(GHDSSGo[GetNextGo(thread_no)].Set());
    }

    int WaitSendGHDSS(const int thread_no) {
        return(GHDSSSendGo[thread_no].Wait());
    }
    int GoNextSendGHDSS(const int thread_no) {
        return(GHDSSSendGo[GetNextGo(thread_no)].Set());
    }
    //
    //
    //
    int WaitOutE(const int thread_no) {
        return(OutEGo[thread_no].Wait());
    }
    int GoNextOutE(const int thread_no) {
        return(OutEGo[GetNextGo(thread_no)].Set());
    }

    int WaitSendOutE(const int thread_no) {
        return(OutESendGo[thread_no].Wait());
    }
    int GoNextSendOutE(const int thread_no) {
        return(OutESendGo[GetNextGo(thread_no)].Set());
    }

    int GetNextGo(const int thread_no) const {
        int ret = 0;

        if (WorkerThreadNum == 1) {
            return(ret);
        } else {
            if ((0 <= thread_no) && (thread_no < (WorkerThreadNum - 1))) {
                ret = thread_no + 1;
            } else if (thread_no == (WorkerThreadNum - 1)){
                ret = 0;
            } else {
                ret = -1;
            }
        }

        return(ret);
    }

    int init();

    int SendPortA(const void *addr, const size_t len) const;
    int SendPortA(const ::HD_Header *hdr, const ::HDH_MicData *mic, const float *data);
    int ClosePortA();

    int SendPortB(const void *addr, const size_t len) const;
    int SendPortB(const ::HD_Header *hdr, const ::HDH_MicData *mic, const float *data);
    int ClosePortB();

    int SendPortC(const void *addr, const size_t len) const;
    int SendPortCwithTS(const ::HD_Header *hdr, const int num, const ::HDH_SrcInfo *srcinfo, char* ts);
    int SendPortC(const ::HD_Header *hdr, const int num, const ::HDH_SrcInfo *srcinfo);
    int ClosePortC();

    int SendPortD(const void *addr, const size_t len) const;
    int SendPortD(const ::HD_Header *hdr, const int num, const ::HDH_SrcInfo *srcinfo, const ::HDH_SrcData *srcdata, const float *data);
    int ClosePortD();

    int SendPortE(const void *addr, const size_t len) const;
    int SendPortE(const ::HD_Header *hdr, const int num, const ::HDH_SrcInfo *srcinfo, const ::HDH_SrcData *srcdata, const float data[][ARY_NUM_FBANK_DELTA_POWER]);
    int ClosePortE();

    int SendPortF(const void *addr, const size_t len) const;
    int SendPortF(const ::HD_Header *hdr, const int num, const ::HDH_SrcInfo *srcinfo, const ::HDH_SrcData *srcdata, const float *data);
    int ClosePortF();

    int SendPortA_flac(const void *addr, const size_t len) const;
    int SendPortA_flac(const ::HD_Header *hdr, const ::HDH_MicData *mic, const float *data);
    int ClosePortA_flac();

    int SendPortF_flac_h(const void *addr, const size_t len);
    int SendPortF_flac(const ::HD_Header *hdr, const int num, const ::HDH_SrcInfo *srcinfo, const ::HDH_SrcData *srcdata, const float *data);
    int ClosePortF_flac();

    //int CommandPort(const void *addr, const size_t len) const;
    //int CommandPort(const ::HD_Header *hdr, const int num, const ::HDH_SrcInfo *srcinfo, const ::HDH_SrcData *srcdata, const float *data);
    //int CloseCommandPort();

    //int ImportParamPort(const void *addr, const size_t len) const;
    //int ImportParamPort(const ::HD_Header *hdr, const int num, const ::HDH_SrcInfo *srcinfo, const ::HDH_SrcData *srcdata, const float *data);
    //int CloseImportParamPort();

    //int ExportParamPort(const void *addr, const size_t len) const;
    //int ExportParamPort(const ::HD_Header *hdr, const int num, const ::HDH_SrcInfo *srcinfo, const ::HDH_SrcData *srcdata, const float *data);
    //int CloseExportParamPort();

    // for debug
	int ls;
	int input_raw_fd;
	FILE *input_raw_fp;
	FILE *source_location_fp; //miyazaki

    int num_src_pre[WorkerThreadNum];
    SOURCE_DATA_ARY_t src_data_pre[WorkerThreadNum];

    HA_Float wavedata[ARY_NUM_SRC][ARY_NUM_WAVE]; // for overlapping-add for synthesized data
    HA_Float tcp_data[ARY_NUM_SRC][ARY_NUM_WAVE]; // 

    int writerawdata(HA_Float write_data[ARY_NUM_SRC][ARY_NUM_WAVE], int index, int id, int flag);
    int finalizerawdata(int index, int id);
    int checkterminate(int pre_id, int src_num, const SOURCE_DATA_ARY_t &ary);
    int checkid(SOURCE_DATA_ARY_t *pre_src, int num, int id);

    int initSaveSourceLocation(void);
    int SaveSourceLocation(int  num_src, SOURCE_DATA_ARY_t *sources);
    void finalizeSaveSourceLocation();

    void init_vars() {
        ResetCount();
        for(int i = 0; i < WorkerThreadNum; i++) {
            num_src_pre[i] = 0;
        }
        for(int i = 0; i < WorkerThreadNum; i++) {
            src_data_pre[i].source_num = 0;
            src_data_pre[i].count      = 0;
        }
    }

public:

    static const int NUM_FEATURE = 8;

    void set_jiliusMFThostname(const char *const hostname) {
        juliusMFThostname = hostname;
    }

    void set_SocketMic(const bool connect, const char *const hostname) {
        SocketMicConnected = connect;
        SocketMicHostname  = hostname;
    }
#ifdef ENABLE_JNI_FOR_ANDROID
	void set_USBMicDeviceType(int device_type){
	    MicDevice = device_type;
	}
	void set_USBMicDevice(int channels, int bits){
	    MicChannels = channels;
	    MicBits     = bits;
	}
	void set_USBMicInterface(int if_no, int ep_addr, int max_packet_size){
	    USBMicIfNo          = if_no;
	    USBMicEpAddr        = ep_addr;
	    USBMicMaxPacketSize = max_packet_size;
	}
	void set_USBMicFd(int fd){
		USBMicFd = fd;
	}
#endif

// Legacy code for refer
/*
    void set_RaspZX(const bool connect, const char *const hostname) {
        RaspZXConnect = connect;
        RaspZXhostname = hostname;
    }
#ifdef ENABLE_JNI_FOR_ANDROID
	void set_RaspLCDevice(int channels, int bits){
	    RaspLCChannels = channels;
	    RaspLCBits     = bits;
	}
	void set_RaspLCInterface(int if_no, int ep_addr, int max_packet_size){
	    RaspLCIfNo          = if_no;
	    RaspLCEpAddr        = ep_addr;
	    RaspLCMaxPacketSize = max_packet_size;
	}
	void set_RaspLCFd(int fd){
		RaspLCFd = fd;
	}
#endif
*/
// Legacy code for refer

private:

    // for Socket Microphone
    bool        SocketMicConnected;
    std::string SocketMicHostname;

#ifdef ENABLE_JNI_FOR_ANDROID
    // for USB Microphone
    int MicDevice;
    int MicChannels;
    int MicBits;
    int USBMicIfNo;
    int USBMicEpAddr;
    int USBMicMaxPacketSize;
    int USBMicFd;
#endif

// Legacy code for refer
/*
    // for RaspZX
    bool RaspZXConnect;
    std::string RaspZXhostname;
    
#ifdef ENABLE_JNI_FOR_ANDROID
    // for RaspLC
    int RaspLCChannels;
    int RaspLCBits;
    int RaspLCIfNo;
    int RaspLCEpAddr;
    int RaspLCMaxPacketSize;
    int RaspLCFd;
#endif
*/
// Legacy code for refer

    // for julius

    std::string juliusMFThostname;
    typedef std::vector<feature> vec_feature;

    std::map<int, SourceInfo> source_info;
    std::map<int, vec_feature> features;

    void init_julius() {
        clear_features_src_info();
    }

    void clear_features_src_info() {
        source_info.clear();
        features.clear();
    }

    void erase_features_src_info(const std::vector<int> &ids) {
        for(std::vector<int>::const_iterator iter = ids.begin(); iter != ids.end(); iter++) {
            const int index = *iter;
            source_info.erase(index);
            features.erase(index);
        }
    }

    void register_feature_src_info(const int src_id, const feature &f, const float azimuth, const float elevation) {
    
        features[src_id].push_back(f);

        SourceInfo s;
        s.source_id = src_id;
        s.azimuth   = azimuth;
        s.elevation = elevation;
        ::gettimeofday(&s.time, NULL);

        source_info[src_id] = s;
    }

#define ENABLE_BLUETOOTH
//#undef ENABLE_BLUETOOTH

#ifdef ENABLE_BLUETOOTH
private:

    bool BluetoothTransfer;
    //::pthread_mutex_t bt_mutex;
    sem_t bt_max_sem;
    float BluetoothMinRad;
    float BluetoothMaxRad;
    std::map<int, int> bt_sockets;
    int bt_current_id;
    HA_Float bt_wavedata[ARY_NUM_SRC][ARY_NUM_WAVE]; // for overlapping-add for synthesized data

    int connect_bt_service(const int id);
    int write_bt_service(const int id, void *abuf, const ssize_t wsize);
    int disconnect_bt_service(const int id);
    int bt_writerawdata(const HA_Float write_data[ARY_NUM_SRC][ARY_NUM_WAVE], const int index, const int id, const float x, const float y, const int flag);
    int bt_finalizerawdata(const int index, const int id);
    int init_bluetooth();
    int bt_set_id(const int id) {
      bt_current_id = id;
      return(bt_current_id);
    }
    bool bt_check_id(const int id) const {
      return((id == bt_current_id) ? true : false);
    }
    bool bt_check_range(const float x, const float y) const {

      const float rad = ::atan2f(y, x);

      if ((BluetoothMinRad <= rad) && (rad <= BluetoothMaxRad)) {
	return(true);
      } else {
	return(false);
      }

    }

public:
    // Bluetooth
    int SetBluetoothParamMicEnable(const int enable) {
      BluetoothTransfer = ((enable == 0) ? false : true);
      return(0);
    }
    int SetBluetoothParamMinDeg(const int deg) {
      BluetoothMinRad = deg * M_PI / 180.0;
      return(0);
    }
    int SetBluetoothParamMaxDeg(const int deg) {
      BluetoothMaxRad = deg * M_PI / 180.0;
      return(0);
    }

#endif	// ENABLE_BLUETOOTH

public:

    ControlObject(const ControlObject &);
    ControlObject();
    virtual ~ControlObject();
    ControlObject &operator = (const ControlObject &);

    bool isValid() const {return(valid);}

    int SetStarted();
    int ResetStarted();
    bool isStarted();

    int SetBeingStopped();
    int ResetBeingStopped();
    bool isBeingStopped();

    int AddSocket(const int port, const int fd);
    int RemoveSocket(const int port, const int fd);

    int AddServerSocket(const int port, const int fd);
    int CloseServer();

    int AddFLACMicDataSocket(const flac_mic_data &fdata);
    
    void launched(const int thread_no) {launch[thread_no].Set();};

#ifdef ENABLE_JNI_FOR_ANDROID
    int Create(JNIEnv *env, jobject thiz);
    int Start(JNIEnv *env, jobject thiz);
    int Stop();

    int SetParam(JNIEnv *env, jobject thiz);
    int GetParam(JNIEnv *env, jobject thiz);
#else
    int Create();
    int Start();
    int Stop();

    int SetParam();
    int GetParam();
#endif // ENABLE_JNI_FOR_ANDROID

    //int Raw(const int thread_no, const bool debug, float **data, char* ts);
    int Raw(const int thread_no, float **data, char* ts);
    int GetRawCount() const {return(RawCount);}

    //int RaspLc_Stop();

    MULTI_FFT_INFO_t *init_fft(const int thread_no);

    int Fft(const int thread_no, MULTI_FFT_INFO_t *fft_info, float **data, COMPLEX_FLOAT **fftdata, char* ts);
    int GetFftCount() const {return(FftCount);}

    int init_lm(const bool hw_avail);
    int Lm(const int thread_no, COMPLEX_FLOAT **fftdata, SOURCE_DATA_ARY_t &ary, SOURCE_DATA_ARY_t &filter_ary, bool &valid_source, char* ts);
    int GetLmCount() const {return(LmCount);}

    //int init_GHDSS();
    int init_GHDSS(const bool hw_avail);
    int GHDSS(const int thread_no, /*const */SOURCE_DATA_ARY_t &ary, const SOURCE_DATA_ARY_t &filter_ary, const bool valid_source, ::HA_Complex SpectralGainFilter_outputSpec[][ARY_NUM_FFT], char* ts);
    int GetGHDSSCount() const {return(GHDSSCount);}

    int init_OutE();
    int OutE(const int thread_no, const SOURCE_DATA_ARY_t &ary, const bool valid_source, ::HA_Complex SpectralGainFilter_outputSpec[][ARY_NUM_FFT]);
    int GetOutECount() const {return(OutECount);}

    int SetLocalizeMusic(int cmd_param_id, int i_value, float f_value, char* file_path);
    int SetConstantLocalization(int cmd_param_id, int i_value, float* f_ar_value/*, char* file_path*/);
    int SetConstantLocalizationFilter(int cmd_param_id, int i_value, float* f_ar_value/*, char* file_path*/);

    inline void setFullPath(char *s1, const char *s2);

    // 2013.07.09, Add comments and new functions by m.takigahira at 2013.09.13
    // Please refer to HA_params.h and ControlObject constructor in HA_ControlObject.cpp
    int SetParamSendPort(int send_port){
        SendPort = send_port;
        return 0;
    }
    int SetParamUseTimeStampPort(int use_time_stamp_port){
        UseTimeStamp = use_time_stamp_port; // use only for AudioVisualizer now
        return 0;
    }
    int SetParamUseSourceFilterPort(int use_source_filter_port){
        UseSourceFilter = use_source_filter_port; // use only for LOC_MUSIC_FILETR or LOC_NMUSIC_FILETR
        return 0;
    }
    int SetParamSaveMicRawType(int save_type_mic_pcm){
        SaveMicRaw = save_type_mic_pcm;
        return 0;
    }
    int SetParamSaveMicSpecType(int save_type_mic_spec){
        SaveMicSpec = save_type_mic_spec;
        return 0;
    }
    int SetParamSaveLocalizePowerSpecType(int save_type_localize_power_spec){
        SaveLocPowSpec = save_type_localize_power_spec;
        return 0;
    }
    int SetParamSaveSepRawType(int save_type_sep_pcm){
        SaveSepRaw = save_type_sep_pcm;
        return 0;
    }
    int SetParamSaveSepSpecType(int save_type_sep_spec){
        SaveSepSpec = save_type_sep_spec;
        return 0;
    }
    int SetParamSaveSepFeatureType(int save_type_sep_feature, int save_type_sep_feature_format){
        SaveSepFeature = save_type_sep_feature;
        SaveFeatureType = save_type_sep_feature_format;
        return 0;
    }
    int SetParamSaveSourceType(int save_type_source){
        SaveSource = save_type_source;
        return 0;
    }
    int SetParamSwitchLocalizeAlgorithm(int selected_localize_algorithm){
        LocalizeAlgorithm = selected_localize_algorithm;
        return 0;
    }
    int SetParamSwitchSeparationAlgorithm(int selected_separation_algorithm){
        UseSeparation = selected_separation_algorithm;
        return 0;
    }
    int SetParamSwitchFilterAlgorithm(int selected_filter_algorithm){
        UseFilter = selected_filter_algorithm;
        return 0;
    }
    int SetParamSwitchSIDAlgorithm(int selected_sid_algorithm){
        UseSID = selected_sid_algorithm;
        return 0;
    }
    int SetNoneSeparationParamTargetChannel(int target_channel){
        SelectChannel = target_channel;
        return 0;
    };

    int SetAudioParam(int input_channels, int advance)
    {
        //LOGD("ch = %d, advance = %d", input_channels, advance);
        UserParamConfigAudio.logical_channel_count = input_channels;
        UserParamConfigAudio.advance = advance;

        return 0;
    }

    // K.Takagi
    int SetLocalizeMusicParamNumSource(int num_source);
    int SetLocalizeMusicParamPeriod(int period);
    int SetLocalizeMusicParamFrequency(int low_bound_freq, int upper_bound_freq);
    int SetLocalizeMusicParamDeg(int min_deg, int max_deg);
    int SetLocalizeMusicParamFilePath(const char *file_name);

    int SetSourceTrackerParamThresh(float thresh);
    int SetSourceTrackerParamPauseLength(float pause_length);
    int SetSourceTrackerParamMinSrcInterval(float min_src_interval);

    int SetSourceIntervalExtenderParam(int preroll_length);

    int SetGhdssParamFilePath(const char *file_name);

    int SetEstimateLeakParamFactor(float factor);

    int SetHrleParamLx(float lx);
    int SetHrleParamTr(float tr);
    int SetHrleParamMinLv(float min_lv);
    int SetHrleParamStepLv(float step_lv);
    int SetHrleParamNumBin(int num_bin);

    int SetWhiteNoiseAdderParamLevel(float wn_level);

    int SetPreEmphasisParamCutoff(int cut_off);
    int SetPreEmphasisParamCoef(float coef);

    int SetMelFilterParamCutoff(int cut_off);
    int SetMelFilterParamFreq(int min_freq, int max_freq);

//takigahira
    int SetInputParamPCMFilePath(const char *file_name);
    int SetOutputParamMicPCMFilePath(const char *file_name);
    int SetOutputParamPowSpecFilePath(const char *file_name);
    int SetOutputParamSepPCMFilePath(const char *file_name);
    int SetOutputParamSepPCMTMPFilePath(const char *file_name);
    int SetOutputParamSepFeatureFilePath(const char *file_name);
    int SetOutputParamSourceFilePath(const char *file_name);
    
    int SetExtendedSourceTrackerParamThresh(float thresh);
    int SetExtendedSourceTrackerParamPauseLength(int pause_length);
    int SetExtendedSourceTrackerParamRemoveLength(int remove_length);
    int SetExtendedSourceTrackerParamRemoveCheckLength(int remove_check_length);
    int SetExtendedSourceTrackerParamCompareMode(int compare_mode);
    int SetExtendedSourceTrackerParamMinSrcInterval(float min_src_interval);
    int SetExtendedSourceTrackerParamMinTFIndexInterval(int min_tfindex_interval);
    int SetExtendedSourceTrackerParamMaxNumSource(int max_num_source);
    int SetExtendedSourceTrackerParamPrerollLength(int preroll_length);
    int SetExtendedSourceTrackerParamRemovedSourceAlgorithm(int removed_source_algorithm);
    int SetExtendedSourceTrackerParamMinID(int min_id);
    int SetNormalizeMusicParamInitialThreshold(float initial_threshold);
    int SetNormalizeMusicParamActiveProp(float active_prop);
    int SetNormalizeMusicParamUpdateInterval(int update_interval);
    int SetNormalizeMusicParamPriorWeight(float prior_weight);
    int SetNormalizeMusicParamMaxSourceNum(int max_source_num);
    int SetNormalizeMusicParamParticleNum(int particle_num);
    int SetNormalizeMusicParamLocalPeakMargin(float local_peak_margin);
    int SetBeamformingParamBFMethod(int bf_method);
    int SetBeamformingParamTFConjFilePath(const char *file_name); //tf_conj_filename
    int SetBeamformingParamLCMethod(int lc_method);
    int SetBeamformingParamLCMyu(float lc_myu);
    int SetBeamformingParamSSMethod(int ss_method);
    int SetBeamformingParamSSMyu(float ss_myu);
    int SetBeamformingParamSSScale(float ss_scal);
    int SetBeamformingParamNlFunc(int nl_func);
    int SetBeamformingParamDecompositionAlgorithm(int decomposition_algorithm);
    int SetBeamformingParamAlpha(float alpha);
    int SetBeamformingParamRegFactor(float reg_factor);
    
    //
    //
    //
    //static const ::Param_AudioStreamFromMic_t audio_info;
    static ::Param_AudioStreamFromMic_t audio_info;
    std::vector<floatx8> advanceBuffer;
    float **audio_pre_data;
    
    DeviceObject      *selectedDeviceObj;
    SocketDeviceObject socketMicObj;
    USBDeviceObject    usbMicObj;
    WavFileObject      wavFileObj;
    BufferObject       bufferObj;

// Legacy code for refer
/*
    RaspLCObject raspLCObj;
    RaspZXObject raspZXObj;
*/
// Legacy code for refer

#ifdef ENABLE_RASPMX
    RaspMXObject raspMXObj;
#endif

    void SetParamMicChannelOffset(int offset)
    {
        UserParamMicChannelOffset = offset;
    }
    void SetParamBypassOutE(int flag)
    {
        UserParamBypassOutE = flag & 0x1;
    }
    int GetParamBypassOutE()
    {
        return UserParamBypassOutE;
    }
    //
    //
    //
    void setParam(const ::Param_LocalizeMUSIC_t &arg) {
        UserParamConfigLocalizeMusic = arg;
    }
    void setParam(const ::Param_SourceTracker_t &arg) {
        UserParamConfigSourceTracker = arg;
    }
    void setParam(const ::Param_SourceIntervalExtender_t &arg) {
        UserParamConfigSourceIntervalExtender = arg;
    }
    void setParam(const ::Param_EstimateLeak_t &arg) {
        UserParamConfigEstimateLeak = arg;
    }
    void setParam(const ::Param_HRLE_t &arg) {
        UserParamConfigHrle = arg;
    }
    void setParam(const ::Param_WhiteNoiseAdder_t &arg) {
        UserParamConfigWhiteNoiseAdder = arg;
    }
    void setParam(const ::Param_PreEmphasis_t &arg) {
        UserParamConfigPreEmphasis = arg;
    }
    void setParam(const ::Param_MelFilterBank_t &arg) {
        UserParamConfigMelFilterBank = arg;
    }
    

    //
    //
private:
    std::string harkPath;

    /* reentrantではないので注意 */
    char *getFilenameWithHarkPath(const char * const filename)
    {
    	static char fullPath[512];
    	int len;

    	if(harkPath.empty())
    	{
    		strcpy(fullPath, "/mnt/sdcard/hark/");
    	}
    	else
    	{
    		strcpy(fullPath, harkPath.c_str());
    	}

    	len = strlen(fullPath);
    	if(fullPath[len-1] != '/')
    	{
    		strcat(fullPath, "/");
    	}
    	strcat(fullPath, filename);

    	return fullPath;
    }

public:
    void setHarkPath(const char *const path)
    {
    	harkPath = path;

    	LOGD("setHarkPath : %s", harkPath.c_str());
    }

    const int dummy_end;

};

extern HARKAndroid::ControlObject *HARKControl;

}

#endif // __cplusplus

#ifdef __cplusplus
extern "C" {
#endif


#ifdef __cplusplus
}
#endif


#endif /* HA_CONTROLOBJECT_H_ */
