// file: $isip/class/algo/Window/win_00.cc
// version: $Id: win_00.cc 8197 2002-06-12 16:30:49Z gao $
//

// isip include files
//
#include "Window.h"
#include "Bessel.h"
#include "Chebyshev.h"

//------------------------------------------------------------------------
//
// required public methods
//
//-----------------------------------------------------------------------

// method: assign
//
// arguments:
//  const Window& arg: (input) object to be assigned
//
// return: a boolean value indicating status
//
// this method assigns the input object to the current object
//
boolean Window::assign(const Window& arg_a) {

  // assign protected data from argument
  //
  algorithm_d = arg_a.algorithm_d;
  alignment_d = arg_a.alignment_d;
  normalization_d = arg_a.normalization_d;
  duration_d = arg_a.duration_d;
  cmode_d = arg_a.cmode_d;
  constants_d.assign(arg_a.constants_d);
  data_d.assign(arg_a.data_d);

  // set initialization flag
  //
  is_valid_d = arg_a.is_valid_d;

  // exit gracefully
  //
  return AlgorithmBase::assign(arg_a);
}

// method: eq
//
// arguments:
//  const Window& arg: (input) object to be compared
//
// return: a boolean value indicating status
//
// this method checks whether the current object is identical to the
// input object
//
boolean Window::eq(const Window& arg_a) const {

  // compare parameters related to algorithm choices
  //
  if ((algorithm_d != arg_a.algorithm_d) ||
      (alignment_d != arg_a.alignment_d) ||
      (normalization_d != arg_a.normalization_d) ||
      (duration_d != arg_a.duration_d) ||
      (cmode_d != arg_a.cmode_d)) {
    return false;
  }

  // for non-CUSTOM, compare the parameters of the function
  //
  else if ((algorithm_d != CUSTOM) &&
	   (!constants_d.almostEqual(arg_a.constants_d))) {
    return false;
  }

  // for CUSTOM, compare the functions directly
  //
  else if ((algorithm_d == CUSTOM) &&
	   (!data_d.almostEqual(arg_a.data_d))) {
    return false;
  }

  // exit gracefully by checking the base class
  //
  return AlgorithmBase::eq(arg_a);
}

// method: clear
//
// arguments:
//  Integral::CMODE ctype: (input) clear mode
//
// return: a boolean value indicating status
//
// this method resets the data members to the default values
//
boolean Window::clear(Integral::CMODE ctype_a) {
  
  // reset the data members unless the mode is RETAIN
  //
  if (ctype_a != Integral::RETAIN) {

    // clear the window design parameters
    //
    algorithm_d = DEF_ALGORITHM;
    alignment_d = DEF_ALIGNMENT;
    normalization_d = DEF_NORMALIZATION;
    duration_d = DEF_DURATION;
    cmode_d = DEF_CMODE;
    
    // clear the vector data
    //
    constants_d.clear(ctype_a);
    data_d.clear(ctype_a);
  }

  // in RETAIN mode, clear the data only if the algorithm is not CUSTOM
  //
  else if (algorithm_d != CUSTOM) {
    data_d.clear(ctype_a);
  }
  
  // set the initialization flag
  //
  is_valid_d = false;

  // call the base clear method
  //
  return AlgorithmBase::clear(ctype_a);
}

//---------------------------------------------------------------------------
//
// class-specific public methods:
//  public methods required by the AlgorithmBase interface contract
//
//---------------------------------------------------------------------------

// method: assign
//
// arguments:
//  const AlgorithmBase& arg: (input) object to be assigned
//
// return: a boolean value indicating status
//
// this method assigns the input algorithm object to the current
// Window object
//
boolean Window::assign(const AlgorithmBase& arg_a) {

  // case: input is an Window object
  //
  if (typeid(arg_a) == typeid(Window)) {
    return assign((Window&)arg_a);
  }

  // case: other
  //  if the input algorithm object is not an Window object, error
  //
  else {
    return Error::handle(name(), L"assign", Error::ARG, __FILE__, __LINE__);
  }
}

