/**************************************************************************

This file is part of the audio mixer GUI for fvwm95.
Copyright (C) 1997 Rodolphe Suescun.

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) 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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

**************************************************************************/

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>

#include "main.h"

#define PixmapPath "/home/rod/C/xclass/icons"

//----------------------------------------------------------------------

OLayoutHints  * topleftlayout =
    new OLayoutHints(LHINTS_TOP | LHINTS_LEFT,1,1,1,1);

OLayoutHints  * centerleftlayout =
    new OLayoutHints(LHINTS_CENTER_Y | LHINTS_LEFT,1,1,1,1);

OLayoutHints  * centerrightlayout =
    new OLayoutHints(LHINTS_CENTER_Y | LHINTS_RIGHT,1,1,1,1);

OLayoutHints  * topcenterxlayout =
    new OLayoutHints(LHINTS_TOP | LHINTS_CENTER_X,1,1,1,1);

OLayoutHints  * topexpandxlayout =
    new OLayoutHints(LHINTS_TOP | LHINTS_EXPAND_X,1,1,1,1);

OLayoutHints  * expandxexpandylayout =
    new OLayoutHints(LHINTS_EXPAND_X | LHINTS_EXPAND_Y,1,1,1,1);

char channel_names[17][14] = { "Main Volume", "Bass", "Treble", "MIDI",
                               "Wave", "PC Speaker", "Line-In", "Microphone",
                               "CD Audio", "Mixer", "Wave 2", "Rec. Source",
                               "Input Gain", "Output Gain", "Line 1", "Line 2",
                               "Line 3"};

OXClient * clientX;
OXMixerMainFrame * mainWindow;

#define VOLUME_SLIDER 0
#define BALANCE_SLIDER 1

const OPicture * spkleft, * spkright;

#define MIXER_DEVICE "/dev/mixer"
int mixer_fd;
int devmask, recmask, stereodevs, readcaps;

int main(int argc, char **argv)
{
  mixer_fd = open(MIXER_DEVICE, O_RDWR, 0);   

  if (mixer_fd < 0)
    printf("No mixer.\n");

  if ((ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) ||
      (ioctl(mixer_fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1) ||
      (ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) == -1) ||
      (ioctl(mixer_fd, SOUND_MIXER_READ_CAPS, &readcaps) == -1)) {
    printf("No mixer.\n");
  }

  close(mixer_fd);

  clientX = new OXClient;

  spkleft = clientX->GetPicture("spkleft.xpm");
  spkright = clientX->GetPicture("spkright.xpm");

  mainWindow = new OXMixerMainFrame(clientX->GetRoot(), 10, 10);

  mainWindow->MapWindow();

  clientX->Run();
}

OXVolumeControlFrame::OXVolumeControlFrame(const OXWindow *p, int chn) :
  OXCompositeFrame(p, 10, 10, VERTICAL_FRAME, _defaultFrameBackground) {

  channel = chn;

  name = new OXLabel(this,new OString(channel_names[channel]));
  AddFrame(name, topleftlayout);

  balancezone = new OXCompositeFrame(this, 10, 10, HORIZONTAL_FRAME);

  balance = new OXLabel(this,new OString("Balance :"));
  AddFrame(balance, topleftlayout);

    balanceslider = new OXHSlider(balancezone, 40,
                                  SLIDER_1 | SCALE_DOWNRIGHT, 5);
    balanceslider->Associate(this);
    balanceslider->SetScale(10);
    balanceslider->SetRange(-10,10);
    balancezone->AddFrame(new OXIcon(balancezone,spkleft,16,16),
                          centerleftlayout);
    balancezone->AddFrame(balanceslider, topcenterxlayout);
    balancezone->AddFrame(new OXIcon(balancezone,spkright,16,16),
                          centerrightlayout);

  AddFrame(balancezone, topcenterxlayout);

  AddFrame(new OXHorizontal3dLine(this), topexpandxlayout);

  volume = new OXLabel(this,new OString("Volume :"));
  AddFrame(volume, topleftlayout);

  volumeslider = new OXVSlider(this, 100, SLIDER_2 | SCALE_BOTH, 6);
  volumeslider->Associate(this);
  volumeslider->SetRange(0,100);
  AddFrame(volumeslider, topcenterxlayout);

  mutebutton = new OXCheckButton(this, new OHotString("&Mute"), 0);
  AddFrame(mutebutton, topleftlayout);

  ReadVolume();
}

