/*
 * sndctl  -  a plugin for the Video Disk Recorder
 *        file: mixer.c
 * description: parent class for all types of mixer devices
 *
 *      author: Thomas Hildebrandt <toxym@web.de>
 *
 * inspired by and reengineered from 'avolctl'
 * thanks to Martin Prochnow <nordlichtl@martins-kabuff.de>
 */

#include "defaults.h"
#include "mixer.h"
#include "soundset.h"

/*********************************************************
 * member functions for class cMixer
 *********************************************************/

/*
 * constructor
 */
cMixer::cMixer(){
  nrOfControls = -1;
}

/*
 * destructor
 */
cMixer::~cMixer(){
  //free( controls );
  delete[] controls;
  free( &nrOfControls );
}

/*
 * returns a vector with all controls names
 */
vector<string> cMixer::GetControls( void ){
  vector<string> ctrls;
  int            i;

  // loop over all controls
  for( i = 0; i < nrOfControls; i++ )
    ctrls.push_back((controls + i)->name );

  return ctrls;
}

/*
 * update mixer values
 */
void cMixer::Update( cSoundSet *SoundSet ){
  int               i;
  string::size_type loc;
  string            v;

  dsyslog( "sndctl (cMixer::Update): Updating controls with sound set '%s'.", SoundSet->GetName().c_str());

  // loop over all controls
  for( i = 0; i < nrOfControls; i++ ){
    v = SoundSet->Get((controls + i)->name );

    // get operation
    (controls + i)->op = v.substr( 0, 1 ).c_str()[0];

    // get value
    if((controls + i)->op == SNDCTL_MIXER_NONE )
      (controls + i)->value = 0;
    else
      (controls + i)->value = atoi( v.substr( 1 ).c_str());

    // check and correct limits
    (controls + i)->value = (controls + i)->value < 0 ? 0 : (controls + i)->value;
    (controls + i)->value = (controls + i)->value > 999 ? 999 : (controls + i)->value;

    // check and set minimum volume
    (controls + i)->limitL = (controls + i)->min;
    if(( loc = v.find( ';', 0 )) != string::npos )
      (controls + i)->limitL = atoi( v.substr( ++loc ).c_str());

    // check and set maximum volume
    (controls + i)->limitU = (controls + i)->max;
    if(( loc != string::npos ) && (( loc = v.find( ';', loc )) != string::npos ))
      (controls + i)->limitU = atoi( v.substr( ++loc ).c_str());

    dsyslog( "sndctl (cMixer::Update): '%s' updated with %c%d;%ld;%ld", (controls + i)->name.c_str(), (controls + i)->op, (controls + i)->value, (controls + i)->limitL, (controls + i)->limitU );
  }
}

/*
 * sets the volume of this sound set
 */
void cMixer::Volume( int Vol ){
  int  i;
  long vol;

  dsyslog( "sndctl (cMixer::Volume): Volume goes to %d%%.", Vol );

  // loop over all controls
  for( i = 0; i < nrOfControls; i++ ){
    // initial value for 'volume'
    (controls + i)->volume = -1;

    // calculate volume according to operation mode
    switch( (controls + i)->op ){
      case SNDCTL_MIXER_STATIC:
        vol = (controls + i)->value;
        break;

      case SNDCTL_MIXER_PLUS:
        vol = Vol + (controls + i)->value;
        break;

      case SNDCTL_MIXER_MINUS:
        vol = Vol - (controls + i)->value;
        break;

      case SNDCTL_MIXER_PERCENT:
        vol = Vol * (controls + i)->value / 100;
        break;

      case SNDCTL_MIXER_NONE:
      default:
        dsyslog( "sndctl (cMixer::Volume): Control '%s' stays unchanged.", (controls + i)->name.c_str());
        continue;
        break;
    }

    // Vol = 0 means, no matter, what happend before
    if( Vol == 0 )
      vol = 0;

    // scale volume to real values
    vol = (controls + i)->min + ( (controls + i)->max - (controls + i)->min ) * vol / 100;

    // check for hard limits
    if( Vol > 0 && vol < (controls + i)->limitL )
      vol = (controls + i)->limitL;
    if( vol > (controls + i)->limitU )
      vol = (controls + i)->limitU;

    dsyslog( "sndctl (cMixer::Volume): Control '%s' goes to '%ld'.", (controls + i)->name.c_str(), vol );

    // set volume
    (controls + i)->volume = vol;
  }

  // set volumes to real hardware
  Volumes();
}
