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

KeyboardView::KeyboardView(QCanvas* canvas)
 : QCanvasRectangle(canvas)
{
    arrowPressed=NONE;
    currentOctave=3;
    maxNbOctaves=10;
    whiteWidth=10;
    blackWidth=6;
    blackHeight=25;
    octaveheight=40;
    octavewidth=whiteWidth*7;
    visibleNbOctaves=4;
    arrowWidth=16;
    headerHeight=30;
    setSize(octavewidth*visibleNbOctaves+2*arrowWidth,octaveheight+headerHeight);
    setZ(10);
    initKeys();
    invMidi=IM_NONE;
    transposeKeyClicked=TKC_NONE;
    aaKeyClicked=AAKC_NONE;
    leftLoop=-1;
    rightLoop=-1;
}


KeyboardView::~KeyboardView()
{
}

/*!
    \fn KeyboardView::setY(double y)
 */
void KeyboardView::setY(double y)
{
    int y2=int(y<0?0:y>canvas()->height()-rect().height()-20?canvas()->height()-rect().height()-20:y);
    QCanvasRectangle::setY(y2);
}


/*!
    \fn KeyboardView::setX(double x)
 */
void KeyboardView::setX(double x)
{
    int x2=int(x>canvas()->width()-rect().width()-20?canvas()->width()-rect().width()-20:x);
    QCanvasRectangle::setX(x2);
}


/*!
    \fn KeyboardView::drawShape(QPainter & p)
 */
