/*
 * 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;

/**
 * Contains all information necessary for a musical note, including
 * pitch, duration, attack velocity, and decay velocity.
 *
 * <p>
 * Most of these settings have defaults.  The default octave is 5.
 * The default duration is a quarter note.  The default attack and
 * decay velocities are 64.
 * </p>
 *
 *@author David Koelle
 *@version 2.0.1
 */
public final class Note implements JFugueElement
{
    private byte value = 0;
    private long duration = 0;
    private double decimalDuration = 0.0;
    private boolean isStartOfTie = false;
    private boolean isEndOfTie = false;
    private byte attackVelocity = DEFAULT_VELOCITY;
    private byte decayVelocity = DEFAULT_VELOCITY;
    private boolean rest = false;
    private byte type = 0;
    private boolean accompanyingNotes = false;

    /**
     * Instantiates a new Note object.
     */
    public Note()
    {
        this.value = 0;
        this.duration = 0;
        this.type = 0;
    }

    /**
     * Instantiates a new Note object with the given note value.
     * This constructor should only be called in cases where the
     * duration of the note is not important (for example,
     * when specifying a root note for a IntervalNotation)
     * @param value the numeric value of the note.  C5 is 60.
     */
    public Note(byte value)
    {
        this(value, (long)1);
    }

    /**
     * Instantiates a new Note object with the given note value and duration.
     * @param value the numeric value of the note.  C5 is 60.
     * @param duration the duration of the note, as milliseconds.
     */
    public Note(byte value, long duration)
    {
        this.value = value;
        this.duration = duration;
    }

    /**
     * Instantiates a new Note object with the given note value and duration.
     * @param value the numeric value of the note.  C5 is 60.
     * @param duration the duration of the note, as a decimal fraction of a whole note.
     */
    public Note(byte value, double decimalDuration)
    {
        this.value = value;
        this.decimalDuration = decimalDuration;
    }

    /**
     * Instantiates a new Note object with the given note value, duration, and attack and decay velocities.
     * @param value the numeric value of the note.  C5 is 60.
     * @param duration the duration of the note.
     */
    public Note(byte value, long duration, byte attackVelocity, byte decayVelocity)
    {
        this.value = value;
        this.duration = duration;
        this.attackVelocity = attackVelocity;
        this.decayVelocity = decayVelocity;
    }

    /**
     * Instantiates a new Note object with the given note value, duration, and attack and decay velocities.
     * @param value the numeric value of the note.  C5 is 60.
     * @param duration the duration of the note.
     */
    public Note(byte value, double decimalDuration, byte attackVelocity, byte decayVelocity)
    {
        this.value = value;
        this.decimalDuration = decimalDuration;
        this.attackVelocity = attackVelocity;
        this.decayVelocity = decayVelocity;
    }

    /**
     * Indicates whether this Note object actually represents a rest.
     * @param rest indicates whether this note is rest
     */
    public void setRest(boolean rest)
    {
        this.rest = rest;
    }

    /**
     * Returns whether this Note object actually represents a rest.
     * @return whether this note is a rest
     */
    public boolean isRest()
    {
        return this.rest;
    }

    /**
     * Sets the numeric value of this note.  C5 is 60.
     * @param value the value of the note
     */
    public void setValue(byte value)
    {
        this.value = value;
    }

    /**
     * Returns the numeric value of this note.  C5 is 60.
     * @return the value of this note
     */
    public byte getValue()
    {
        return this.value;
    }

    /**
     * Sets the duration of this note.
     * @param duration the duration of this note
     */
    public void setDuration(long duration)
    {
        this.duration = duration;
    }

    /**
     * Returns the duration of this note.
     * @return the duration of this note
     */
    public long getDuration()
    {
        return this.duration;
    }

    /**
     * Sets the decimal fraction value for the duration.
     * @param number the decimal fraction for the duration
     */
    public void setDecimalDuration(double duration)
    {
        this.decimalDuration = duration;
    }

    /**
     * Returns the decimal fraction value for the duration.
     * @return the decimal fraction value for the duration
     */
    public double getDecimalDuration()
    {
        return this.decimalDuration;
    }

    /**
     * Indicates whether this note has a tie to some future note.
     * @param tied true if the note is tied, false if not
     */
    public void setStartOfTie(boolean startOfTie)
    {
        this.isStartOfTie = startOfTie;
    }

    /**
     * Returns whether this note has a tie to some future note.
     * @return true is the note is tied, false if not
     */
    public boolean isStartOfTie()
    {
        return this.isStartOfTie;
    }


