/*
    This file is part of kenvy24 KDE multimedia utility
    Copyright (c) 2007  Valentin RUSU kenvy24@rusu.info

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License or (at
    your option) version 3 or, at the discretion of KDE e.V. (which shall
    act as a proxy as in section 14 of the GPLv3), any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


#ifndef _ENVYCARD_H_
#define _ENVYCARD_H_

#include <qobject.h>
#include <alsa/asoundlib.h>
#include <string>
#include <vector>
#include <QVariant>

#include "envystructs.h"

class QSocketNotifier;

class EnvyCard : public QObject {
  Q_OBJECT
  public:

    enum CardDevice
    {
        /* MidiMan */
        ICE1712_SUBDEVICE_DELTA1010     = 0x121430d6,
        ICE1712_SUBDEVICE_DELTADIO2496  = 0x121431d6,
        ICE1712_SUBDEVICE_DELTA66       = 0x121432d6,
        ICE1712_SUBDEVICE_DELTA44       = 0x121433d6,
        ICE1712_SUBDEVICE_AUDIOPHILE    = 0x121434d6,
        ICE1712_SUBDEVICE_DELTA410      = 0x121438d6,
        ICE1712_SUBDEVICE_DELTA1010LT   = 0x12143bd6,

        /* Terratec */
        ICE1712_SUBDEVICE_EWX2496       = 0x3b153011,
        ICE1712_SUBDEVICE_EWS88MT       = 0x3b151511,
        ICE1712_SUBDEVICE_EWS88D        = 0x3b152b11,
        ICE1712_SUBDEVICE_DMX6FIRE      = 0x3b153811,

        /* Hoontech */
        ICE1712_SUBDEVICE_STDSP24       = 0x12141217      /* Hoontech SoundTrack Audio DSP 24 */

        // TODO add other cards to this enum
    };

    private:

        typedef struct {
            unsigned int subvendor; /* PCI[2c-2f] */
            unsigned char size; /* size of EEPROM image in bytes */
            unsigned char version;  /* must be 1 */
            unsigned char codec;    /* codec configuration PCI[60] */
            unsigned char aclink;   /* ACLink configuration PCI[61] */
            unsigned char i2sID;    /* PCI[62] */
            unsigned char spdif;    /* S/PDIF configuration PCI[63] */
            unsigned char gpiomask; /* GPIO initial mask, 0 = write, 1 = don't */
            unsigned char gpiostate; /* GPIO initial state */
            unsigned char gpiodir;  /* GPIO direction state */
            unsigned short ac97main;
            unsigned short ac97pcm;
            unsigned short ac97rec;
            unsigned char ac97recsrc;
            unsigned char dacID[4]; /* I2S IDs for DACs */
            unsigned char adcID[4]; /* I2S IDs for ADCs */
            unsigned char extra[4];
        } ice1712_eeprom_t;

        snd_ctl_t               *ctl;
        int                     cardNumber;
        QString                 cardName;
        QString                 cardLongName;
        bool                    cardIsDMX6FIRE;
        ice1712_eeprom_t        card_eeprom;
        snd_ctl_elem_value_t    *peaks;
        StereoLevels            mixerLevels;
        StereoLevels            pcmLevels[ MAX_PCM_OUTPUTS ];
        StereoLevels            spdifLevels[ MAX_SPDIF_CHANNELS ];
        StereoLevels            spdifInLevels[ MAX_SPDIF_CHANNELS ];
        StereoLevels            inputLevels[ MAX_PCM_OUTPUTS + MAX_SPDIF_CHANNELS + MAX_INPUT_CHANNELS ];
        bool                    outputActive[ MAX_OUTPUT_CHANNELS ];
        int                     outChannels;
        bool                    outputSPDIFActive[ MAX_SPDIF_CHANNELS ];
        int                     spdifChannels;
        std::vector< QSocketNotifier* >     eventNotifiers;

        class SndCtlElemValue {
          public:
            snd_ctl_elem_value_t *val;

            SndCtlElemValue( _snd_ctl_elem_iface, const char* name );
            SndCtlElemValue( _snd_ctl_elem_iface, const char* name,  int index );
            ~SndCtlElemValue();

            snd_ctl_elem_value_t* operator & () const { return val; }
            int getInteger( int index =0) const { return snd_ctl_elem_value_get_integer( val, index ); }
            void setInteger( int intVal ) const { snd_ctl_elem_value_set_integer( val, 0, intVal ); }
            // TODO: map the other snd_ctl_elem_XXX  ALSA calls here, then update code to call them
        };

        struct MixerInput {
            bool    muteLeft;
            bool    muteRight;
            int     volumeLeft;
            int     voluemRight;
        };

    public:
        enum PatchBayRoutes {
            ROUTE_NONE = -1,
            ROUTE_PCM_OR_SPDIF = 0,
            ROUTE_MIXER_OUT,
            ROUTE_INPUT_SPDIF_LEFT,
            ROUTE_INPUT_SPDIF_RIGHT,
            ROUTE_INPUT_ANALOG_LEFT,
            ROUTE_INPUT_ANALOG_RIGHT
        };

        /**
         * This constructor is called from EnvyCardFactory during
         * card enumeration and when a ICE1712 based card was found
         *
         * @param sndCtl the initialized ALSA object
         * @param name the card named, as stated by ALSA
         * @param number the card number, as stated by ALSA
         */
        EnvyCard( snd_ctl_t* sndCtl, const char* name, int number );
        ~EnvyCard();
    private:
        // hide default constructor
        EnvyCard();

    public:
        bool foundEnvyCard() const { return cardName.length() >0; }
        const QString& getCardName() const { return cardName; }
        const QString& getCardLongName() const { return cardLongName; }
        bool isDMX6FIRE() const { return getCardDevice() == ICE1712_SUBDEVICE_DMX6FIRE ; }
        CardDevice getCardDevice() const { return (CardDevice)card_eeprom.subvendor; }

        /**
         * This method reads current peak levels from the sound card.
         */
        void updatePeaks();
        const StereoLevels& getMixerLevels() const { return mixerLevels; }

        bool isOutputActive( int index );
        bool isOutputSPDIFActive( int index );
        PatchBayRoutes  getAnalogRouteState( int index, LeftRight );
        PatchBayRoutes  getSPDIFRouteState( int index, LeftRight );
        bool getMixerPCMMuteState( int index, LeftRight );
        bool getMixerAnalogInMuteState( int index, LeftRight );
        bool getMixerSPDIFMuteState( int index, LeftRight );