void KeyboardView::drawShape(QPainter & p)
{
    QFont font;
    font.setPixelSize(10);
    p.setFont(font);
    RasterOp oldRop=p.rasterOp();
    p.setRasterOp(AndROP);
    QPen oldpen=p.pen();
    p.setPen(QPen(gray,2));
    QColor bgColor(100,100,100);
    p.fillRect(rect(),QBrush(bgColor));
    p.drawRect(rect());
    p.setPen(QPen(black,1));
    p.drawRect(rect().left()+arrowWidth,rect().top()+headerHeight,rect().width()-arrowWidth*2,octaveheight);
    
    QFontMetrics fm(font);
    QString transNoteTextS(" TRANSPOSE NOTE: ");
    QString transOctaveTextS(" OCTAVE: ");
    QString eleetTextS(" Freecycle ");
    
    QString aaMidiLoopTextS(" ASSIGN MIDI LOOP: ");
    QString aaMidiAllTextS(" FOR ALL: ");
    QString clearMidiTextS(" CLEAR: ");

    
    
    QRect transNoteTextR(fm.boundingRect(transNoteTextS));
    QRect transOctaveTextR(fm.boundingRect(transOctaveTextS));
    QRect eleetTextR(fm.boundingRect(eleetTextS));
    
    QRect aaMidiLoopTextR(fm.boundingRect(aaMidiLoopTextS));
    QRect aaMidiAllTextR(fm.boundingRect(aaMidiAllTextS));
    QRect clearMidiTextR(fm.boundingRect(clearMidiTextS));
    
    
    transNoteTextR.moveTopLeft(QPoint(rect().left()+5, rect().top()+6));
    
    noteDownR.setRect(transNoteTextR.right()+5,rect().top()+3,12,13);
    noteUpR.setRect(noteDownR.right()+5,noteDownR.top(),12,13);
    
    transOctaveTextR.moveTopLeft(QPoint(noteUpR.right()+5, rect().top()+6));
    
    octaveDownR.setRect(transOctaveTextR.right()+5,noteUpR.top(),15,13);
    octaveUpR.setRect(octaveDownR.right()+5,octaveDownR.top(),15,13);
    
    eleetTextR.moveTopRight(QPoint(rect().right()-5, rect().top()+6));
    
    aaMidiLoopTextR.moveTopLeft(QPoint(rect().left()+5, transNoteTextR.bottom()+8));
    aaMidiLoopR.setRect(aaMidiLoopTextR.right()+5,aaMidiLoopTextR.top()-3,12,13);
    aaMidiAllTextR.moveTopLeft(QPoint(aaMidiLoopR.right()+5, aaMidiLoopTextR.top()));
    aaMidiAllR.setRect(aaMidiAllTextR.right()+5,aaMidiLoopTextR.top()-3,12,13);
    clearMidiTextR.moveTopLeft(QPoint(aaMidiAllR.right()+5, aaMidiLoopTextR.top()));
    clearMidiR.setRect(clearMidiTextR.right()+5,aaMidiLoopTextR.top()-3,12,13);
    
    
    QColor tk_notclicked(220,220,220);
    QColor tk_clicked(255,255,255);
    p.setRasterOp(CopyROP);
    
    p.setPen(white);
    p.drawText(transNoteTextR,Qt::AlignCenter, transNoteTextS);
    p.drawText(transOctaveTextR,Qt::AlignCenter, transOctaveTextS);
    p.drawText(eleetTextR,Qt::AlignCenter, eleetTextS);
    p.drawText(aaMidiLoopTextR,Qt::AlignCenter, aaMidiLoopTextS);
    p.drawText(aaMidiAllTextR,Qt::AlignCenter, aaMidiAllTextS);
    p.drawText(clearMidiTextR,Qt::AlignCenter, clearMidiTextS);
    p.setPen(black);
    
    if(transposeKeyClicked==TKC_NOTE_DOWN)
        p.fillRect(noteDownR,QBrush(tk_clicked));
    else p.fillRect(noteDownR,QBrush(tk_notclicked));
    p.drawRect(noteDownR);
    p.drawText(noteDownR,Qt::AlignCenter,"<");
    
    if(transposeKeyClicked==TKC_NOTE_UP)
        p.fillRect(noteUpR,QBrush(tk_clicked));
    else p.fillRect(noteUpR,QBrush(tk_notclicked));
    p.drawRect(noteUpR);
     p.drawText(noteUpR,Qt::AlignCenter,">");
     
    if(transposeKeyClicked==TKC_OCTAVE_DOWN)
        p.fillRect(octaveDownR,QBrush(tk_clicked));
    else p.fillRect(octaveDownR,QBrush(tk_notclicked));
    p.drawRect(octaveDownR);
     p.drawText(octaveDownR,Qt::AlignCenter,"<<");
     
    if(transposeKeyClicked==TKC_OCTAVE_UP)
        p.fillRect(octaveUpR,QBrush(tk_clicked));
    else 
        p.fillRect(octaveUpR,QBrush(tk_notclicked));
    p.drawRect(octaveUpR);
     p.drawText(octaveUpR,Qt::AlignCenter,">>");
     
    if(aaKeyClicked==AAKC_ALL)
        p.fillRect(aaMidiAllR,QBrush(tk_clicked));
    else if(getCurrentKey()==-1)
         p.fillRect(aaMidiAllR,QBrush(red));
    else p.fillRect(aaMidiAllR,QBrush(tk_notclicked));
    p.drawRect(aaMidiAllR);
     p.drawText(aaMidiAllR,Qt::AlignCenter,"X");
     
    if(aaKeyClicked==AAKC_LOOP)
        p.fillRect(aaMidiLoopR,QBrush(tk_clicked));
    else if(getCurrentKey()==-1)
         p.fillRect(aaMidiLoopR,QBrush(red));
    else p.fillRect(aaMidiLoopR,QBrush(tk_notclicked));
    p.drawRect(aaMidiLoopR);
     p.drawText(aaMidiLoopR,Qt::AlignCenter,"X");
     
     if(aaKeyClicked==AAKC_CLEAR)
        p.fillRect(clearMidiR,QBrush(tk_clicked));
    else 
        p.fillRect(clearMidiR,QBrush(tk_notclicked));
    p.drawRect(clearMidiR);
    p.drawText(clearMidiR,Qt::AlignCenter,"X");
    
    p.setRasterOp(AndROP);
    p.save();
    p.translate(x(),y());
    
    
    int y_arrow=((rect().height()-headerHeight)/2)-8+headerHeight; //arrow dimensions: 16x16
    QBrush arrowBrush(bgColor);
    if(arrowPressed==RIGHT){
        p.fillRect(QRect(0,headerHeight,arrowWidth,rect().height()-headerHeight),arrowBrush);
        p.fillRect(
            QRect(rect().width()-arrowWidth,headerHeight,arrowWidth,rect().height()-headerHeight),
            QBrush(black));
    }else if(arrowPressed==LEFT){
        p.fillRect(QRect(0,headerHeight,arrowWidth,rect().height()-headerHeight),QBrush(black));
        p.fillRect(
            QRect(rect().width()-arrowWidth,headerHeight,arrowWidth,rect().height()-headerHeight),
            arrowBrush);
    }else{
        p.fillRect(QRect(0,headerHeight,arrowWidth,rect().height()-headerHeight),arrowBrush);
        p.fillRect(
                QRect(rect().width()-arrowWidth,headerHeight,arrowWidth,rect().height()-headerHeight),
                arrowBrush);
    }
    
    p.setRasterOp(oldRop);
    p.drawPixmap(0,y_arrow,QPixmap(qembed_findImage("1leftarrow-blue")));
    p.drawPixmap(rect().width()-arrowWidth,y_arrow,QPixmap(qembed_findImage("1rightarrow-blue")));
    p.translate(arrowWidth,headerHeight);
    KeyList::iterator it;
    for ( it = keys.begin(); it != keys.end(); ++it ){
        KeyboardView::Key current=(*it);
        QBrush brush(Qt::SolidPattern);
        if(current.note>=currentOctave*12 && current.note<currentOctave*12+visibleNbOctaves*12){
            
            if(current.midiOn==1) brush.setColor(green);
            else if(current.state==KEY_ASSIGNED) brush.setColor(red);
            else if(current.state==KEY_CURRENT) brush.setColor(blue);
            else if(current.color==0) brush.setColor(white);
            else brush.setColor(black);
            QRect keyRect(current.rect);
            keyRect.moveLeft(keyRect.left()-currentOctave*octavewidth);
            if (current.note % 12 ==0 || current.note % 12 ==5){
                //no black key before
                p.fillRect(keyRect,brush);
                p.drawLine(keyRect.topLeft(),keyRect.bottomLeft());
            }else if(current.note % 12 ==2 || current.note % 12 ==4 || current.note % 12 ==7 ||
                    current.note % 12 == 9 ||current.note % 12 ==11){
                //black key before
                QRect rectBottom=keyRect;
                QRect rectRight=keyRect;
                rectBottom.setTop(blackHeight);
                rectRight.setLeft(rectRight.right()-blackWidth);
                p.fillRect(rectBottom,brush);
                p.fillRect(rectRight,brush);
                p.drawLine(rectBottom.topLeft(),rectBottom.bottomLeft());
            }else{
                //black key
                p.fillRect(keyRect,brush);
                p.drawRect(keyRect);
            }
            if(current.note % 12 == 0){
                p.drawText(QRect(keyRect.x(),keyRect.y()+octaveheight-14,whiteWidth,12),
                            Qt::AlignCenter,QString::number(current.note / 12 -2));
            }
        }
    }
    p.restore();
    if(invMidi==FROM_LEFT){
      p.drawPixmap(rect().left()+arrowWidth,rect().bottom()-16,QPixmap(qembed_findImage("1leftarrow-small")));
    }else if (invMidi==FROM_RIGHT){
      p.drawPixmap(rect().right()-16-arrowWidth,rect().bottom()-16,QPixmap(qembed_findImage("1rightarrow-small")));
    }
    
    
    p.setPen(QPen(black,1));
    p.drawRect(rect().left()+arrowWidth,rect().top()+headerHeight,rect().width()-arrowWidth*2,octaveheight);
    p.setPen(oldpen);
    
}