    /**
     * Indicates whether this note is tied to some past note.
     * @param tied true if the note is tied, false if not
     */
    public void setEndOfTie(boolean endOfTie)
    {
        this.isEndOfTie = endOfTie;
    }

    /**
     * Returns whether this note is tied to some past note.
     * @return true is the note is tied, false if not
     */
    public boolean isEndOfTie()
    {
        return this.isEndOfTie;
    }

    /**
     * Sets the attack velocity for this note.
     * @param velocity the attack velocity
     */
    public void setAttackVelocity(byte velocity)
    {
        this.attackVelocity = velocity;
    }

    /**
     * Returns the attack velocity for this note.
     * @return the attack velocity
     */
    public byte getAttackVelocity()
    {
        return this.attackVelocity;
    }

    /**
     * Sets the decay velocity for this note.
     * @param velocity the decay velocity
     */
    public void setDecayVelocity(byte velocity)
    {
        this.decayVelocity = velocity;
    }

    /**
     * Returns the decay velocity for this note.
     * @return the decay velocity
     */
    public byte getDecayVelocity()
    {
        return this.decayVelocity;
    }

    /**
     * Sets whether this Note will have other Notes (sequential or parallel) associated with it.
     * @param accompanying
     */
    public void setHasAccompanyingNotes(boolean accompanying)
    {
        this.accompanyingNotes = accompanying;
    }

    /**
     * Returns whether this Note will have other Notes (sequential or parallel) associated with it.
     */
    public boolean hasAccompanyingNotes()
    {
        return this.accompanyingNotes;
    }

    /**
     * Sets the note type - either First, Sequential, or Parallel.
     * @param type the note type
     */
    public void setType(byte type)
    {
        this.type = type;
    }

    /**
     * Returns the note type - either First, Sequential, or Parallel.
     * @return the note type
     */
    public byte getType()
    {
        return this.type;
    }

    /** Indicates that this note is the first note in the token. */
    public static final byte FIRST      = 0;
    /** Indicates that this note immediately follows a previous note in the same token. */
    public static final byte SEQUENTIAL = 1;
    /** Indicates that this note is played at the same time as a previous note in the same token. */
    public static final byte PARALLEL   = 2;
    /** Default value for attack and decay velocity. */
    public static final byte DEFAULT_VELOCITY = 64;

    /**
     * Returns the Music String representing this element and all of its settings.
     * For a Note object, the Music String is a note, expressed as either a letter, <code>note</code>, or a bracketed number, <code>[<i>note-value</i>],
     * and a duration, expressed as either a letter, <code>duration</code>, or a slash followed by a numeric duration, <code>/<i>decimal-duration</i></code><br />
     * If either the attack or decay velocity is set to a value besides the default, <code>a<i>velocity</i></code> and/or <code>d<i>velocity</i></code> will be added to the string.
     * If this note is to be played in sequence or in parallel to another note, a <code>+</code> or <code>_</code> character will be added as appropriate.
     * @return the Music String for this element
     */
    public String getMusicString()
    {
        StringBuffer buffy = new StringBuffer();

        // If this is a Sequential note or a Parallel note, include that information.
        if (SEQUENTIAL == this.type) {
            buffy.append("_");
        }
        else if (PARALLEL == this.type) {
            buffy.append("+");
        }

        // Add the note value and duration value
//      buffy.append("[");
//      buffy.append(this.value);
//      buffy.append("]/");
//      buffy.append(this.decimalDuration);
        buffy.append(Note.getStringForNote(this.value, this.decimalDuration));

        if (this.attackVelocity != DEFAULT_VELOCITY) {
            buffy.append("a");
            buffy.append(this.attackVelocity);
        }
        if (this.decayVelocity != DEFAULT_VELOCITY) {
            buffy.append("d");
            buffy.append(this.decayVelocity);
        }

        return buffy.toString();
    }

    /**
     * Returns verification string in this format:
     * Note: value={#}, duration={#}, startTie={T|F}, endTie={T|F}, attack={#}, decay={#}, isFirst={T|F}, isParallel={T|F}, isSequential={T|F}
     * @version 4.0
     */
    public String getVerifyString()
    {
        StringBuffer buffy = new StringBuffer();
        buffy.append("Note: value=");
        buffy.append(getValue());
        buffy.append(", duration=");
        buffy.append(getDecimalDuration());
        buffy.append(", startTie=");
        buffy.append(isStartOfTie() ? "T" : "F");
        buffy.append(", endTie=");
        buffy.append(isEndOfTie() ? "T" : "F");
        buffy.append(", attack=");
        buffy.append(getAttackVelocity());
        buffy.append(", decay=");
        buffy.append(getDecayVelocity());
        buffy.append(", isFirst=");
        buffy.append(getType() == FIRST ? "T" : "F");
        buffy.append(", isParallel=");
        buffy.append(getType() == PARALLEL ? "T" : "F");
        buffy.append(", isSequential=");
        buffy.append(getType() == SEQUENTIAL ? "T" : "F");

        return buffy.toString();
    }

