/*
(c) Copyright 1988-1994 Microsoft Corporation.

Microsoft Bars&Pipes Professional Source Code License

This License governs use of the accompanying Software.  
Microsoft hopes you find this Software useful.

You are licensed to do anything you want with the Software. 

In return, we simply require that you agree:

1.      Not to remove any copyright notices from the Software.

2.      That the Software comes "as is", with no warranties.  
        None whatsoever. This means no implied warranty of merchantability or 
        fitness for a particular purpose or any warranty of non-infringement.  
        Also, you must pass this disclaimer on whenever you distribute the Software.

3.      That we will not be liable for any of those types of damages known as indirect, 
        special, consequential, or incidental related to the Software or this License, 
        to the maximum extent the law permits. Also, you must pass this limitation of 
        liability on whenever you distribute the Software.

4.      That if you sue anyone over patents that you think may apply to the Software 
        for that person’s use of the Software, your license to the Software ends automatically.

5.      That the patent rights Microsoft is licensing only apply to the Software, 
        not to any derivatives you make.

6.      That your rights under the License end automatically if you breach this in any way.
*/

#include "/CompilerSpecific.h"
#include <intuition/intuition.h>
#include <string.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include "edit.h"
#include "notate.h"
#include "all.h"

extern struct Functions functions;

static struct NoteEvent *nextoftype(note)

struct NoteEvent *note;

{
    struct NoteEvent *scan = note->next;
    char status = note->status;
    for (;scan;scan = scan->next) {
        if (scan->status == status) return(scan);
    }
    return(note);
}

static struct NoteEvent *lastoftype(list,note)

struct NoteEvent *list, *note;

{
    struct NoteEvent *found = note;
    char status = note->status;
    for (;list;list = list->next) {
        if (list == note) return(found);
        if (list->status == status) {
            found = list;
        }
    }
    return(note);
}

static shiftmagnify(edit,direction,same)

struct Edit *edit;
short direction, same;

{
    struct NoteEvent *note = edit->selectnote;
    struct NoteEvent *next = 0;
    note->type &= 0xF;
    if (edit->editmode != E_MAGNIFY) return(0);
    if (direction == 1) {
        if (same && (note->type == EVENT_VOICE)) {
            edit->selectnote = nextoftype(note);
        }
        else if (note->next) edit->selectnote = note->next;
        return(1);
    }
    if (same && (note->type == EVENT_VOICE)) {
        edit->selectnote = lastoftype(edit->clip.events.first,note);
        return(1);
    }
    switch(edit->selecttype) {
        case TYPE_LYRIC :
            next = (struct NoteEvent *) List_Pred((struct ListItem *)edit->clip.lyrics.first,(struct ListItem *)note);
            break;
        case TYPE_CHORD :
            next = (struct NoteEvent *) List_Pred((struct ListItem *)edit->clip.chords.first,(struct ListItem *)note);
            break;
        case TYPE_KEY :
            next = (struct NoteEvent *) List_Pred((struct ListItem *)edit->clip.keys.first,(struct ListItem *)note);
            break;
        case TYPE_RHYTHM :
            next = (struct NoteEvent *) List_Pred((struct ListItem *)edit->clip.rhythm.first,(struct ListItem *)note);
            break;
        case TYPE_DYNAMICS :
            next = (struct NoteEvent *) List_Pred((struct ListItem *)edit->clip.dynamics.first,(struct ListItem *)note);
            break;
        case TYPE_TIMESIG :
            next = (struct NoteEvent *) List_Pred((struct ListItem *)edit->clip.timesig.first,(struct ListItem *)note);
            break;
        default :
            if (direction > 1) {
                edit->selectnote = next;
                return(1);
            }
            if (note->type == EVENT_VOICE) {
                next = (struct NoteEvent *) List_Pred((struct ListItem *)edit->clip.events.first,(struct ListItem *)note);
                if (!next) return(1);
                switch (next->status) {
                    case MIDI_NOTEON :
                        edit->selecttype = TYPE_STAFF;
                        break;
                    case MIDI_PTOUCH :
                        edit->selecttype = TYPE_PTOUCH;
                        break;
                    case MIDI_CCHANGE :
                        edit->selecttype = TYPE_CONTROL;
                        break;
                    case MIDI_PCHANGE :
                        edit->selecttype = TYPE_PATCH;
                        break;
                    case MIDI_MTOUCH :
                        edit->selecttype = TYPE_MTOUCH;
                        break;
                    case MIDI_PBEND :
                        edit->selecttype = TYPE_PBEND;
                        break;
                    default :
                        edit->selecttype = 0;
                }
            }
    }
    if (next) {
        edit->selectnote = next;
        return(1);
    }
    return(0);
}