/*!
    \fn KeyboardView::rtti() const
 */
int KeyboardView::rtti() const
{
    return RTTI_KEYBOARDRECT;
}


/*!
    \fn KeyboardView::initKeys()
 */
void KeyboardView::initKeys()
{
    keys.clear();
    int count=0;
    for (int j=0;j<maxNbOctaves;j++){
        for (int i=0;i<7;i++){
            Key key;
            key.color=0;
            key.note=count++;
            key.rect=QRect(whiteWidth*(i+j*7),0,whiteWidth,octaveheight);
            key.state=KEY_FREE;
            key.midiOn=0;
            key.beatlinesamplepos=-1;
            keys.append(key);
            if(i!=2 && i!=6){
                Key key;
                key.color=1;
                key.note=count++;
                key.rect=QRect(QRect(whiteWidth*((i+j*7+1))-blackWidth/2,0,blackWidth,blackHeight));
                key.state=KEY_FREE;
                key.midiOn=0;
                key.beatlinesamplepos=-1;
                keys.append(key);
            }
            
        }
    }
}


/*!
    \fn KeyboardView::mouseReleaseEvent(QMouseEvent * e)
 */
void KeyboardView::mouseReleaseEvent(QMouseEvent * /*e*/)
{
    arrowPressed=NONE;
    transposeKeyClicked=TKC_NONE;
    aaKeyClicked=AAKC_NONE;
    update();
}