    public static String createVerifyString(int value, double duration)
    {
        return createVerifyString(value, duration, false, false, (byte)64, (byte)64, true, false, false);
    }

    public static String createVerifyString(int value, double duration, boolean startTie, boolean endTie)
    {
        return createVerifyString(value, duration, startTie, endTie, (byte)64, (byte)64, true, false, false);
    }

    public static String createVerifyString(int value, double duration, int attack, int decay)
    {
        return createVerifyString(value, duration, false, false, attack, decay, true, false, false);
    }

    public static String createVerifyString(int value, double duration, boolean isFirst, boolean isParallel, boolean isSequential)
    {
        return createVerifyString(value, duration, false, false, (byte)64, (byte)64, isFirst, isParallel, isSequential);
    }

    public static String createVerifyString(int value, double duration, boolean startTie, boolean endTie, int attack, int decay, boolean isFirst, boolean isParallel, boolean isSequential)
    {
        StringBuffer buffy = new StringBuffer();
        buffy.append("Note: value=");
        buffy.append(value);
        buffy.append(", duration=");
        buffy.append(duration);
        buffy.append(", startTie=");
        buffy.append(startTie ? "T" : "F");
        buffy.append(", endTie=");
        buffy.append(endTie ? "T" : "F");
        buffy.append(", attack=");
        buffy.append(attack);
        buffy.append(", decay=");
        buffy.append(decay);
        buffy.append(", isFirst=");
        buffy.append(isFirst ? "T" : "F");
        buffy.append(", isParallel=");
        buffy.append(isParallel ? "T" : "F");
        buffy.append(", isSequential=");
        buffy.append(isSequential ? "T" : "F");

        return buffy.toString();
    }

    /**
     * Easily create compound note verification strings, like:
     * Note: value=60, duration=1.0, startTie=F, endTie=F, attack=64, decay=64, isFirst=T, isParallel=F, isSequential=F; Note: value=63, duration=0.0, startTie=F, endTie=F, attack=64, decay=64, isFirst=F, isParallel=T, isSequential=F
     */
    public static String createCompoundVerifyString(String... strings)
    {
        StringBuffer buffy = new StringBuffer();

        for (String string : strings) {
            buffy.append(string);
            buffy.append("; ");
        }

        return buffy.toString().substring(0, buffy.toString().length()-2);
    }

    /**
     * Returns a MusicString representation of the given MIDI note value and duration --
     * which indicates a note and an octave.
     * @param noteValue this MIDI note value, like 60
     * @param decimalDuration the duration of this note, like 0.5
     * @return a MusicString value, like C5h
     */
    public static String getStringForNote(byte noteValue, double decimalDuration)
    {
        StringBuffer buffy = new StringBuffer();
        buffy.append(getStringForNote(noteValue));
        buffy.append(getStringForDuration(decimalDuration));
        return buffy.toString();
    }

    /**
     * Returns the frequency, in Hertz, for the given note value.
     * For example, the frequency for A5 (MIDI note 69) is 440.0
     * @param noteValue
     * @return
     */
    public static double getFrequencyForNote(int noteValue)
    {
        double freq = 0.0;
        double freq0 = 8.1757989156;
        for (double d=0; d <= noteValue; d++)
        {
            freq = freq0 * Math.pow(2.0, d / 12.0);
        }

        // Truncate to 4 significant digits
        double retVal = Math.rint(freq * 10000.0) / 10000.0;

        return retVal;
    }

    /**
     * Returns a MusicString representation of the given MIDI note value --
     * which indicates a note and an octave.
     * @param noteValue this MIDI note value, like 60
     * @return a MusicString value, like C5
     */
    public static String getStringForNote(byte noteValue)
    {
        int note = noteValue % 12;
        int octave = noteValue / 12;
        StringBuffer buffy = new StringBuffer();
        buffy.append(NOTES[note]);
        buffy.append(octave);
        return buffy.toString();
    }

