// file: $isip/class/algo/Mask/mask_05.cc
// version: $Id: mask_05.cc 8037 2002-04-01 20:04:33Z gao $
//

// isip include files
//
#include "Mask.h"

// method: apply
//
// arguments:
//  Vector<AlgorithmData>& output: (output) output data
//  const Vector< CircularBuffer<AlgorithmData> >& input: (input) input data
//
// return: a boolean value indicating status
//
// this method calls the appropriate computation methods
//
boolean Mask::apply(Vector<AlgorithmData>& output_a,
		    const Vector< CircularBuffer<AlgorithmData> >& input_a) {

  // set the length to the number of channels input
  //
  long len = input_a.length();
  output_a.setLength(len);

  // accumulate a result status -- if compute method returns false
  // this apply method should return false
  //  
  boolean res = true;

  // start the debugging output
  //
  displayStart(this);
  
  // loop over the channels and call the compute method
  //
  for (long c = 0; c < len; c++) {

    // display the channel number
    //
    displayChannel(c);
    
    // call AlgorithmData::makeVectorFloat to force the output vector for
    // this channel to be a VectorFloat, call AlgorithmData::getVectorFloat
    // on the input for this channel to check that the input is
    // already a VectorFloat and return that vector.
    //
    if (input_a(c)(0).getDataType() == AlgorithmData::VECTOR_FLOAT) {

      res &= compute(output_a(c).makeVectorFloat(),
		     input_a(c)(0).getVectorFloat());

    }
    
    else if (input_a(c)(0).getDataType() == AlgorithmData::VECTOR_COMPLEX_FLOAT) {
      
      res &= compute(output_a(c).makeVectorComplexFloat(),
		     input_a(c)(0).getVectorComplexFloat());

    }
    else {
      return Error::handle(name(), L"apply", ERR_UNSUPA, __FILE__, __LINE__);
    }
    
      
    // set the coefficient type for the output
    //    
    output_a(c).setCoefType(input_a(c)(0).getCoefType());
  }

  // finish the debugging output
  //
  displayFinish(this);  

  // exit gracefully
  //
  return res;
}

// method: compute
//
// arguments:
//  VectorFloat& output: (output) operand
//  const VectorFloat& input: (input) operand
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long index: (input) channel index
//
// return: a boolean value indicating status
//
// this method takes the mask of the single-channel input data
// according to specified algorithm and implementation
//
boolean Mask::compute(VectorFloat& output_a, const VectorFloat& input_a,
		      AlgorithmData::COEF_TYPE input_coef_type_a,
		      long index_a) {

  // local variable
  //
  boolean status = false;
  
  // branch on algorithm:
  //  algorithm: SELECT
  //
  if (algorithm_d == SELECT) {

    // branch on implementation:
    //  Implementation: INDEX
    //
    if (implementation_d == INDEX) {
      status = computeSelectIndex(output_a, input_a);  
    }
  }

  // algorithm: REMOVE
  //
  else {

    // branch on implementation:
    //  Implementation: INDEX
    //
    if (implementation_d == INDEX) {
      status = computeRemoveIndex(output_a, input_a);  
    }
  }
  
  // possibly display the data
  //
  display(output_a, input_a, name());
  
  // exit gracefully
  //
  return status;
}

// method: compute
//
// arguments:
//  VectorComplexFloat& output: (output) output data
//  const VectorComplexFloat& input: (input) input data
//  AlgorithmData::COEF_TYPE input_coef_type: (input) type of input
//  long index: (input) channel index
//
// return: a boolean value indicating status
//
// this method takes the mask of the single-channel complex input data
// according to specified algorithm and implementation
//
boolean Mask::compute(VectorComplexFloat& output_a,
		      const VectorComplexFloat& input_a,
		      AlgorithmData::COEF_TYPE input_coef_type_a,
		      long index_a) {

  // delare local variables
  //
  static VectorFloat v_in_real;
  static VectorFloat v_in_imag;
  VectorFloat v_out_real;
  VectorFloat v_out_imag;  
  boolean status = false;
  
  // get the real part of the complex data
  //
  input_a.real(v_in_real);

  // get the imaginary part of the complex data
  //
  input_a.imag(v_in_imag);
  
  // call compute method for real vector
  //
  status = compute(v_out_real, v_in_real, input_coef_type_a);

  // call compute method for imaginary vector
  //
  status = compute(v_out_imag, v_in_imag, input_coef_type_a);

  // set the output values
  //
  for (long i = 0; i < input_a.length(); i++) {
    output_a(i).assign(complexfloat(v_out_real(i), v_out_imag(i)));
  }
  
  // exit gracefully
  //
  return status;
}

// method: computeSelectIndex
//
// arguments:
//  VectorFloat& output: (output) operand
//  const VectorFloat& input: (input) operand
//
// return: a boolean value indicating status
//
// this method takes the mask of the input using the SELECT algorithm.
// it outputs those elements of input data, which are indicated in the
// mask string, i.e., removes the elements which are not indicated by
// string
//
boolean Mask::computeSelectIndex(VectorFloat& output_a,
				 const VectorFloat& input_a) {

  // declare local variable
  //
  VectorByte mask(mask_d);
  
  // set the length of mask vector as same as input vector
  //
  long in_len = input_a.length();
  long mask_len = mask.length();
  mask.setLength(in_len, true);
    
  // pad the mask vector with OFF's if mask length is smaller
  // than input length
  //
  if (in_len > mask_len) {
    for (long i = mask_len; i < in_len; i++) {
      mask(i) = OFF;
    }
  }

  // allocate space for output vector:
  //  the number of preserved coefficients in output vector should be the sum
  //  of the mask vector, i.e. the number of '1' elements in mask vector
  //
  output_a.setLength((long)((unsigned long)mask.sum()));
  
  // output the masked elements in input vector for which mask flags
  // correspond to ON
  //
  for (long i = 0, j = 0; i < mask.length(); i++) {
    if (mask(i) == ON) {
      output_a(j++) = input_a(i);
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: computeRemoveIndex
//
// arguments:
//  VectorFloat& output: (output) operand
//  const VectorFloat& input: (input) operand
//
// return: a boolean value indicating status
//
// this method takes the mask of the input using REMOVE algorithm. it
// removes those elements, which are indicated in the mask string, and
// outputs the remaining elements of the input data.
//
boolean Mask::computeRemoveIndex(VectorFloat& output_a,
				 const VectorFloat& input_a) {

  // declare local variable
  //
  VectorByte mask(mask_d);
  
  // set the length of mask vector as same as input vector
  //
  long in_len = input_a.length();
  long mask_len = mask.length();
  mask.setLength(in_len, true);
  
  // pad the mask vector with OFF's if mask length is smaller than input
  // length
  //
  if (in_len > mask_len) {
    for (long i = mask_len; i < in_len; i++) {
      mask(i) = OFF;
    }
  }

  // allocate space for output vector:
  //  the number of preserved coefficients in output vector should be
  //  the number of 'OFF' elements in mask vector, i.e. the length of
  //  mask vector minus the number of 'ON' elements in mask vector
  //
  output_a.setLength(in_len - (long)(unsigned long)mask.sum());
  
  // output the masked elements in input vector for which mask flags
  // correspond to 0
  //
  for (long i = 0, j = 0; i < mask.length(); i++) {
    if (mask(i) == OFF) {
      output_a(j++) = input_a(i);
    }
  }

  // exit gracefully
  //
  return true;
}