/*!
    \fn KeyboardView::mousePressEvent(QMouseEvent * e)
 */
void KeyboardView::mousePressEvent(QMouseEvent * e)
{
    QPoint pos=e->pos();
    pos.setX(int(pos.x()-x()));
    pos.setY(int(pos.y()-y()));
    if(QRect(0,headerHeight,arrowWidth,rect().height()-headerHeight).contains(pos)){
        arrowPressed=LEFT;
        currentOctave=currentOctave>0?currentOctave-1:currentOctave;
        update();
        return;
    }else if(QRect(rect().width()-arrowWidth,
            headerHeight,arrowWidth,rect().height()-headerHeight).contains(pos)){
        arrowPressed=RIGHT;
        currentOctave=currentOctave<maxNbOctaves-visibleNbOctaves?currentOctave+1:currentOctave;
        update();
        return;
    }
    if(noteUpR.contains(e->pos())) {transposeKeyClicked=TKC_NOTE_UP;transposeAll(1);update();return;}
    else if(noteDownR.contains(e->pos())) {transposeKeyClicked=TKC_NOTE_DOWN;transposeAll(-1);update();return;}
    else if(octaveUpR.contains(e->pos())) {transposeKeyClicked=TKC_OCTAVE_UP;transposeAll(12);update();return;}
    else if(octaveDownR.contains(e->pos())) {transposeKeyClicked=TKC_OCTAVE_DOWN;transposeAll(-12);update();return;}
    else if(getCurrentKey()!=-1 && aaMidiAllR.contains(e->pos()))
        {aaKeyClicked=AAKC_ALL;assignMissingMidiAll(getCurrentKey());update();return;}
    else if(getCurrentKey()!=-1 && aaMidiLoopR.contains(e->pos()))
        {aaKeyClicked=AAKC_LOOP;assignMissingMidiLoop(getCurrentKey());update();return;}
    else if(clearMidiR.contains(e->pos())) {aaKeyClicked=AAKC_CLEAR;initKeys();update();return;}
    
    pos.setX(pos.x()-arrowWidth+currentOctave*octavewidth);
    pos.setY(pos.y()-headerHeight);
    KeyList::iterator it;
    for ( it = keys.begin(); it != keys.end(); ++it ){
        QRect currentRect=(*it).rect;
        if((*it).color==0) currentRect.setTop(blackHeight);
        if((*it).state==KEY_CURRENT){
            (*it).state=KEY_FREE;
            (*it).beatlinesamplepos=-1;
        }else if(currentRect.contains(pos)){
            if((*it).state!=KEY_ASSIGNED){
                (*it).state=KEY_CURRENT;
                (*it).beatlinesamplepos=getBeatlinePos();
            }
        }
    }
    update();
}