// method: eq
//
// arguments:
//  const AlgorithmBase& arg: (input) object to be compared
//
// return: a boolean value indicating status
//
// this method checks whether the current Window object is identical
// to the input algorithm object
//
boolean Window::eq(const AlgorithmBase& arg_a) const {

  // case: input is an Window object
  //
  if (typeid(arg_a) == typeid(Window)) {
    return eq((Window&)arg_a);
  }

  // case: other
  //  if the input algorithm object is not an Window object, return
  //  an error
  //
  else {
    return Error::handle(name(), L"eq", Error::ARG, __FILE__, __LINE__);
  }
}

// method: init
//
// arguments: none
//
// return: a boolean value indicating status
//
// to initialize this object, either the
// or the duration and sample frequency must be set,
// or the length.
//
// see the header file for references for the algorithms.
//
boolean Window::init() {

  // check for a valid mode
  //
  long N;

  if (cmode_d == FRAME_INTERNAL) {
    N = data_d.length();
  }
  else if (cmode_d == CROSS_FRAME) {
    N = (long)Integral::round(duration_d * sample_freq_d);
  }
  else {
      return Error::handle(name(), L"init", ERR_UNKIMP, __FILE__, __LINE__);
  }
  if (N <= 0) {
    return false;
  }

  // for all algorithms, we need to size data_d, because this is where
  // the window function will be stored
  //
  data_d.setLength(N);

  // algorithm: RECTANGULAR
  //
  if (algorithm_d == RECTANGULAR) {

    // assign the constants if necessary
    //
    if (constants_d.length() == 0) {
      constants_d.assign(DEF_RECT_CONSTANTS);
    }
    else if (constants_d.length() != 1) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }

    // generate the window
    //
    if (!generateRectangular(data_d, constants_d, N)) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }      
  }

  // algorithm: BARTLETT
  //
  else if (algorithm_d == BARTLETT) {

    // assign the constants if necessary
    //
    if (constants_d.length() == 0) {
      constants_d.assign(DEF_BART_CONSTANTS);
    }
    else if (constants_d.length() != 1) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }

    // generate the window
    //
    if (!generateBartlett(data_d, constants_d, N)) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }      
  }

  // Algorithm: BLACKMAN
  //
  else if (algorithm_d == BLACKMAN) {

    // assign the constants if necessary
    //
    if (constants_d.length() == 0) {
      constants_d.assign(DEF_BLCK_CONSTANTS);
    }
    else if (constants_d.length() != 3) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }

    // generate the window
    //
    if (!generateBlackman(data_d, constants_d, N)) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }      
  }

  // algorithm: DOLPH_CHEBYSHEV
  //
  else if (algorithm_d == DOLPH_CHEBYSHEV) {

    // assign the constants if necessary
    //
    if (constants_d.length() == 0) {
      constants_d.assign(DEF_DCHB_CONSTANTS);
    }
    else if (constants_d.length() != 2) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }

    // generate the window
    //
    if (!generateDolphChebyshev(data_d, constants_d, N)) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }      
  }

  else if (algorithm_d == GAUSSIAN) {

    // assign the constants if necessary
    //
    if (constants_d.length() == 0) {
      constants_d.assign(DEF_GAUS_CONSTANTS);
    }
    else if (constants_d.length() != 2) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }

    // generate the window
    //
    if (!generateGaussian(data_d, constants_d, N)) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }      
  }

  // algorithm: HAMMING
  //

  else if (algorithm_d == HAMMING) {

    // assign the constants if necessary
    //
    if (constants_d.length() == 0) {
      constants_d.assign(DEF_HAMM_CONSTANTS);
    }
    else if (constants_d.length() != 2) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }

    // generate the window
    //
    if (!generateGeneralizedHanning(data_d, constants_d, N)) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }      
  }

  else if (algorithm_d == HANNING) {

    // assign the constants if necessary
    //
    if (constants_d.length() == 0) {
      constants_d.assign(DEF_HANN_CONSTANTS);
    }
    else if (constants_d.length() != 2) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }

    // generate the window
    //
    if (!generateGeneralizedHanning(data_d, constants_d, N)) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }      
  }

  // algorithm: KAISER
  //
  else if (algorithm_d == KAISER) {

    // assign the constants if necessary
    //
    if (constants_d.length() == 0) {
      constants_d.assign(DEF_KAIS_CONSTANTS);
    }
    else if (constants_d.length() != 2) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }

    // generate the window
    //
    if (!generateKaiser(data_d, constants_d, N)) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }      
  }

  // algorithm: LIFTER
  //
  else if (algorithm_d == LIFTER) {

    // assign the constants if necessary
    //
    if (constants_d.length() == 0) {
      constants_d.assign(DEF_LIFT_CONSTANTS);
    }
    else if (constants_d.length() != 3) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }

    // generate the window
    //
    if (!generateLifter(data_d, constants_d, N)) {
      return Error::handle(name(), L"init", ERR_PRM, __FILE__, __LINE__);
    }      
  }

  // algorithm: CUSTOM
  //   needs no initialization at this point
  //
  else if (algorithm_d != CUSTOM) {
    return Error::handle(name(), L"init", ERR_UNKALG, __FILE__, __LINE__);
  }

  // normalize if necessary
  //
  if (normalization_d == UNIT_ENERGY) {
    float rms = data_d.rms();
    if (rms <= 0.0) {
      return Error::handle(name(), L"init",
			   Error::ENUM, __FILE__, __LINE__);
    }
    else {
      data_d.div(data_d.rms());
    }
  }

  // set flag
  //
  is_valid_d = true;

  // exit gracefully
  //
  return true;
}

