/*
 * JFugue - API for Music Programming
 * Copyright (C) 2003-2008  David Koelle
 *
 * http://www.jfugue.org 
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *  
 */

package org.jfugue;

import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiMessage;
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.Transmitter;

/**
 * Represents an attached MIDI device, such as a keyboard.  This class
 * uses javax.sound.MidiDevice, but is not derived from javax.sound.MidiDevice.
 * 
 * @author David Koelle
 * @version 3.0
 */
public class DeviceThatWillTransmitMidi 
{
    private MidiDevice device;
    private Transmitter transmitter;
    private MidiReceiverForTransmitterDevice mrftd;
//    private Receiver defaultReceiver;
    
    
    /**
     * Creates a new DeviceThatWillTransmitMidi using JFugue's Intelligent Device Resolver to pick the 
     * most likely device to open.
     * @throws MidiUnavailableException
     */
    public DeviceThatWillTransmitMidi() throws MidiUnavailableException
    {
        this.device = IntelligentDeviceResolver.selectTransmitterDevice();
        init();
    }
    
    public DeviceThatWillTransmitMidi(MidiDevice.Info info) throws MidiUnavailableException
    {
        this.device = MidiSystem.getMidiDevice(info);
        init();
    }
    
    private void init() throws MidiUnavailableException
    {
        try 
        {
            if (!(device.isOpen())) {
              device.open();
            }
    
            this.transmitter = device.getTransmitter();
            this.mrftd = new MidiReceiverForTransmitterDevice();
        } catch (MidiUnavailableException e)
        {
            device.close();
            throw e;
        }
    }
    
    public Transmitter getTransmitter()
    {
        return this.transmitter;
    }
    
    public void addParserListener(ParserListener listener)
    {
        this.mrftd.getParser().addParserListener(listener);
    }
    
    public void removeParserListener(ParserListener listener)
    {
        this.mrftd.getParser().removeParserListener(listener);
    }
    
    /**
     * Reads a pattern from the external device - use this to record the
     * keys you're pressing on the keyboard!  
     * 
     * This method will return a JFugue Pattern, which you can then 
     * manipulate to your heart's content.
     * 
     * @return The Pattern representing the music played on the device
     */
    public void startListening()
    {
        this.transmitter.setReceiver(this.mrftd);
    }
    
    public void stopListening()
    {
//        this.transmitter.setReceiver(this.defaultReceiver);
        device.close();
//        this.transmitter.close();
//        this.mrftd.close();
    }

    public void listenForMillis(long millis) throws InterruptedException
    {
        startListening();
        Thread.sleep(millis);
        stopListening();
    }
    
    public Pattern getPatternFromListening()
    {
        return this.mrftd.getPattern();
    }

    public Sequence getSequenceFromListening()
    {
        return this.mrftd.getSequence();
    }

    public void close()
    {
        transmitter.close();
        device.close();
//        this.mrftd.close();
//        this.transmitter.close();
    }
    
    class MidiReceiverForTransmitterDevice implements Receiver
    {
        private MidiParser parser;
        private Sequencer sequencer;
        private Receiver sequencerReceiver;
        private MusicStringRenderer renderer;
        
        public MidiReceiverForTransmitterDevice()
        {
            System.out.println("Built mrftd");
            
            parser = new MidiParser();  
            renderer = new MusicStringRenderer();
            parser.addParserListener(renderer);
            
            try {
                sequencer = MidiSystem.getSequencer();
                sequencerReceiver = sequencer.getReceiver();
            } catch (MidiUnavailableException e)
            {
                e.printStackTrace();
            }
        }
        
        public Parser getParser()
        {
            return this.parser;
        }
        
        public void send(MidiMessage message, long timestamp)
        {
            System.out.println("Parsing "+message+" ts: "+timestamp);
            parser.parse(message, timestamp / (1000 * 4));
            sequencerReceiver.send(message, timestamp);
        }
        
        public void close() 
        {
            sequencerReceiver.close();
        }
        
        public Pattern getPattern()
        {
            return renderer.getPattern();
        }

        public Sequence getSequence()
        {
            return sequencer.getSequence();
        }
    
    }
}