/*        MixerAdjustement getMixerCaptureAdjustement( int index, LeftRight);
        MixerAdjustement getMixerPlaybackAdjustement( int index, LeftRight);
        MixerAdjustement getMixerSPDIFAdjustement( int index, LeftRight);*/
        StereoLevels getDACVolume();
        StereoLevels getADCVolume();



        void patchBaySetRoute( RouteKind, int index, LeftRight, PatchBayRoutes );
        void routeThroughMixer();

        /**
         * Use this method to make this class emit all signal
         * This is useful when connecting the card to the UI in order
         * to get the UI display the current hardware state
         */
        void pulseSignals();

    Q_SIGNALS:
        void mixerLevelUpdated( const StereoLevels& );
        void pcmLevelUpdated( int, const StereoLevels& );
        void spdifLevelUpdated( int, const StereoLevels& );
        void spdifInLevelUpdated( int, const StereoLevels& );
        void inputLevelUpdated( int, const StereoLevels& );
        void patchBayUpdated();
        void mixerUpdatePCMMuteSwitch( int index, LeftRight channel, bool on );
        void mixerUpdateAnalogInMuteSwitch( int index, LeftRight channel, bool on );
        void mixerUpdateSPDIFInMuteSwitch( int index, LeftRight channel, bool on );
        /**
         * ice1712 based card are peculiar in that playback channels can be mixed
         * separately to the mixer output. More clearly, (pcm left) can be sent to
         * (main out left) and (main out right) in the seame time. If the volume amount
         * from (pcm left) to (main outs) is the same, the output will be mono !
         * To preserve stereo output, one of the two volumes must be 0 : the right one
         * for pair channels and the left one for impair channels
         */
        void mixerUpdatePlaybackVolume( int index, LeftRight channel, int, int );
        void mixerUpdateAnalogInVolume( int index, LeftRight channel, int, int );
        void mixerUpdateSPDIFVolume( int index, LeftRight channel, int, int );
        void analogUpdateDACVolume( int index, LeftRight channel, int );

    protected Q_SLOTS:
        void driverEvent( int );

    public Q_SLOTS:
        void mixerMuteCaptureChannel( int, LeftRight, bool );
        void mixerMutePlaybackChannel( int, LeftRight, bool );
        void mixerMuteSPDIFChannel( int, LeftRight, bool );
        void setMixerCaptureVolume( int, LeftRight, int volLeft, int volRight );
        void setMixerPlaybackVolume( int, LeftRight, int volLeft, int volRight );
        void setMixerSPDIFVolume( int, LeftRight, int volLeft, int volRight );
        void setDACVolume( LeftRight, int level );
        void setADCVolume( LeftRight, int level );
        /**
         * give the following parameter values
         *      0 : internal
         *      1 : front
         *      2 : rear
         *      3 : wavetable
         */
        void setLineInSource( int );
        /**
         * give the followin parameter values
         *      0 : microphone
         *      1 : phono
         */
        void setMicInSource( int );

    private:
        enum Section {
            SECTION_PLAYBACK,
            SECTION_CAPTURE,
            SECTION_IEC598
        };

        void initPatchbay();
        void initEvents();
        void writeSndCtl( const SndCtlElemValue& );
        void readSndCtl( const SndCtlElemValue& );
        void setVolume( Section, int, LeftRight, int volLeft, int volRight );
//        MixerAdjustement getMixerAdjustement( Section, int index, LeftRight );
        const char* getSectionName( Section );
//        MixerAdjustement deduceMixerAdjustement( int left, int right, int maxVol = 96 );

        // driver events start
        void mixerUpdatePlaybackVolume(int);
        void mixerUpdateInputVolume(int);
        void mixerUpdateSPDIFVolume(int);
        void mixerUpdatePlaybackSwitch(int);
        void mixerUpdateInputSwitch(int);
        void mixerUpdateSPDIFSwitch(int);
        void patchbayUpdate();
        void dacVolumeUpdate(int);
        void adcVolumeUpdate(int);
        // driver events end

};

// This is needed to enable EnvyCard QVariant type
Q_DECLARE_METATYPE( EnvyCard* )

#endif // _ENVYCARD_H_
// kate: space-indent on; indent-width 4; replace-tabs on;
