//////////////////////////////////////////////////////////////////////////////
//	Copyright 2011 Alex Leffelman
//	
//	Licensed under the Apache License, Version 2.0 (the "License");
//	you may not use this file except in compliance with the License.
//	You may obtain a copy of the License at
//	
//	http://www.apache.org/licenses/LICENSE-2.0
//	
//	Unless required by applicable law or agreed to in writing, software
//	distributed under the License is distributed on an "AS IS" BASIS,
//	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//	See the License for the specific language governing permissions and
//	limitations under the License.
//////////////////////////////////////////////////////////////////////////////

package com.leff.midi.event.meta;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.leff.midi.event.MidiEvent;
import com.leff.midi.util.VariableLengthInt;

public abstract class MetaEvent extends MidiEvent
{
    protected int mType;
    protected VariableLengthInt mLength;

    protected MetaEvent(long tick, long delta, int type, VariableLengthInt length)
    {
        super(tick, delta);

        mType = type & 0xFF;
        mLength = length;
    }

    protected abstract int getEventSize();

    @Override
    public void writeToFile(OutputStream out, boolean writeType) throws IOException
    {
        writeToFile(out);
    }

    protected void writeToFile(OutputStream out) throws IOException
    {
        super.writeToFile(out, true);
        out.write(0xFF);
        out.write(mType);
    }

    public static MetaEvent parseMetaEvent(long tick, long delta, InputStream in) throws IOException
    {
        MetaEventData eventData = new MetaEventData(in);

        boolean isText = false;
        switch(eventData.type)
        {
            case SEQUENCE_NUMBER:
            case MIDI_CHANNEL_PREFIX:
            case END_OF_TRACK:
            case TEMPO:
            case SMPTE_OFFSET:
            case TIME_SIGNATURE:
            case KEY_SIGNATURE:
                break;
            case TEXT_EVENT:
            case COPYRIGHT_NOTICE:
            case TRACK_NAME:
            case INSTRUMENT_NAME:
            case LYRICS:
            case MARKER:
            case CUE_POINT:
            case SEQUENCER_SPECIFIC: // Not technically text, but follows same
                                     // structure
            default: // Also not technically text, but it should follow
                isText = true;
                break;
        }

        if(isText)
        {
            String text = new String(eventData.data);

            switch(eventData.type)
            {
                case TEXT_EVENT:
                    return new Text(tick, delta, text);
                case COPYRIGHT_NOTICE:
                    return new CopyrightNotice(tick, delta, text);
                case TRACK_NAME:
                    return new TrackName(tick, delta, text);
                case INSTRUMENT_NAME:
                    return new InstrumentName(tick, delta, text);
                case LYRICS:
                    return new Lyrics(tick, delta, text);
                case MARKER:
                    return new Marker(tick, delta, text);
                case CUE_POINT:
                    return new CuePoint(tick, delta, text);
                case SEQUENCER_SPECIFIC:
                    return new SequencerSpecificEvent(tick, delta, eventData.data);
                default:
                    return new GenericMetaEvent(tick, delta, eventData);
            }
        }

        switch(eventData.type)
        {
            case SEQUENCE_NUMBER:
                return SequenceNumber.parseSequenceNumber(tick, delta, eventData);
            case MIDI_CHANNEL_PREFIX:
                return MidiChannelPrefix.parseMidiChannelPrefix(tick, delta, eventData);
            case END_OF_TRACK:
                return new EndOfTrack(tick, delta);
            case TEMPO:
                return Tempo.parseTempo(tick, delta, eventData);
            case SMPTE_OFFSET:
                return SmpteOffset.parseSmpteOffset(tick, delta, eventData);
            case TIME_SIGNATURE:
                return TimeSignature.parseTimeSignature(tick, delta, eventData);
            case KEY_SIGNATURE:
                return KeySignature.parseKeySignature(tick, delta, eventData);
        }
        System.out.println("Completely broken in MetaEvent.parseMetaEvent()");
        return null;
    }

    protected static class MetaEventData
    {
        public final int type;
        public final VariableLengthInt length;
        public final byte[] data;

        public MetaEventData(InputStream in) throws IOException
        {
            type = in.read();
            length = new VariableLengthInt(in);
            data = new byte[length.getValue()];
            if(length.getValue() > 0)
            {
                in.read(data);
            }
        }
    }

    public static final int SEQUENCE_NUMBER = 0;
    public static final int TEXT_EVENT = 1;
    public static final int COPYRIGHT_NOTICE = 2;
    public static final int TRACK_NAME = 3;
    public static final int INSTRUMENT_NAME = 4;
    public static final int LYRICS = 5;
    public static final int MARKER = 6;
    public static final int CUE_POINT = 7;
    public static final int MIDI_CHANNEL_PREFIX = 0x20;
    public static final int END_OF_TRACK = 0x2F;
    public static final int TEMPO = 0x51;
    public static final int SMPTE_OFFSET = 0x54;
    public static final int TIME_SIGNATURE = 0x58;
    public static final int KEY_SIGNATURE = 0x59;
    public static final int SEQUENCER_SPECIFIC = 0x7F;
}
