/*
 * Copyright (c) 2007-2011 Madhav Vaidyanathan
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2.
 *
 *  This program 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 General Public License for more details.
 */



package com.jinmingyunle.midiplaylib.midifile;

import java.util.ArrayList;


/** @class MidiTrack
 * MidiTrack将曲目的原始middievents作为输入，并获取:
 * The MidiTrack takes as input the raw MidiEvents for the track, and gets:
 * * -曲目中midi音符的列表。
 * - The list of midi notes in the track.
 * * -在轨道上使用的第一个乐器。
 * - The first instrument used in the track.
 *对于midi文件中的每个NoteOn事件，都会创建一个新的MidiNote
 * For each NoteOn event in the midi file, a new MidiNote is created
 * 和添加到轨道，使用AddNote()方法。
 * and added to the track, using the AddNote() method.
 * 当遇到NoteOff事件时，会调用NoteOff()方法
 * The NoteOff() method is called when a NoteOff event is encountered,
 * 以更新MidiNote的持续时间
 * in order to update the duration of the MidiNote.
 */ 
public class MidiTrack {
    private int tracknum;                 /** The track number 音轨数量*/
    private ArrayList<MidiNote> notes;    /** List of Midi notes  音符集合*/
    private int instrument;               /** Instrument for this track 乐器  音轨*/
    private String trackName;           /**  声道名称 */
    private ArrayList<MidiEvent> lyrics;  /** The lyrics歌词 in this track */
    private int time = 0;

    /** Create an empty MidiTrack.  Used by the Clone method */
    public MidiTrack(int tracknum) {
        this.tracknum = tracknum;
        notes = new ArrayList<MidiNote>(20);
        instrument = 0;
    } 

    /** Create a MidiTrack based on the Midi events.  Extract the NoteOn/NoteOff
     *  events to gather the list of MidiNotes. 基于Midi事件创建MidiTrack。 提取NoteOn件/ NoteOff
     * *事件收集MidiNotes列表
     */
    public MidiTrack(ArrayList<MidiEvent> events, int tracknum) {
        this.tracknum = tracknum;
        notes = new ArrayList<MidiNote>(events.size());
        instrument = 0;
        time = 0;
        time = events.get(events.size() - 1).StartTime+events.get(events.size() - 1).DeltaTime;
 
        for (MidiEvent mevent : events) {
            if (mevent.EventFlag == MidiFile.EventNoteOn && mevent.Velocity > 0) {
                MidiNote note = new MidiNote(mevent.StartTime, mevent.Channel, mevent.Notenumber, 0);
                AddNote(note);
            }
            else if (mevent.EventFlag == MidiFile.EventNoteOn && mevent.Velocity == 0) {
                NoteOff(mevent.Channel, mevent.Notenumber, mevent.StartTime);
            }
            else if (mevent.EventFlag == MidiFile.EventNoteOff) {
                NoteOff(mevent.Channel, mevent.Notenumber, mevent.StartTime);
            }
            else if (mevent.EventFlag == MidiFile.EventProgramChange) {
                instrument = mevent.Instrument;
            }
            else if (mevent.Metaevent == MidiFile.MetaEventLyric) {
                AddLyric(mevent);
                if (lyrics == null) {
                    lyrics = new ArrayList<MidiEvent>();
                }
                lyrics.add(mevent);
            } else if(mevent.Metaevent == MidiFile.MetaEventSequenceName) {
                trackName = new String(mevent.Value);
            } else if(mevent.Metaevent == MidiFile.MetaEventInstrument) {
//                trackName = new String(mevent.Value);
            }
        }
        if (notes.size() > 0 && notes.get(0).getChannel() == 9)  {
            instrument = 128;  /* Percussion */
        }
    }

    public int getTime() {
        return time;
    }

    public void setTime(int time) {
        this.time = time;
    }

    public String getTrackName() {
        return trackName;
    }

    public void setTrackName(String trackName) {
        this.trackName = trackName;
    }

    public int trackNumber() { return tracknum; }

    public ArrayList<MidiNote> getNotes() { return notes; }

    public int getInstrument() { return instrument; }
    public void setInstrument(int value) { instrument = value; }

    public ArrayList<MidiEvent> getLyrics() { return lyrics; }
    public void setLyrics(ArrayList<MidiEvent> value) { lyrics = value; }


    public String getInstrumentName() { if (instrument >= 0 && instrument <= 128)
                  return MidiFile.Instruments[instrument];
              else
                  return "";
            }

    /** Add a MidiNote to this track.  This is called for each NoteOn event */
    public void AddNote(MidiNote m) {
        notes.add(m);
    }

    /** A NoteOff event occured.  Find the MidiNote of the corresponding
     * NoteOn event, and update the duration of the MidiNote.
     */
    public void NoteOff(int channel, int notenumber, int endtime) {
        for (int i = notes.size()-1; i >= 0; i--) {
            MidiNote note = notes.get(i);
            if (note.getChannel() == channel && note.getNumber() == notenumber &&
                note.getDuration() == 0) {
                note.NoteOff(endtime);
                return;
            }
        }
    }

    /** Add a lyric event to this track */
    public void AddLyric(MidiEvent mevent) { 
        if (lyrics == null) {
            lyrics = new ArrayList<MidiEvent>();
        }
        lyrics.add(mevent);
    }

    /** Return a deep copy clone of this MidiTrack. */
    public MidiTrack Clone() {
        MidiTrack track = new MidiTrack(trackNumber());
        track.instrument = instrument;
        for (MidiNote note : notes) {
            track.notes.add( note.Clone() );
        }
        if (lyrics != null) {
            track.lyrics = new ArrayList<MidiEvent>();
            for (MidiEvent ev : lyrics) {
                track.lyrics.add(ev);
            }
        }
        return track;
    }

    @Override
    public String toString() {
        String result = "Track number=" + tracknum + " instrument=" + instrument + "\n";
        for (MidiNote n : notes) {
           result = result + n + "\n";
        }
        result += "End Track\n";
        return result;
    }
}


