//
// C++ Implementation: alsaseqmidiinput
//
// Description: 
//
//
// Author: Predrag Viceic <viceic@net2000.ch>, (C) 2005
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "alsaseqmidiinput.h"
#include "midiinput.h"

AlsaSeqMidiInput::AlsaSeqMidiInput(QObject *parent, const char *name)
 : MidiInput(parent,name)
{
    noteAssignements=new noteAssignements_t[127];
    for(int i=0;i<127;i++){
        noteAssignements[i].start=-1;
        noteAssignements[i].stop=-1;
    }
  
  
  
    seq_handle=0;
    if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK) < 0) {
        fprintf(stderr, "Error opening ALSA sequencer.\n");
    }
    cout<<"ALSA sequencer successfully opened.\n";
    snd_seq_set_client_name(seq_handle, "Freecycle");
    if ((exportedClientPortid = snd_seq_create_simple_port(seq_handle, "Freecycle",
                SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
                SND_SEQ_PORT_TYPE_APPLICATION)) < 0) {
        fprintf(stderr, "Error creating sequencer port.\n");
    }
    exportedClientClientid=snd_seq_client_id(seq_handle);
    exportedClient=QString::number(exportedClientClientid)+":"+QString::number(exportedClientPortid)+" Freecycle";
    selectedClient=NONE;
    cout<<"created the MIDI port "<<exportedClient<<"\n";
    threadInitDone=false;
  
}

AlsaSeqMidiInput::~AlsaSeqMidiInput()
{
    snd_seq_close (seq_handle);
}

void AlsaSeqMidiInput::initThread(){
    npfd = snd_seq_poll_descriptors_count(seq_handle, POLLIN);
    pfd = (struct pollfd *)alloca(npfd * sizeof(struct pollfd));
    snd_seq_poll_descriptors(seq_handle, pfd, npfd, POLLIN);
    threadInitDone=true;
}


int AlsaSeqMidiInput::openDevice(QString device){
    if(selectedClient!=NONE){
        snd_seq_port_subscribe_t *subs;
        snd_seq_port_subscribe_alloca(&subs);
        snd_seq_addr_t sender, dest;
        sender.client = subscribedToClient;
        sender.port = subscribedToPort;
        dest.client = exportedClientClientid;
        dest.port = exportedClientPortid;
        snd_seq_port_subscribe_set_sender(subs, &sender);
        snd_seq_port_subscribe_set_dest(subs, &dest);
        int ret=snd_seq_unsubscribe_port(seq_handle, subs);
        if(ret<0){
                cout<<"("<<ret<<") "<<"Cannot unsubscribe!\n";
            }else{
               cout<<"Removed MIDI port subscription\n";
               selectedClient=NONE;
                //if(pfd!=0) zap(pfd);
            }
    }
    if(device!=NONE){
        QString client_port=QStringList::split(" ",device).first();
        QString name=QStringList::split(client_port,device).last();
        int client=QStringList::split(":",client_port).first().toInt();
        int port=QStringList::split(":",client_port).last().toInt();
        snd_seq_addr_t sender, dest;
        sender.client = client;
        sender.port = port;
        dest.client = exportedClientClientid;
        dest.port = exportedClientPortid;
        snd_seq_port_subscribe_t *subs;
        snd_seq_port_subscribe_alloca(&subs);
        snd_seq_port_subscribe_set_sender(subs, &sender);
        snd_seq_port_subscribe_set_dest(subs, &dest);
        int ret=snd_seq_subscribe_port(seq_handle, subs);
        if(ret<0){
            cout<<"("<<ret<<") "<<"Cannot subscribe to "<<client<<":"<<port<<" "<<name<<"!\n";
            return ret;
        }
        
        subscribedToClient=client;
        subscribedToPort=port;
        
        
        selectedClient=device;
        cout<<"Subscribed to "<<client<<":"<<port<<" "<<name<<"\n";
        
        
        
        return ret;
     }
}
void AlsaSeqMidiInput::process(){
  if(!threadInitDone) initThread();
  static int currently_playing=-1;
  if(poll(pfd, npfd, 10000) > 0){
    snd_seq_event_t* ev;
    do {
        if(snd_seq_event_input(seq_handle, &ev)>=0){
            //printMessage(*(ev->data.raw32.d));
            if(ev->type==SND_SEQ_EVENT_STOP){
                soundPlayer->setPlaying(FALSE);
                cout<<"stop received\n";
            }else if(ev->type==SND_SEQ_EVENT_NOTEON || ev->type==SND_SEQ_EVENT_NOTEOFF){
                if(ev->type==SND_SEQ_EVENT_NOTEON){
                    playNote(ev->data.note.note);
                    currently_playing=ev->data.note.note;
                }else if(ev->type==SND_SEQ_EVENT_NOTEOFF){
                    if (currently_playing==ev->data.note.note) soundPlayer->setPlaying(FALSE);
                }
                emitMidiNote(ev->type==SND_SEQ_EVENT_NOTEON,ev->data.note.note, ev->data.note.velocity); 
            }
            snd_seq_free_event(ev);
        }
    } while (snd_seq_event_input_pending(seq_handle, 0) > 0);
  }
}
void AlsaSeqMidiInput::printMessage(int msg){
    cout<<msg<<"\n";
}
QString AlsaSeqMidiInput::getSelectedDeviceString(){
    return selectedClient;
}
QString AlsaSeqMidiInput::getDefaultDeviceString(){
    return NONE;
}
QStringList AlsaSeqMidiInput::getDevices(){
    QStringList result;
    snd_seq_client_info_t *cinfo;
    snd_seq_port_info_t *pinfo;
    snd_seq_client_info_alloca(&cinfo);
    snd_seq_port_info_alloca(&pinfo);
    snd_seq_client_info_set_client(cinfo, -1);
    unsigned int caps;
    
    while (snd_seq_query_next_client(seq_handle, cinfo) == 0) {
        snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
        snd_seq_port_info_set_port(pinfo, -1);
        while (snd_seq_query_next_port(seq_handle, pinfo) == 0) {
            if (snd_seq_port_info_get_client(pinfo) == SND_SEQ_CLIENT_SYSTEM)
                continue; /* ignore Timer and Announce ports on client 0 */
            caps = snd_seq_port_info_get_capability(pinfo);
            if (!(caps & SND_SEQ_PORT_CAP_SUBS_READ))
                continue; /* ignore if you cannot read or write port */
            if (caps & SND_SEQ_PORT_CAP_SUBS_READ) {
                    result.append(QString::number(snd_seq_port_info_get_client(pinfo))+":"+
                                    QString::number(snd_seq_port_info_get_port(pinfo))+" "+
                                    QString(snd_seq_port_info_get_name(pinfo)));
            }
        }
    }
    result.append(NONE);
    return result;
}
void AlsaSeqMidiInput::setMidiChannel(int c){
}