    /**
     * Returns a MusicString representation of a decimal duration.  This code
     * currently only converts single duration values representing whole, half,
     * quarter, eighth, etc. durations; and dotted durations associated with those
     * durations (such as "h.", equal to 0.75).  This method does not convert
     * combined durations (for example, "hi" for 0.625) or anything greater
     * than a duration of 1.0 (for example, "wwww" for 4.0).  For these values,
     * the original decimal duration is returned in a string, prepended with a "/"
     * to make the returned value a valid MusicString duration indicator.
     *
     * @param decimalDuration The decimal value of the duration to convert
     * @return a MusicString fragment representing the duration
     */
    public static String getStringForDuration(double decimalDuration)
    {
        if (decimalDuration == 1.0) return "w";
        else if (decimalDuration == 0.75) return "h.";
        else if (decimalDuration == 0.5) return "h";
        else if (decimalDuration == 0.375) return "q.";
        else if (decimalDuration == 0.25) return "q";
        else if (decimalDuration == 0.1875) return "i.";
        else if (decimalDuration == 0.125) return "i";
        else if (decimalDuration == 0.09375) return "s.";
        else if (decimalDuration == 0.0625) return "s";
        else if (decimalDuration == 0.046875) return "t.";
        else if (decimalDuration == 0.03125) return "t";
        else if (decimalDuration == 0.0234375) return "x.";
        else if (decimalDuration == 0.015625) return "x";
        else if (decimalDuration == 0.01171875) return "o.";
        else if (decimalDuration == 0.0078125) return "o";
        else return "/" + decimalDuration;
    }

    /**
     * Returns the decimal duration that is equal to the given MusicString representation.
     * This code currently only converts single duration values representing whole, half,
     * quarter, eighth, etc. durations; and dotted durations associated with those
     * durations (such as "h.", equal to 0.75).  This method does not convert
     * combined durations (for example, "hi" for 0.625) or anything greater
     * than a duration of 1.0 (for example, "wwww" for 4.0).  For these values,
     * the original decimal duration is returned in a string, prepended with a "/"
     * to make the returned value a valid MusicString duration indicator.
     *
     * @param stringDuration The MusicString duration character (or dotted character)
     * @return a decimal value representing the duration, expressed as a fraction of a whole note
     */
    public static double getDecimalForDuration(String stringDuration)
    {
        String stringDuration2 = stringDuration.toLowerCase();
        if (stringDuration2.equals("w")) return 1.0;
        else if (stringDuration2.equals("h.")) return 0.75;
        else if (stringDuration2.equals("h")) return 0.5;
        else if (stringDuration2.equals("q.")) return 0.375;
        else if (stringDuration2.equals("q")) return 0.25;
        else if (stringDuration2.equals("i.")) return 0.1875;
        else if (stringDuration2.equals("i")) return 0.125;
        else if (stringDuration2.equals("s.")) return 0.09375;
        else if (stringDuration2.equals("s")) return 0.0625;
        else if (stringDuration2.equals("t.")) return 0.046875;
        else if (stringDuration2.equals("t")) return 0.03125;
        else if (stringDuration2.equals("x.")) return 0.0234375;
        else if (stringDuration2.equals("x")) return 0.015625;
        else if (stringDuration2.equals("o.")) return 0.01171875;
        else if (stringDuration2.equals("o")) return 0.0078125;
        else return 0.0;
    }

    public static final String[] NOTES = new String[] { "C", "C#", "D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B" };

    public static final byte ACOUSTIC_BASS_DRUM = 35;
    public static final byte BASS_DRUM = 36;
    public static final byte SIDE_STICK = 37;
    public static final byte ACOUSTIC_SNARE = 38;
    public static final byte HAND_CLAP = 39;

    public static final byte ELECTRIC_SNARE = 40;
    public static final byte LOW_FLOOR_TOM = 41;
    public static final byte CLOSED_HI_HAT = 42;
    public static final byte HIGH_FLOOR_TOM = 43;
    public static final byte PEDAL_HI_HAT = 44;
    public static final byte LOW_TOM = 45;
    public static final byte OPEN_HI_HAT = 46;
    public static final byte LOW_MID_TOM = 47;
    public static final byte HI_MID_TOM = 48;
    public static final byte CRASH_CYMBAL_1 = 49;

    public static final byte HIGH_TOM = 50;
    public static final byte RIDE_CYMBAL_1 = 51;
    public static final byte CHINESE_CYMBAL = 52;
    public static final byte RIDE_BELL = 53;
    public static final byte TAMBOURINE = 54;
    public static final byte SPLASH_CYMBAL = 55;
    public static final byte COWBELL = 56;
    public static final byte CRASH_CYMBAL_2 = 57;
    public static final byte VIBRASLAP = 58;
    public static final byte RIDE_CYMBAL_2 = 59;