/*!
    \fn KeyboardView::bake()
 */
void KeyboardView::bake()
{
    KeyList::iterator it;
    for ( it = keys.begin(); it != keys.end(); ++it ){
        if((*it).state==KEY_CURRENT){
            (*it).state=KEY_ASSIGNED;
            break;
        }
    }
    update();
}


/*!
    \fn KeyboardView::setVisible ( bool yes )
 */
void KeyboardView::setVisible ( bool yes )
{
    if(!yes) bake();
    else{
        KeyList::iterator it;
        for ( it = keys.begin(); it != keys.end(); ++it ){
            if((*it).beatlinesamplepos==getBeatlinePos()){
                (*it).state=KEY_CURRENT;
                break;
            }
        }
    }
    QCanvasRectangle::setVisible(yes);
}


long KeyboardView::getBeatlinePos() const
{
  return beatlinePos;
}


void KeyboardView::setBeatlinePos(const long& theValue)
{
  beatlinePos = theValue;
}


/*!
    \fn KeyboardView::getMidiNoteFor(long samplepos)
 */
int KeyboardView::getMidiNoteFor(long samplepos)
{
    KeyList::iterator it;
    for ( it = keys.begin(); it != keys.end(); ++it ){
        if((*it).beatlinesamplepos==samplepos){
            return (*it).note;
        }
    }
    return -1;
}


KeyboardView::KeyList KeyboardView::getKeys()
{
  return keys;
}


void KeyboardView::setKeys(KeyboardView::KeyList theValue)
{
  keys = theValue;
}


/*!
    \fn KeyboardView::showMidiKey(int onoff,int key, int velocity)
 */
void KeyboardView::showMidiKey(int onoff, int _key, int /*velocity*/)
{
    invMidi=IM_NONE;
    if(_key>=currentOctave*12 && _key<currentOctave*12+visibleNbOctaves*12){
        KeyList::iterator it;
        for ( it = keys.begin(); it != keys.end(); ++it ){
            if((*it).note==_key){
                (*it).midiOn=onoff;
                //cout<<"Midi note: "<<(*it).note<<" onoff: "<<(*it).midiOn<<endl;
                break;
            }
        }
    }else if(_key<currentOctave*12 && onoff){
        invMidi=FROM_LEFT;
    }else if(onoff && _key>currentOctave*12+visibleNbOctaves*12){
        invMidi=FROM_RIGHT;
    }
    update();
}


/*!
    \fn KeyboardView::getSamplePosFor(int midiNote)
 */
long KeyboardView::getSamplePosFor(int midiNote)
{
    KeyList::iterator it;
    for ( it = keys.begin(); it != keys.end(); ++it ){
        if((*it).note==midiNote){
            return (*it).beatlinesamplepos;
        }
    }
    return -1;
}


/*!
    \fn KeyboardView::initKeys(QValueList <beatline_data>)
 */
void KeyboardView::initKeys(QValueList <beatline_data> bdl)
{
   initKeys(); //dirty, dirty ...
   for(uint i=0;i<bdl.count();i++){
       KeyboardView::Key* key=getKey(bdl[i].midiNote);
       if(key){
        key->beatlinesamplepos=bdl[i].position;
        key->state=KEY_ASSIGNED;
       } 
   }
}


/*!
    \fn KeyboardView::getKey(int midiNote)
 */
KeyboardView::Key* KeyboardView::getKey(int midiNote)
{
    for (uint i=0;i<keys.count();i++){
        if(keys[i].note==midiNote) return &keys[i];
    }
    return NULL;
}


/*!
    \fn KeyboardView::transposeAll(int amountNotes)
 */