//-----------------------------------------------------------------------------
//
// we define non-integral constants in the default constructor
//      
//-----------------------------------------------------------------------------

// constants: class name
//
const String Window::CLASS_NAME(L"Window");

// constants: i/o related constants
//
const String Window::DEF_PARAM(L"");
const String Window::PARAM_ALGORITHM(L"algorithm");
const String Window::PARAM_IMPLEMENTATION(L"implementation");
const String Window::PARAM_ALIGNMENT(L"alignment");
const String Window::PARAM_NORMALIZATION(L"normalization");
const String Window::PARAM_DURATION(L"duration");
const String Window::PARAM_CONSTANTS(L"constants");
const String Window::PARAM_DATA(L"data");
const String Window::PARAM_CMODE(L"compute_mode");

// constants: default constants for each window algorithm
//
const VectorFloat Window::DEF_RECT_CONSTANTS(L"1.0");
const VectorFloat Window::DEF_BART_CONSTANTS(L"1.0");
const VectorFloat Window::DEF_BLCK_CONSTANTS(L"1.0, 0.42, 0.50");
const VectorFloat Window::DEF_DCHB_CONSTANTS(L"1.0, 0.50");
const VectorFloat Window::DEF_GAUS_CONSTANTS(L"1.0, 0.2618");
const VectorFloat Window::DEF_HAMM_CONSTANTS(L"1.0, 0.54");
const VectorFloat Window::DEF_HANN_CONSTANTS(L"1.0, 0.50");
const VectorFloat Window::DEF_KAIS_CONSTANTS(L"1.0, 0.50");
const VectorFloat Window::DEF_LIFT_CONSTANTS(L"1.0, 22.0, 11.0");

// constants: NameMap(s) for the enumerated values
//
const NameMap Window::ALGO_MAP(L"RECTANGULAR, BLACKMAN, BARTLETT, DOLPH_CHEBYSHEV, GAUSSIAN, HAMMING, HANNING, KAISER, LIFTER, CUSTOM");
const NameMap Window::IMPL_MAP(L"MULTIPLICATION");
const NameMap Window::ALGN_MAP(L"CENTER, LEFT, RIGHT");
const NameMap Window::NORM_MAP(L"NONE, UNIT_ENERGY");
  
// static instantiations: memory manager
//
MemoryManager Window::mgr_d(sizeof(Window), Window::name());