    public static final byte HI_BONGO = 60;
    public static final byte LOW_BONGO = 61;
    public static final byte MUTE_HI_CONGA = 62;
    public static final byte OPEN_HI_CONGA = 63;
    public static final byte LOW_CONGA = 64;
    public static final byte HIGH_TIMBALE = 65;
    public static final byte LOW_TIMBALE = 66;
    public static final byte HIGH_AGOGO = 67;
    public static final byte LOW_AGOGO = 68;
    public static final byte CABASA = 69;

    public static final byte MARACAS = 70;
    public static final byte SHORT_WHISTLE = 71;
    public static final byte LONG_WHISTLE = 72;
    public static final byte SHORT_GUIRO = 73;
    public static final byte LONG_GUIRO = 74;
    public static final byte CLAVES = 75;
    public static final byte HI_WOOD_BLOCK = 76;
    public static final byte LOW_WOOD_BLOCK = 77;
    public static final byte MUTE_CUICA = 78;
    public static final byte OPEN_CUICA = 79;

    public static final byte MUTE_TRIANGLE = 80;
    public static final byte OPEN_TRIANGLE = 81;

}

/*

Here's a handy chart from http://www.borg.com/~jglatt/tutr/notefreq.htm

   MIDI                 MIDI                   MIDI
   Note   Frequency     Note   Frequency       Note   Frequency
C  0    8.1757989156    12    16.3515978313    24    32.7031956626
Db 1    8.6619572180    13    17.3239144361    25    34.6478288721
D  2    9.1770239974    14    18.3540479948    26    36.7080959897
Eb 3    9.7227182413    15    19.4454364826    27    38.8908729653
E  4   10.3008611535    16    20.6017223071    28    41.2034446141
F  5   10.9133822323    17    21.8267644646    29    43.6535289291
Gb 6   11.5623257097    18    23.1246514195    30    46.2493028390
G  7   12.2498573744    19    24.4997147489    31    48.9994294977
Ab 8   12.9782717994    20    25.9565435987    32    51.9130871975
A  9   13.7500000000    21    27.5000000000    33    55.0000000000
Bb 10  14.5676175474    22    29.1352350949    34    58.2704701898
B  11  15.4338531643    23    30.8677063285    35    61.7354126570

C  36  65.4063913251    48   130.8127826503    60   261.6255653006
Db 37  69.2956577442    49   138.5913154884    61   277.1826309769
D  38  73.4161919794    50   146.8323839587    62   293.6647679174
Eb 39  77.7817459305    51   155.5634918610    63   311.1269837221
E  40  82.4068892282    52   164.8137784564    64   329.6275569129
F  41  87.3070578583    53   174.6141157165    65   349.2282314330
Gb 42  92.4986056779    54   184.9972113558    66   369.9944227116
G  43  97.9988589954    55   195.9977179909    67   391.9954359817
Ab 44  103.8261743950   56   207.6523487900    68   415.3046975799
A  45  110.0000000000   57   220.0000000000    69   440.0000000000
Bb 46  116.5409403795   58   233.0818807590    70   466.1637615181
B  47  123.4708253140   59   246.9416506281    71   493.8833012561

C  72  523.2511306012   84  1046.5022612024    96  2093.0045224048
Db 73  554.3652619537   85  1108.7305239075    97  2217.4610478150
D  74  587.3295358348   86  1174.6590716696    98  2349.3181433393
Eb 75  622.2539674442   87  1244.5079348883    99  2489.0158697766
E  76  659.2551138257   88  1318.5102276515   100  2637.0204553030
F  77  698.4564628660   89  1396.9129257320   101  2793.8258514640
Gb 78  739.9888454233   90  1479.9776908465   102  2959.9553816931
G  79  783.9908719635   91  1567.9817439270   103  3135.9634878540
Ab 80  830.6093951599   92  1661.2187903198   104  3322.4375806396
A  81  880.0000000000   93  1760.0000000000   105  3520.0000000000
Bb 82  932.3275230362   94  1864.6550460724   106  3729.3100921447
B  83  987.7666025122   95  1975.5332050245   107  3951.0664100490

C  108 4186.0090448096  120  8372.0180896192
Db 109 4434.9220956300  121  8869.8441912599
D  110 4698.6362866785  122  9397.2725733570
Eb 111 4978.0317395533  123  9956.0634791066
E  112 5274.0409106059  124 10548.0818212118
F  113 5587.6517029281  125 11175.3034058561
Gb 114 5919.9107633862  126 11839.8215267723
G  115 6271.9269757080  127 12543.8539514160
Ab 116 6644.8751612791
A  117 7040.0000000000
Bb 118 7458.6201842894
B  119 7902.1328200980

Note: Middle C is note #60. Frequency is in Hertz.


*/