static void drawchange(edit,up)

struct Edit *edit;
short up;

{
    struct NoteEvent *note = edit->selectnote;
    struct ChordEvent *chord;
    struct DynamicsEvent *dynamics;
    short value;
    short bump;
    if (edit->shiftkey) bump = -10;
    else bump = -1;
    if (up) bump = -bump;
    switch (edit->selecttype) {
        case TYPE_CHORD :
        case TYPE_KEY :
            chord = (struct ChordEvent *) note;
            if (up) chord->root = (chord->root + 1) % 12;
            else chord->root = (chord->root + 11) % 12;
            break;
        case TYPE_DYNAMICS :
            dynamics = (struct DynamicsEvent *) note;
            value = dynamics->value;
            value += bump;
            if (value < 1) value = 1;
            if (value > 127) value = 127;
            dynamics->value = value;
            break;
        case TYPE_NOTATION :
        case TYPE_STAFF :
        case TYPE_PIANO :
            value = note->value;
            value += bump;
            if (value < 1) value = 1;
            if (value > 127) value = 127;
            note->value = value;
            break;
        case TYPE_VELOCITY :
        case TYPE_PTOUCH :
        case TYPE_CONTROL :
            value = note->velocity;
            value += bump;
            if (value < 1) value = 1;
            if (value > 127) value = 127;
            note->velocity = value;
            break;
        case TYPE_PBEND :
            value = note->velocity << 7;
            value += bump + note->value;
            if (value < 0) value = 0;
            if (value > 16383) value = 16383;
            note->velocity = value >> 7;
            note->value = value & 0x7F;
            break;
        case TYPE_PATCH :
            value = note->value;
            value += bump;
            note->value = value & 0x7F;
            break;
        case TYPE_MTOUCH :
            value = note->value;
            value += bump;
            if (value < 1) value = 1;
            if (value > 127) value = 127;
            note->value = value;
            break;
    }
}

static void dragchange(edit,up)

struct Edit *edit;
short up;

{
    struct NoteEvent *note = edit->selectnote;
    struct ChordEvent *chord;
    struct DynamicsEvent *dynamics;
    short value;
    short bump;
    if (edit->shiftkey) bump = -12;
    else bump = -1;
    if (up) bump = -bump;
    switch (edit->selecttype) {
        case TYPE_CHORD :
        case TYPE_KEY :
            chord = (struct ChordEvent *) note;
            if (up) chord->root = (chord->root + 1) % 12;
            else chord->root = (chord->root + 11) % 12;
            break;
        case TYPE_DYNAMICS :
            dynamics = (struct DynamicsEvent *) note;
            value = dynamics->value;
            value += bump;
            if (value < 1) value = 1;
            if (value > 127) value = 127;
            dynamics->value = value;
            break;
        case TYPE_NOTATION :
        case TYPE_STAFF :
        case TYPE_PIANO :
        case TYPE_VELOCITY :
        case TYPE_MTOUCH :
        case TYPE_PTOUCH :
        case TYPE_CONTROL :
        case TYPE_PBEND :
            value = note->value;
            value += bump;
            if (value < 1) value = 1;
            if (value > 127) value = 127;
            note->value = value;
            break;
        case TYPE_PATCH :
            value = note->value;
            value += bump;
            note->value = value & 0x7F;
            break;
    }
}

void arrowkeys(edit,code)

