/* WCEMixer.java
   Copyright (C) 2006, 2007, 2008, 2009 Mysaifu.com

This file is part of Mysaifu JVM.

Mysaifu JVM 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; the version 2.

Mysaifu JVM 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 GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package gnu.javax.sound.sampled.wce;

import java.util.ArrayList;
import java.util.List;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioPermission;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.Control;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;

public class WCEMixer
    extends WCELine
    implements Mixer
{
  /**
   * イベントキュー
   */
  private final static WCEEventQueue eventQueue;

  static
    {
      eventQueue = new WCEEventQueue();
      // イベントキュースレッドを開始する
      Thread t = new Thread(eventQueue);
      t.start();

      // コールバックスレッドを開始する
      WCECallbackWindowThread callbackWindowThread = new WCECallbackWindowThread(
                                                                                 eventQueue);
      callbackWindowThread.start();

      // コールバックウインドウが初期化されるまで待つ
      try
        {
          callbackWindowThread.waitForInitialized();
        }
      catch (InterruptedException ie)
        {
          ie.printStackTrace();
        }
    }

  // デバイスがサポートしているフォーマットを
  // 表す定数
  private static int WAVE_FORMAT_1M08 = 0x00000001; /* 11.025 kHz, Mono, 8-bit */

  private static int WAVE_FORMAT_1S08 = 0x00000002; /* 11.025 kHz, Stereo, 8-bit */

  private static int WAVE_FORMAT_1M16 = 0x00000004; /* 11.025 kHz, Mono, 16-bit */

  private static int WAVE_FORMAT_1S16 = 0x00000008; /*
                                                     * 11.025 kHz, Stereo,
                                                     * 16-bit
                                                     */

  private static int WAVE_FORMAT_2M08 = 0x00000010; /* 22.05 kHz, Mono, 8-bit */

  private static int WAVE_FORMAT_2S08 = 0x00000020; /* 22.05 kHz, Stereo, 8-bit */

  private static int WAVE_FORMAT_2M16 = 0x00000040; /* 22.05 kHz, Mono, 16-bit */

  private static int WAVE_FORMAT_2S16 = 0x00000080; /* 22.05 kHz, Stereo, 16-bit */

  private static int WAVE_FORMAT_4M08 = 0x00000100; /* 44.1 kHz, Mono, 8-bit */

  private static int WAVE_FORMAT_4S08 = 0x00000200; /* 44.1 kHz, Stereo, 8-bit */

  private static int WAVE_FORMAT_4M16 = 0x00000400; /* 44.1 kHz, Mono, 16-bit */

  private static int WAVE_FORMAT_4S16 = 0x00000800; /* 44.1 kHz, Stereo, 16-bit */

  /**
   * Mixer.Info
   */
  private final Mixer.Info mixerInfo;

  /**
   * オープン中のSourceDataLine
   */
  private final List sourceLineList = new ArrayList();

  /**
   * オープン中のTargetDataLine
   */
  private final List targetLineList = new ArrayList();

  /**
   * Windows CE用Mixerのインスタンスを作成する。
   */
  public WCEMixer(Mixer.Info info)
  {
    super(new Line.Info(Mixer.class));
    this.mixerInfo = info;
  }

  public Mixer.Info getMixerInfo()
  {
    return this.mixerInfo;
  }

  /**
   * ネイティブフォーマット情報を、AudioFormatに変換する
   * 
   * @see #getNativeSourceLineFormats()
   * @see #getNativeTargetLineFormats()
   */
  private AudioFormat[] toAudioFormatArray(int nativeFormats)
  {
    List list = new ArrayList();
    if ((nativeFormats & WAVE_FORMAT_1M08) != 0)
      {
        // 11.025 kHz, mono, 8-bit
        list.add(new AudioFormat(11025.0f, 8, 1, // mono
                                 false, // unsigned
                                 false));
      }
    if ((nativeFormats & WAVE_FORMAT_1M16) != 0)
      {
        // 11.025 kHz, mono, 16-bit
        list.add(new AudioFormat(11025.0f, 16, 1, // mono
                                 true, // signed
                                 false));
      }
    if ((nativeFormats & WAVE_FORMAT_1S08) != 0)
      {
        // 11.025 kHz, stereo, 8-bit
        list.add(new AudioFormat(11025.0f, 8, 2, // stereo
                                 false, // unsigned
                                 false));
      }
    if ((nativeFormats & WAVE_FORMAT_1S16) != 0)
      {
        // 11.025 kHz, stereo, 16-bit
        list.add(new AudioFormat(11025.0f, 16, 2, // stereo
                                 true, // signed
                                 false));
      }
    if ((nativeFormats & WAVE_FORMAT_2M08) != 0)
      {
        // 22.05 kHz, mono, 8-bit
        list.add(new AudioFormat(22050.0f, 8, 1, // mono
                                 false, // unsigned
                                 false));
      }
    if ((nativeFormats & WAVE_FORMAT_2M16) != 0)
      {
        // 22.05 kHz, mono, 16-bit
        list.add(new AudioFormat(22050.0f, 16, 1, // mono
                                 true, // signed
                                 false));
      }
    if ((nativeFormats & WAVE_FORMAT_2S08) != 0)
      {
        // 22.05 kHz, stereo, 8-bit
        list.add(new AudioFormat(22050.0f, 8, 2, // stereo
                                 false, // unsigned
                                 false));
      }
    if ((nativeFormats & WAVE_FORMAT_2S16) != 0)
      {
        // 22.05 kHz, stereo, 16-bit
        list.add(new AudioFormat(22050.0f, 16, 2, // stereo
                                 true, // signed
                                 false));
      }
    if ((nativeFormats & WAVE_FORMAT_4M08) != 0)
      {
        // 44.1 kHz, mono, 8-bit
        list.add(new AudioFormat(44100.0f, 8, 1, // mono
                                 false, // unsigned
                                 false));
      }
    if ((nativeFormats & WAVE_FORMAT_4M16) != 0)
      {
        // 44.1 kHz, mono, 16-bit
        list.add(new AudioFormat(44100.0f, 16, 1, // mono
                                 true, // signed
                                 false));
      }
    if ((nativeFormats & WAVE_FORMAT_4S08) != 0)
      {
        // 44.1 kHz, stereo, 8-bit
        list.add(new AudioFormat(44100.0f, 8, 2, // stereo
                                 false, // unsigned
                                 false));
      }
    if ((nativeFormats & WAVE_FORMAT_4S16) != 0)
      {
        // 44.1 kHz, stereo, 16-bit
        list.add(new AudioFormat(44100.0f, 16, 2, // stereo
                                 true, // signed
                                 false));
      }

    return (AudioFormat[]) list.toArray(new AudioFormat[0]);
  }

  /**
   * Windows CE音声出力デバイスがサポートするフォーマットと チャンネル数を返す。 戻り値は、WAVEOUTCAPS.dwFormats
   * の値である
   */
  private native int getNativeSourceLineFormats();

  public Line.Info[] getSourceLineInfo()
  {
    int nativeFormats = getNativeSourceLineFormats();
    AudioFormat[] formats = toAudioFormatArray(nativeFormats);
    return new Line.Info[] { new DataLine.Info(SourceDataLine.class, formats,
                                               AudioSystem.NOT_SPECIFIED,
                                               AudioSystem.NOT_SPECIFIED) };
  }

  /**
   * Windows CE音声入力デバイスがサポートする フォーマットの一覧を返す。
   */
  private native int getNativeTargetLineFormats();

  public Line.Info[] getTargetLineInfo()
  {
    int nativeFormats = getNativeTargetLineFormats();
    AudioFormat[] formats = toAudioFormatArray(nativeFormats);
    return new Line.Info[] { new DataLine.Info(TargetDataLine.class, formats,
                                               AudioSystem.NOT_SPECIFIED,
                                               AudioSystem.NOT_SPECIFIED) };
  }

  public Line.Info[] getSourceLineInfo(Line.Info info)
  {
    List list = new ArrayList();
    Line.Info[] allInfo = getSourceLineInfo();
    for (int i = 0; i < allInfo.length; ++i)
      {
        if (allInfo[i].matches(info))
          {
            list.add(allInfo[i]);
          }
      }
    return (Line.Info[]) list.toArray(new Line.Info[0]);
  }

  public Line.Info[] getTargetLineInfo(Line.Info info)
  {
    List list = new ArrayList();
    Line.Info[] allInfo = getTargetLineInfo();
    for (int i = 0; i < allInfo.length; ++i)
      {
        if (allInfo[i].matches(info))
          {
            list.add(allInfo[i]);
          }
      }
    return (Line.Info[]) list.toArray(new Line.Info[0]);
  }

  /**
   * 指定されたLine.Infoに適合するLineがサポートされているかを 返す。
   */
  public boolean isLineSupported(Line.Info info)
  {
    // ToDo: ネイティブライブラリ側で判断するように修正する
    boolean result = true;
    try
      {
        getLine(info);
      }
    catch (Exception e)
      {
        result = false;
      }
    return result;
  }

  /**
   * 指定されたLine.Infoに適合するLineを返す。
   */
  public Line getLine(Line.Info info) throws LineUnavailableException
  {
    Line result = null;
    if (info instanceof DataLine.Info)
      {
        DataLine.Info dinfo = (DataLine.Info) info;
        if (SourceDataLine.class.equals(dinfo.getLineClass()))
          {
            result = new WCESourceDataLine(dinfo);
          }
        else if (TargetDataLine.class.equals(dinfo.getLineClass()))
          {
            result = new WCETargetDataLine(dinfo);
          }
        else if (Clip.class.equals(dinfo.getLineClass()))
          {
            result = new WCEClip(dinfo);
          }
      }

    if (result == null)
      {
        throw new LineUnavailableException(info.toString());
      }

    // リスナを追加する
    result.addLineListener(new LineListener()
    {
      public void update(LineEvent e)
      {
        LineEvent.Type type = e.getType();
        boolean open = (type == LineEvent.Type.OPEN);
        boolean close = (type == LineEvent.Type.CLOSE);
        Line line = e.getLine();
        if (open)
          {
            if (e instanceof SourceDataLine)
              {
                sourceLineList.add(line);
              }
            else if (e instanceof TargetDataLine)
              {
                targetLineList.add(line);
              }
          }
        else if (close)
          {
            if (e instanceof SourceDataLine)
              {
                sourceLineList.remove(line);
              }
            else if (e instanceof TargetDataLine)
              {
                targetLineList.remove(line);
              }
          }
      }
    });

    return result;
  }

  public int getMaxLines(Line.Info info)
  {
    // ToDo: 実装する
    return AudioSystem.NOT_SPECIFIED;
  }

  /**
   * すでにオープンされているSourceDataLineの配列を返す
   */
  public Line[] getSourceLines()
  {
    // セキュリティチェック
    SecurityManager sm = System.getSecurityManager();
    if (sm != null)
      {
        sm.checkPermission(new AudioPermission("play"));
      }

    return (Line[]) this.sourceLineList.toArray(new Line[0]);
  }

  /**
   * すでにオープンされているTargetDataLienの配列を返す。
   */
  public Line[] getTargetLines()
  {
    // セキュリティチェック
    SecurityManager sm = System.getSecurityManager();
    if (sm != null)
      {
        sm.checkPermission(new AudioPermission("record"));
      }

    return (Line[]) this.targetLineList.toArray(new Line[0]);
  }

  public void synchronize(Line[] lines, boolean maintainSync)
  {
    // ToDo: 実装する
    throw new IllegalArgumentException();
  }

  public void unsynchronize(Line[] lines)
  {
    // ToDo: 実装する
  }

  public boolean isSynchronizationSupported(Line[] lines, boolean maintainSync)
  {
    // ToDo: 実装する
    return false;
  }

  public Control[] getControls()
  {
    // ToDo: 実装する
    return new Control[0];
  }

  public boolean isControlSupported(Control.Type control)
  {
    // ToDo: 実装する
    return false;
  }

  public Control getControl(Control.Type control)
  {
    // ToDo: 実装する
    throw new IllegalArgumentException();
  }

}
