/* WCESequencer.java -- MIDI Sequencer Device for Windows Mobile.
   Copyright (C) 2009 Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath 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, or (at your option)
any later version.

GNU Classpath 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.midi.wce;

import java.io.InputStream;
import java.io.IOException;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.ControllerEventListener;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiDevice.Info;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Track;
import javax.sound.midi.Transmitter;

public class WCESequencer implements Sequencer
{
  private static final WCESequencer INSTANCE = new WCESequencer();

  private boolean opened;

  private Sequence sequence;

  private boolean running;

  /**
   * Output port
   */
  private WCEMidiOutputPort outputPort;

  /**
   * Transmitter of this WCESequencer.
   */
  class SequencerTransmitter implements Transmitter
  {
    private Receiver receiver;

    public void setReceiver(Receiver receiver)
    {
      if (receiver instanceof WCESynthesizer.SynthesizerReceiver)
      {
        // If specified instance is a Synthesizer's receiver, 
        // get output port to play a Sequence object.
        outputPort = ((WCESynthesizer.SynthesizerReceiver)receiver).getOutputPort();
      }
      else
      {
        throw new UnsupportedOperationException("Unsupported receiver:" + receiver);
      }
      this.receiver = receiver;
    }

    public Receiver getReceiver()
    {
      return this.receiver;
    }

    public void close()
    {
      // do nothing
    }
  }

  protected WCESequencer()
  {
    // connect to WCESynthesizer
    getTransmitter().setReceiver(WCESynthesizer.getInstance().getReceiver());
  }

  public static WCESequencer getInstance()
  {
    return INSTANCE;
  }

  public MidiDevice.Info getDeviceInfo()
  {
    return WCEMidiDeviceProvider.SEQUENCER_INFO;
  }

  public synchronized void open() throws MidiUnavailableException
  {
    this.opened = true;
  }

  public synchronized void close()
  {
    this.opened = false;
  }

  public boolean isOpen()
  {
    return this.opened;
  }

  public long getMicrosecondPosition()
  {
    return -1L;
  }

  public int getMaxReceivers()
  {
    return 0;
  }

  public int getMaxTransmitters()
  {
    return -1;
  }

  public Receiver getReceiver()
                           throws MidiUnavailableException
  {
    throw new MidiUnavailableException();
  }

  public Transmitter getTransmitter()
  {
    return new SequencerTransmitter();
  }

  public void setSequence(Sequence sequence)
                 throws InvalidMidiDataException
  {
    this.sequence = sequence;
    if (this.outputPort != null)
    {
      this.outputPort.setSequence(sequence);
    }
  }

  public void setSequence(InputStream stream)
                 throws IOException,
                        InvalidMidiDataException
  {
    setSequence(MidiSystem.getSequence(stream));
  }

  public Sequence getSequence()
  {
    return this.sequence;
  }

  public synchronized void start()
  {
    if (!isOpen())
    {
      throw new IllegalStateException("sequencer not open");
    }
    this.running = true;
    if (this.outputPort != null)
    {
      this.outputPort.start();
    }
    else
    {
      throw new UnsupportedOperationException("not connected to WCEMidiOutputPort");
    }
  }

  public synchronized void stop()
  {
    if (isRunning())
    {
      this.running = false;
    }
    if (this.outputPort != null)
    {
      this.outputPort.stop();
    }
  }

  public boolean isRunning()
  {
    if (this.outputPort != null)
    {
      this.running =  this.outputPort.isPlaying();
    }
    return this.running;
  }

  public void startRecording()
  {
    throw new UnsupportedOperationException("Not implemented");
  }

  public void stopRecording()
  {
    throw new UnsupportedOperationException("Not implemented");
  }

  public boolean isRecording()
  {
    return false;
  }

  public void recordEnable(Track track, int channel)
  {
  }

  public void recordDisable(Track track)
  {
  }

  public float getTempoInBPM()
  {
    return 0.0f;
  }

  public void setTempoInBPM(float bpm)
  {
  }

  public float getTempoInMPQ()
  {
    return 0.0f;
  }

  public void setTempoInMPQ(float mpq)
  {
  }

  public void setTempoFactor(float factor)
  {
  }

  public float getTempoFactor()
  {
    return 1.0f;
  }

  public long getTickLength()
  {
    return 0L;
  }

  public long getTickPosition()
  {
    return 0L;
  }

  public void setTickPosition(long tick)
  {
  }

  public long getMicrosecondLength()
  {
    return 0L;
  }

  public void setMicrosecondPosition(long microseconds)
  {
  }

  public void setMasterSyncMode(Sequencer.SyncMode sync)
  {
  }

  public Sequencer.SyncMode getMasterSyncMode()
  {
    return null;
  }

  public Sequencer.SyncMode[] getMasterSyncModes()
  {
    return null;
  }

  public void setSlaveSyncMode(Sequencer.SyncMode sync)
  {
  }

  public Sequencer.SyncMode getSlaveSyncMode()
  {
    return null;
  }

  public Sequencer.SyncMode[] getSlaveSyncModes()
  {
    return null;
  }

  public void setTrackMute(int track,
                  boolean mute)
  {
  }

  public boolean getTrackMute(int track)
  {
    return false;
  }

  public void setTrackSolo(int track,
                  boolean solo)
  {
  }

  public boolean getTrackSolo(int track)
  {
    return false;
  }

  public boolean addMetaEventListener(MetaEventListener listener)
  {
    return false;
  }

  public void removeMetaEventListener(MetaEventListener listener)
  {
  }

  public int[] addControllerEventListener(ControllerEventListener listener,
                                   int[] controllers)
  {
    return new int[0];
  }

  public int[] removeControllerEventListener(ControllerEventListener listener,
                                    int[] controllers)
  {
    return new int[0];
  }

  public void setLoopStartPoint(long tick)
  {
  }

  public long getLoopStartPoint()

  {
    return 0L;
  }

  public void setLoopEndPoint(long tick)
  {
  }

  public long getLoopEndPoint()
  {
    return -1L;
  }

  public void setLoopCount(int count)
  {
  }

  public int getLoopCount()
  {
    return 0;
  }
}