void KeyboardView::transposeAll(int amountNotes)
{
    int correctedAmountNotes=amountNotes;
    if(amountNotes>0){
        int last_note=0;
        for (uint i=0;i<keys.count();i++){
            if(keys[i].beatlinesamplepos!=-1) last_note=keys[i].note;
        }
        if (last_note+amountNotes>maxNbOctaves*12-1) correctedAmountNotes=maxNbOctaves*12-1-last_note;
        if(correctedAmountNotes!=0){
            for (uint i=0;i<keys.count()-correctedAmountNotes;i++){
                int j=keys.count()-1-i;
                if(keys[j-correctedAmountNotes].beatlinesamplepos!=-1){
                    keys[j].beatlinesamplepos=keys[j-correctedAmountNotes].beatlinesamplepos;
                    keys[j].state=keys[j-correctedAmountNotes].state;
                    keys[j-correctedAmountNotes].beatlinesamplepos=-1;
                    keys[j-correctedAmountNotes].state=KEY_FREE;
                }
            }
        }
    }else{
        int first_note=127;
        for (int i=keys.count()-1;i>=0;i--){
            if(keys[i].beatlinesamplepos!=-1) first_note=keys[i].note;
        }
        if (first_note+amountNotes<0) correctedAmountNotes=-first_note;
        if(correctedAmountNotes!=0){
            for (uint i=0;i<keys.count()+correctedAmountNotes;i++){
                if(keys[i-correctedAmountNotes].beatlinesamplepos!=-1){
                    keys[i].beatlinesamplepos=keys[i-correctedAmountNotes].beatlinesamplepos;
                    keys[i].state=keys[i-correctedAmountNotes].state;
                    keys[i-correctedAmountNotes].beatlinesamplepos=-1;
                    keys[i-correctedAmountNotes].state=KEY_FREE;
                }
            }
        }
        
    }
}


/*!
    \fn KeyboardView::assignMissingMidiAll(int start_note)
 */
void KeyboardView::assignMissingMidiAll(int start_note)
{
   initKeys();
   qHeapSort(keys);
   for (uint i=0;i<beatlineData.count();i++){
        Key* key=getNextFreeKey(start_note);
        if(key){
            key->beatlinesamplepos=beatlineData[i].position;
            key->state=KEY_ASSIGNED;
        }
   }
}


/*!
    \fn KeyboardView::assignMissingMidiLoop(int start_note)
 */
void KeyboardView::assignMissingMidiLoop(int start_note)
{
   qHeapSort(keys);
   for (uint i=0;i<beatlineData.count();i++){
    if(beatlineData[i].position>=leftLoop && beatlineData[i].position<rightLoop){
        Key* key=getNextFreeKey(start_note);
        if(key){
            key->beatlinesamplepos=beatlineData[i].position;
            key->state=KEY_ASSIGNED;
        }
    }
   }
}


/*!
    \fn KeyboardView::getCurrentKey()
 */
int KeyboardView::getCurrentKey()
{
    KeyList::iterator it;
    for ( it = keys.begin(); it != keys.end(); ++it ){
        if((*it).state==KEY_CURRENT) return (*it).note;
    }
    return -1;
}




/*!
    \fn KeyboardView::setLoop(long l, long r)
 */
void KeyboardView::setLoop(long l, long r)
{
    leftLoop=l;
    rightLoop=r;
}


/*!
    \fn KeyboardView::setBeatlineData(QValueList <beatline_data> bld)
 */
void KeyboardView::setBeatlineData(QValueList <beatline_data> bld)
{
    beatlineData=bld;
}


/*!
    \fn KeyboardView::getNextFreeKey(int note)
 */
KeyboardView::Key* KeyboardView::getNextFreeKey(int note)
{
    for (uint i=0;i<keys.count();i++){
        if(keys[i].note>=note && keys[i].state!=KEY_ASSIGNED) return &keys[i];
    }
    for (uint i=0;i<keys.count();i++){
        if(keys[i].note<note && keys[i].state!=KEY_ASSIGNED) return &keys[i];
    }
    return NULL; //too many beatlines for the keyboard
}