struct Edit *edit;
char code;

{
    long time;
    short add,test;
    struct NoteEvent *note = edit->selectnote;
    if (edit->modebits & MB_NOTERES)
    {
      add = edit->insertlength;
    }
    else if (edit->modebits & MB_NOTATERES)
    {
      add = (192 >> (edit->notateres & 3));
    }
    else if (edit->modebits & MB_ALIGNRES)
    {
      add = 0;
      for (test = 1;!add && (test < 3000);test++)
        add = (short)converttime(&edit->clip,(unsigned long)test);
    }
    else
    {
      add = 1;
    }
    if (note && (edit->editmode & (E_DRAG | E_DRAW | E_MAGNIFY | E_FILL))) {
        saveundo(edit);
        if ((edit->editmode & E_MAGNIFY) && !(edit->editmode & E_DRAG))
            displayeventtype(edit,D_LAST);
        else displayeventtype(edit,D_DELETE | D_LAST);
        if (code == 76) {       /* Up arrow */
            code = 0;
            if (edit->editmode & (E_DRAW | E_FILL)) {
                drawchange(edit,1);
            }
            else if (edit->editmode & E_DRAG) {
                dragchange(edit,1);
            }
            else if (shiftmagnify(edit,1,0)) {
                note = edit->selectnote;
                code = 1;
            }
        }
        else if (code == 77) {  /* Down arrow */
            code = 0;
            if (edit->editmode & (E_DRAW | E_FILL)) {
                drawchange(edit,0);
            }
            else if (edit->editmode & E_DRAG) {
                dragchange(edit,0);
            }
            else if (shiftmagnify(edit,-1,0)) {
                note = edit->selectnote;
                code = 0;
            }
        }
        else if (code == 79) {  /* Left arrow */
            code = 1;
            if (shiftmagnify(edit,0,1)) note = edit->selectnote;
            else if ((edit->editmode & E_DRAW) && (note->status == MIDI_NOTEON)) {
              code = 0;
              if (note->duration > add) note->duration -= add;
              displayeditlength(edit,note->time,note->duration);
            }
            else {
                time = note->time - add;
                if (time < 0) time = 0;
                note->time = time;
            }
        }
        else if (code == 78) {  /* Right arrow */
            code = 1;
            if (shiftmagnify(edit,1,1)) note = edit->selectnote;
            else if ((edit->editmode & E_DRAW) && (note->status == MIDI_NOTEON)) {
                code = 0;
                note->duration += add;
                displayeditlength(edit,note->time,note->duration);
            }
            else note->time += add;
        }
        if (code) {
            edit->clip.events.first = (struct Event *)
                sorteventlist(edit->clip.events.first);
            edit->clip.lyrics.first = (struct Event *)
                sorteventlist(edit->clip.lyrics.first);
            edit->clip.rhythm.first = (struct Event *)
                sorteventlist(edit->clip.rhythm.first);
            cleanupdynamics(&edit->clip);
            cleanupkey(&edit->clip);
            cleanupchords(&edit->clip);
            cleanuptimesig(&edit->clip);
        }
        edit->selectnote = note;
            initnote(edit,(struct NotateEvent *)note,0);
            displayeventtype(edit,D_HIGHLIGHT | D_COMPLEMENT);
            tagnote(edit,(struct NotateEvent *)note);
        sendnoteout(edit,note);
        if (edit->editmode & E_MAGNIFY) refresheditnote(edit);
    }
}

void changestring(struct Edit *edit, char up)
{
    struct NotateEvent *note = (struct NotateEvent *)edit->selectnote;
        short string;
        displayevent(edit, (struct NoteEvent *)note, D_DELETE | D_TAB);
        string = note->mark & NM_STRING;
        if (up) string++;
        else string--;
        if (string >= edit->tabstrings) string = edit->tabstrings - 1;
        if (string < 0) string = 0;
        note->mark &= ~NM_STRING;
        note->mark |= string;
        displayevent(edit, (struct NoteEvent *)note, D_HIGHLIGHT | D_TAB);
}
