int MidiKeyboardComponent::remappedXYToNote (const Point<int>& pos, float& mousePositionVelocity) const
{
    if (pos.getY() < blackNoteLength)
    {
        for (int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
        {
            for (int i = 0; i < 5; ++i)
            {
                const int note = octaveStart + blackNotes [i];

                if (note >= rangeStart && note <= rangeEnd)
                {
                    int kx, kw;
                    getKeyPos (note, kx, kw);
                    kx += xOffset;

                    if (pos.getX() >= kx && pos.getX() < kx + kw)
                    {
                        mousePositionVelocity = pos.getY() / (float) blackNoteLength;
                        return note;
                    }
                }
            }
        }
    }

    for (int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
    {
        for (int i = 0; i < 7; ++i)
        {
            const int note = octaveStart + whiteNotes [i];

            if (note >= rangeStart && note <= rangeEnd)
            {
                int kx, kw;
                getKeyPos (note, kx, kw);
                kx += xOffset;

                if (pos.getX() >= kx && pos.getX() < kx + kw)
                {
                    const int whiteNoteLength = (orientation == horizontalKeyboard) ? getHeight() : getWidth();
                    mousePositionVelocity = pos.getY() / (float) whiteNoteLength;
                    return note;
                }
            }
        }
    }

    mousePositionVelocity = 0;
    return -1;
}
Rectangle<float> MidiKeyboardComponent::getRectangleForKey (int note) const
{
    jassert (note >= rangeStart && note <= rangeEnd);

    auto pos = getKeyPos (note);
    auto x = pos.getStart();
    auto w = pos.getLength();

    if (MidiMessage::isMidiNoteBlack (note))
    {
        auto blackNoteLength = getBlackNoteLength();

        switch (orientation)
        {
            case horizontalKeyboard:            return { x, 0, w, blackNoteLength };
            case verticalKeyboardFacingLeft:    return { getWidth() - blackNoteLength, x, blackNoteLength, w };
            case verticalKeyboardFacingRight:   return { 0, getHeight() - x - w, blackNoteLength, w };
            default:                            jassertfalse; break;
        }
    }
    else
    {
        switch (orientation)
        {
            case horizontalKeyboard:            return { x, 0, w, (float) getHeight() };
            case verticalKeyboardFacingLeft:    return { 0, x, (float) getWidth(), w };
            case verticalKeyboardFacingRight:   return { 0, getHeight() - x - w, (float) getWidth(), w };
            default:                            jassertfalse; break;
        }
    }

    return {};
}
/**
* insertKeyVal: inserted the pair keys - val, these keys must be haven't set yet!
* @keys the keys
* @keynum number of keys passed in
* @val value for these keys
*/
bool MapArraySR::insertKeyVal( int* keys, int keynum, bool issorted,int value )
{
	int* ikeys = new int[ keynum ];
	memcpy( ikeys, keys, sizeof( int ) * keynum );
	int pos = 0;
	if( !getKeyPos( ikeys, keynum ,issorted, pos))
	{
		cout<<"Unable to compute the position for the keys!"<<endl;
		delete []ikeys;
		return false;
	}
	
	//////////////////////////////////////////////////////////////////////////
	//cout<<"pos:"<<pos<<endl;
	//////////////////////////////////////////////////////////////////////////

	if( posinval[ pos ] != -1 )
	{
		cout<<"These keys have already been set!"<<endl;
		delete []ikeys;
		return false;
	}
	
	val.push_back( value );
	posinval[ pos ] = val.size() - 1;
	delete []ikeys;
	return true;
}
/**
* insertKeyVal: inserted the pair keys - val, value for these keys will be replaced if exists, otherwise, set it!
* @keys the keys
* @keynum number of keys passed in
* @val value for these keys
*/
bool MapArraySR::replaceKeyVal( int* keys, int keynum,bool issorted, int value )
{
	int* ikeys = new int[ keynum ];
	memcpy( ikeys, keys, sizeof( int ) * keynum );
	//compute the position
	int pos = 0;
	if( !getKeyPos( ikeys,  keynum , issorted,pos))
	{
		cout<<"Unable to compute the position for the keys!"<<endl;
		delete []ikeys;
		return false;
	}

	if( posinval[ pos ] != -1 )
	{
		val[ posinval[ pos ] ] = value;
	}
	else
	{
		val.push_back( value );
		posinval[ pos ] = val.size() - 1;
	}	
	delete []ikeys;
	return true;
}
int MidiKeyboardComponent::remappedXYToNote (Point<float> pos, float& mousePositionVelocity) const
{
    auto blackNoteLength = getBlackNoteLength();

    if (pos.getY() < blackNoteLength)
    {
        for (int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
        {
            for (int i = 0; i < 5; ++i)
            {
                auto note = octaveStart + blackNotes[i];

                if (note >= rangeStart && note <= rangeEnd)
                {
                    if (getKeyPos (note).contains (pos.x - xOffset))
                    {
                        mousePositionVelocity = jmax (0.0f, pos.y / blackNoteLength);
                        return note;
                    }
                }
            }
        }
    }

    for (int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
    {
        for (int i = 0; i < 7; ++i)
        {
            auto note = octaveStart + whiteNotes[i];

            if (note >= rangeStart && note <= rangeEnd)
            {
                if (getKeyPos (note).contains (pos.x - xOffset))
                {
                    auto whiteNoteLength = (orientation == horizontalKeyboard) ? getHeight() : getWidth();
                    mousePositionVelocity = jmax (0.0f, pos.y / (float) whiteNoteLength);
                    return note;
                }
            }
        }
    }

    mousePositionVelocity = 0;
    return -1;
}
//==============================================================================
void MidiKeyboardComponent::repaintNote (const int noteNum)
{
    if (noteNum >= rangeStart && noteNum <= rangeEnd)
    {
        int x, w;
        getKeyPos (noteNum, x, w);

        if (orientation == horizontalKeyboard)
            repaint (x, 0, w, getHeight());
        else if (orientation == verticalKeyboardFacingLeft)
            repaint (0, x, getWidth(), w);
        else if (orientation == verticalKeyboardFacingRight)
            repaint (0, getHeight() - x - w, getWidth(), w);
    }
}
Rectangle<int> MidiKeyboardComponent::getWhiteNotePos (int noteNum) const
{
    int x, w;
    getKeyPos (noteNum, x, w);
    Rectangle<int> pos;

    switch (orientation)
    {
        case horizontalKeyboard:            pos.setBounds (x, 0, w, getHeight()); break;
        case verticalKeyboardFacingLeft:    pos.setBounds (0, x, getWidth(), w); break;
        case verticalKeyboardFacingRight:   pos.setBounds (0, getHeight() - x - w, getWidth(), w); break;
        default: break;
    }

    return pos;
}
bool MapArraySR::getKeyVal(int * keys, int keynum,bool issorted, int& value )
{
	int* ikeys = new int[ keynum ];
	memcpy( ikeys, keys, sizeof( int ) * keynum );
	//compute the position
	int pos = 0;
	if( !getKeyPos(  ikeys, keynum ,issorted, pos))
	{
		cout<<"Unable to compute the position for the keys!"<<endl;
		delete []ikeys;
		return false;
	}

	value = posinval[ pos ];
	if( value == -1 )	//no corresponding value to these keys
	{
		delete []ikeys;
		return false;
	}
	value = val[ value ];	//the real value to these keys
	delete []ikeys;
	return true;
}
void MidiKeyboardComponent::resized()
{
    int w = getWidth();
    int h = getHeight();

    if (w > 0 && h > 0)
    {
        if (orientation != horizontalKeyboard)
            std::swap (w, h);

        blackNoteLength = roundToInt (h * 0.7f);

        int kx2, kw2;
        getKeyPos (rangeEnd, kx2, kw2);

        kx2 += kw2;

        if (firstKey != rangeStart)
        {
            int kx1, kw1;
            getKeyPos (rangeStart, kx1, kw1);

            if (kx2 - kx1 <= w)
            {
                firstKey = rangeStart;
                sendChangeMessage();
                repaint();
            }
        }

        const bool showScrollButtons = canScroll && (firstKey > rangeStart || kx2 > w + xOffset * 2);

        scrollDown->setVisible (showScrollButtons);
        scrollUp->setVisible (showScrollButtons);

        xOffset = 0;

        if (showScrollButtons)
        {
            const int scrollButtonW = jmin (12, w / 2);

            if (orientation == horizontalKeyboard)
            {
                scrollDown->setBounds (0, 0, scrollButtonW, getHeight());
                scrollUp->setBounds (getWidth() - scrollButtonW, 0, scrollButtonW, getHeight());
            }
            else if (orientation == verticalKeyboardFacingLeft)
            {
                scrollDown->setBounds (0, 0, getWidth(), scrollButtonW);
                scrollUp->setBounds (0, getHeight() - scrollButtonW, getWidth(), scrollButtonW);
            }
            else if (orientation == verticalKeyboardFacingRight)
            {
                scrollDown->setBounds (0, getHeight() - scrollButtonW, getWidth(), scrollButtonW);
                scrollUp->setBounds (0, 0, getWidth(), scrollButtonW);
            }

            int endOfLastKey, kw;
            getKeyPos (rangeEnd, endOfLastKey, kw);
            endOfLastKey += kw;

            float mousePositionVelocity;
            const int spaceAvailable = w - scrollButtonW * 2;
            const int lastStartKey = remappedXYToNote (Point<int> (endOfLastKey - spaceAvailable, 0), mousePositionVelocity) + 1;

            if (lastStartKey >= 0 && firstKey > lastStartKey)
            {
                firstKey = jlimit (rangeStart, rangeEnd, lastStartKey);
                sendChangeMessage();
            }

            int newOffset = 0;
            getKeyPos (firstKey, newOffset, kw);
            xOffset = newOffset - scrollButtonW;
        }
        else
        {
            firstKey = rangeStart;
        }

        timerCallback();
        repaint();
    }
}
void MidiKeyboardComponent::paint (Graphics& g)
{
    g.fillAll (Colours::white.overlaidWith (findColour (whiteNoteColourId)));

    const Colour lineColour (findColour (keySeparatorLineColourId));
    const Colour textColour (findColour (textLabelColourId));

    int x, w, octave;

    for (octave = 0; octave < 128; octave += 12)
    {
        for (int white = 0; white < 7; ++white)
        {
            const int noteNum = octave + whiteNotes [white];

            if (noteNum >= rangeStart && noteNum <= rangeEnd)
            {
                getKeyPos (noteNum, x, w);

                if (orientation == horizontalKeyboard)
                    drawWhiteNote (noteNum, g, x, 0, w, getHeight(),
                                   state.isNoteOnForChannels (midiInChannelMask, noteNum),
                                   noteUnderMouse == noteNum,
                                   lineColour, textColour);
                else if (orientation == verticalKeyboardFacingLeft)
                    drawWhiteNote (noteNum, g, 0, x, getWidth(), w,
                                   state.isNoteOnForChannels (midiInChannelMask, noteNum),
                                   noteUnderMouse == noteNum,
                                   lineColour, textColour);
                else if (orientation == verticalKeyboardFacingRight)
                    drawWhiteNote (noteNum, g, 0, getHeight() - x - w, getWidth(), w,
                                   state.isNoteOnForChannels (midiInChannelMask, noteNum),
                                   noteUnderMouse == noteNum,
                                   lineColour, textColour);
            }
        }
    }

    float x1 = 0.0f, y1 = 0.0f, x2 = 0.0f, y2 = 0.0f;

    if (orientation == verticalKeyboardFacingLeft)
    {
        x1 = getWidth() - 1.0f;
        x2 = getWidth() - 5.0f;
    }
    else if (orientation == verticalKeyboardFacingRight)
        x2 = 5.0f;
    else
        y2 = 5.0f;

    g.setGradientFill (ColourGradient (Colours::black.withAlpha (0.3f), x1, y1,
                                       Colours::transparentBlack, x2, y2, false));

    getKeyPos (rangeEnd, x, w);
    x += w;

    if (orientation == verticalKeyboardFacingLeft)
        g.fillRect (getWidth() - 5, 0, 5, x);
    else if (orientation == verticalKeyboardFacingRight)
        g.fillRect (0, 0, 5, x);
    else
        g.fillRect (0, 0, x, 5);

    g.setColour (lineColour);

    if (orientation == verticalKeyboardFacingLeft)
        g.fillRect (0, 0, 1, x);
    else if (orientation == verticalKeyboardFacingRight)
        g.fillRect (getWidth() - 1, 0, 1, x);
    else
        g.fillRect (0, getHeight() - 1, x, 1);

    const Colour blackNoteColour (findColour (blackNoteColourId));

    for (octave = 0; octave < 128; octave += 12)
    {
        for (int black = 0; black < 5; ++black)
        {
            const int noteNum = octave + blackNotes [black];

            if (noteNum >= rangeStart && noteNum <= rangeEnd)
            {
                getKeyPos (noteNum, x, w);

                if (orientation == horizontalKeyboard)
                    drawBlackNote (noteNum, g, x, 0, w, blackNoteLength,
                                   state.isNoteOnForChannels (midiInChannelMask, noteNum),
                                   noteUnderMouse == noteNum,
                                   blackNoteColour);
                else if (orientation == verticalKeyboardFacingLeft)
                    drawBlackNote (noteNum, g, getWidth() - blackNoteLength, x, blackNoteLength, w,
                                   state.isNoteOnForChannels (midiInChannelMask, noteNum),
                                   noteUnderMouse == noteNum,
                                   blackNoteColour);
                else if (orientation == verticalKeyboardFacingRight)
                    drawBlackNote (noteNum, g, 0, getHeight() - x - w, blackNoteLength, w,
                                   state.isNoteOnForChannels (midiInChannelMask, noteNum),
                                   noteUnderMouse == noteNum,
                                   blackNoteColour);
            }
        }
    }
}
int MidiKeyboardComponent::getKeyStartPosition (const int midiNoteNumber) const
{
    int x, y;
    getKeyPos (midiNoteNumber, x, y);
    return x;
}
void MidiKeyboardComponent::resized()
{
    auto w = getWidth();
    auto h = getHeight();

    if (w > 0 && h > 0)
    {
        if (orientation != horizontalKeyboard)
            std::swap (w, h);

        auto kx2 = getKeyPos (rangeEnd).getEnd();

        if ((int) firstKey != rangeStart)
        {
            auto kx1 = getKeyPos (rangeStart).getStart();

            if (kx2 - kx1 <= w)
            {
                firstKey = (float) rangeStart;
                sendChangeMessage();
                repaint();
            }
        }

        scrollDown->setVisible (canScroll && firstKey > (float) rangeStart);

        xOffset = 0;

        if (canScroll)
        {
            auto scrollButtonW = jmin (scrollButtonWidth, w / 2);
            auto r = getLocalBounds();

            if (orientation == horizontalKeyboard)
            {
                scrollDown->setBounds (r.removeFromLeft  (scrollButtonW));
                scrollUp  ->setBounds (r.removeFromRight (scrollButtonW));
            }
            else if (orientation == verticalKeyboardFacingLeft)
            {
                scrollDown->setBounds (r.removeFromTop    (scrollButtonW));
                scrollUp  ->setBounds (r.removeFromBottom (scrollButtonW));
            }
            else
            {
                scrollDown->setBounds (r.removeFromBottom (scrollButtonW));
                scrollUp  ->setBounds (r.removeFromTop    (scrollButtonW));
            }

            auto endOfLastKey = getKeyPos (rangeEnd).getEnd();

            float mousePositionVelocity;
            auto spaceAvailable = w;
            auto lastStartKey = remappedXYToNote ({ endOfLastKey - spaceAvailable, 0 }, mousePositionVelocity) + 1;

            if (lastStartKey >= 0 && ((int) firstKey) > lastStartKey)
            {
                firstKey = (float) jlimit (rangeStart, rangeEnd, lastStartKey);
                sendChangeMessage();
            }

            xOffset = getKeyPos ((int) firstKey).getStart();
        }
        else
        {
            firstKey = (float) rangeStart;
        }

        scrollUp->setVisible (canScroll && getKeyPos (rangeEnd).getStart() > w);
        repaint();
    }
}
void MidiKeyboardComponent::paint (Graphics& g)
{
    g.fillAll (findColour (whiteNoteColourId));

    auto lineColour = findColour (keySeparatorLineColourId);
    auto textColour = findColour (textLabelColourId);

    for (int octave = 0; octave < 128; octave += 12)
    {
        for (int white = 0; white < 7; ++white)
        {
            auto noteNum = octave + whiteNotes[white];

            if (noteNum >= rangeStart && noteNum <= rangeEnd)
                drawWhiteNote (noteNum, g, getRectangleForKey (noteNum),
                               state.isNoteOnForChannels (midiInChannelMask, noteNum),
                               mouseOverNotes.contains (noteNum), lineColour, textColour);
        }
    }

    float x1 = 0.0f, y1 = 0.0f, x2 = 0.0f, y2 = 0.0f;
    auto width = getWidth();
    auto height = getHeight();

    if (orientation == verticalKeyboardFacingLeft)
    {
        x1 = width - 1.0f;
        x2 = width - 5.0f;
    }
    else if (orientation == verticalKeyboardFacingRight)
        x2 = 5.0f;
    else
        y2 = 5.0f;

    auto x = getKeyPos (rangeEnd).getEnd();
    auto shadowCol = findColour (shadowColourId);

    if (! shadowCol.isTransparent())
    {
        g.setGradientFill (ColourGradient (shadowCol, x1, y1, shadowCol.withAlpha (0.0f), x2, y2, false));

        switch (orientation)
        {
            case horizontalKeyboard:            g.fillRect (0.0f, 0.0f, x, 5.0f); break;
            case verticalKeyboardFacingLeft:    g.fillRect (width - 5.0f, 0.0f, 5.0f, x); break;
            case verticalKeyboardFacingRight:   g.fillRect (0.0f, 0.0f, 5.0f, x); break;
            default: break;
        }
    }

    if (! lineColour.isTransparent())
    {
        g.setColour (lineColour);

        switch (orientation)
        {
            case horizontalKeyboard:            g.fillRect (0.0f, height - 1.0f, x, 1.0f); break;
            case verticalKeyboardFacingLeft:    g.fillRect (0.0f, 0.0f, 1.0f, x); break;
            case verticalKeyboardFacingRight:   g.fillRect (width - 1.0f, 0.0f, 1.0f, x); break;
            default: break;
        }
    }

    auto blackNoteColour = findColour (blackNoteColourId);

    for (int octave = 0; octave < 128; octave += 12)
    {
        for (int black = 0; black < 5; ++black)
        {
            auto noteNum = octave + blackNotes[black];

            if (noteNum >= rangeStart && noteNum <= rangeEnd)
                drawBlackNote (noteNum, g, getRectangleForKey (noteNum),
                               state.isNoteOnForChannels (midiInChannelMask, noteNum),
                               mouseOverNotes.contains (noteNum), blackNoteColour);
        }
    }
}
float MidiKeyboardComponent::getTotalKeyboardWidth() const noexcept
{
    return getKeyPos (rangeEnd).getEnd();
}
float MidiKeyboardComponent::getKeyStartPosition (int midiNoteNumber) const
{
    return getKeyPos (midiNoteNumber).getStart();
}
void MidiKeyboardComponent::paint (Graphics& g)
{
    g.fillAll (Colours::white.overlaidWith (findColour (whiteNoteColourId)));

    const Colour lineColour (findColour (keySeparatorLineColourId));
    const Colour textColour (findColour (textLabelColourId));

    int x, w, octave;

    for (octave = 0; octave < 128; octave += 12)
    {
        for (int white = 0; white < 7; ++white)
        {
            const int noteNum = octave + whiteNotes [white];

            if (noteNum >= rangeStart && noteNum <= rangeEnd)
            {
                const Rectangle<int> pos (getWhiteNotePos (noteNum));

                drawWhiteNote (noteNum, g, pos.getX(), pos.getY(), pos.getWidth(), pos.getHeight(),
                               state.isNoteOnForChannels (midiInChannelMask, noteNum),
                               mouseOverNotes.contains (noteNum), lineColour, textColour);
            }
        }
    }

    float x1 = 0.0f, y1 = 0.0f, x2 = 0.0f, y2 = 0.0f;

    if (orientation == verticalKeyboardFacingLeft)
    {
        x1 = getWidth() - 1.0f;
        x2 = getWidth() - 5.0f;
    }
    else if (orientation == verticalKeyboardFacingRight)
        x2 = 5.0f;
    else
        y2 = 5.0f;

    g.setGradientFill (ColourGradient (Colours::black.withAlpha (0.3f), x1, y1,
                                       Colours::transparentBlack, x2, y2, false));

    getKeyPos (rangeEnd, x, w);
    x += w;

    switch (orientation)
    {
        case horizontalKeyboard:            g.fillRect (0, 0, x, 5); break;
        case verticalKeyboardFacingLeft:    g.fillRect (getWidth() - 5, 0, 5, x); break;
        case verticalKeyboardFacingRight:   g.fillRect (0, 0, 5, x); break;
        default: break;
    }

    g.setColour (lineColour);

    switch (orientation)
    {
        case horizontalKeyboard:            g.fillRect (0, getHeight() - 1, x, 1); break;
        case verticalKeyboardFacingLeft:    g.fillRect (0, 0, 1, x); break;
        case verticalKeyboardFacingRight:   g.fillRect (getWidth() - 1, 0, 1, x); break;
        default: break;
    }

    const Colour blackNoteColour (findColour (blackNoteColourId));

    for (octave = 0; octave < 128; octave += 12)
    {
        for (int black = 0; black < 5; ++black)
        {
            const int noteNum = octave + blackNotes [black];

            if (noteNum >= rangeStart && noteNum <= rangeEnd)
            {
                getKeyPos (noteNum, x, w);
                Rectangle<int> pos;

                switch (orientation)
                {
                    case horizontalKeyboard:            pos.setBounds (x, 0, w, blackNoteLength); break;
                    case verticalKeyboardFacingLeft:    pos.setBounds (getWidth() - blackNoteLength, x, blackNoteLength, w); break;
                    case verticalKeyboardFacingRight:   pos.setBounds (0, getHeight() - x - w, blackNoteLength, w); break;
                    default: break;
                }

                drawBlackNote (noteNum, g, pos.getX(), pos.getY(), pos.getWidth(), pos.getHeight(),
                               state.isNoteOnForChannels (midiInChannelMask, noteNum),
                               mouseOverNotes.contains (noteNum), blackNoteColour);
            }
        }
    }
}