void OXVolumeControlFrame::ReadVolume() {
  int volinfo, volright, volleft, volume, balance;

  mixer_fd = open(MIXER_DEVICE, O_RDWR, 0);   

  if (mixer_fd < 0)
    printf("No mixer.\n");

  if (ioctl(mixer_fd, MIXER_READ(channel), &volinfo) == -1) {
    printf("No mixer.\n");
  }

  close(mixer_fd);

  volleft = volinfo & 255;
  volright = (volinfo >> 8) & 255;

  volume = (volleft > volright) ? volleft : volright;
  balance = 0;

  if (volume > volleft)
    balance = 10 - (10*volleft) / volume;
  else if (volume > volright)
    balance = (10*volright) / volume - 10;

  volumeslider->SetPosition(100 - volume);
  balanceslider->SetPosition(balance);
}

void OXVolumeControlFrame::WriteVolume() {
  int volinfo, volright, volleft, volume, balance;

  if (mutebutton->GetState() == BUTTON_DOWN)
    volume = 0;
  else 
    volume = 100 - volumeslider->GetPosition();

  if ((1 << channel) & stereodevs) {
    balance = balanceslider->GetPosition();
  } else {
    balanceslider->SetPosition(0);
    balance = 0;
  }

  if (balance < 0) {
    volleft = volume;
    volright = (volume*(balance+10)) / 10;
  } else {
    volright = volume;
    volleft = (volume*(10-balance)) / 10;
  }

  volinfo = volleft + (volright << 8);

  mixer_fd = open(MIXER_DEVICE, O_RDWR, 0);   

  if (mixer_fd < 0)
    printf("No mixer.\n");

  if (ioctl(mixer_fd, MIXER_WRITE(channel), &volinfo) == -1) {
    printf("No mixer.\n");
  }

  close(mixer_fd);
}

OXVolumeControlFrame::~OXVolumeControlFrame() {
// things to add here...
}

int OXVolumeControlFrame::ProcessMessage(long msg, long parm1, long parm2) {

  //printf("%i %i %i\n",GET_MSG(msg),GET_SUBMSG(msg),parm1);
  switch (GET_MSG(msg)) {

    case XC_COMMAND:    // buttons and menu commands
    case XC_VSLIDER:
    case XC_HSLIDER:
      WriteVolume();
      break;

    default:
      break;

  } // switch(GET_MSG(msg))

  return True;
}

OXMixerMainFrame::OXMixerMainFrame(const OXWindow *p, int w, int h) :
  OXMainFrame(p, w, h) {
  int first = 1;

  menu_options = new OXPopupMenu(_client->GetRoot());
  menu_options->AddEntry(new OHotString("&Properties"), MENU_PROPS);
  menu_options->AddSeparator();
  menu_options->AddEntry(new OHotString("E&xit"), MENU_EXIT);

  menu_help = new OXPopupMenu(_client->GetRoot());
  menu_help->AddEntry(new OHotString("&Contents"), MENU_HELP);
  menu_help->AddSeparator();
  menu_help->AddEntry(new OHotString("&About"), MENU_ABOUT);

  menu = new OXMenuBar(this, 1, 1, HORIZONTAL_FRAME);
  menu->AddPopup(new OHotString("O&ptions"), menu_options, topleftlayout);
  menu->AddPopup(new OHotString("&?"), menu_help, topleftlayout);
  AddFrame(menu, topexpandxlayout);

  menu_options->Associate(this);
  menu_help->Associate(this);

  AddFrame(new OXHorizontal3dLine(this), topexpandxlayout);

  volumes = new OXCompositeFrame(this, 10, 10, HORIZONTAL_FRAME);
  for(int i = 0;i<17;i++) {
    if ((1 << i) & devmask) {
      if (!first)
        volumes->AddFrame(new OXVertical3dLine(volumes), expandxexpandylayout);
      first = 0;
      volumes->AddFrame(new OXVolumeControlFrame(volumes, i), topleftlayout);
    }
  }
  AddFrame(volumes, topcenterxlayout);

  SetWindowName("fOX Mixer");
  SetClassHints("Mixer", "Mixer");

  SetMWMHints(MWM_DECOR_ALL | MWM_DECOR_MAXIMIZE | MWM_DECOR_MENU,
              MWM_FUNC_ALL | MWM_FUNC_MAXIMIZE | MWM_FUNC_RESIZE,
              MWM_INPUT_MODELESS);


  Resize(GetDefaultSize());
  MapSubwindows();
}

OXMixerMainFrame::~OXMixerMainFrame() {
}

int OXMixerMainFrame::ProcessMessage(long msg, long parm1, long parm2) {
  //printf("%i %i %i\n",GET_MSG(msg),GET_SUBMSG(msg),parm1);
  switch (GET_MSG(msg)) {

    case XC_COMMAND:    // buttons and menu commands
      switch(GET_SUBMSG(msg)) {

        case CM_MENU:    // buttons and menu commands
          switch(parm1) {

            case MENU_EXIT:
              delete _client;
              exit(0);
              break;

            default:
              break;

          }
          break;

        default:
          break;

     }
     break;

    default:
      break;

  } // switch(GET_MSG(msg))

  return True;
}
