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

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

import com.leff.midi.util.MidiUtil;

public class MidiFile
{
    public static final int HEADER_SIZE = 14;
    public static final byte[] IDENTIFIER = { 'M', 'T', 'h', 'd' };

    public static final int DEFAULT_RESOLUTION = 480;

    private int mType;
    private int mTrackCount;
    private int mResolution;

    private ArrayList<MidiTrack> mTracks;

    public MidiFile()
    {
        this(DEFAULT_RESOLUTION);
    }

    public MidiFile(int resolution)
    {
        this(resolution, new ArrayList<MidiTrack>());
    }

    public MidiFile(int resolution, ArrayList<MidiTrack> tracks)
    {
        mResolution = resolution >= 0 ? resolution : DEFAULT_RESOLUTION;

        mTracks = tracks != null ? tracks : new ArrayList<MidiTrack>();
        mTrackCount = tracks.size();
        mType = mTrackCount > 1 ? 1 : 0;
    }

    public MidiFile(File fileIn) throws FileNotFoundException, IOException
    {
        this(new FileInputStream(fileIn));
    }

    public MidiFile(InputStream rawIn) throws IOException
    {
        BufferedInputStream in = new BufferedInputStream(rawIn);

        byte[] buffer = new byte[HEADER_SIZE];
        in.read(buffer);

        initFromBuffer(buffer);

        mTracks = new ArrayList<MidiTrack>();
        for(int i = 0; i < mTrackCount; i++)
        {
            mTracks.add(new MidiTrack(in));
        }
    }

    public void setType(int type)
    {
        if(type < 0)
        {
            type = 0;
        }
        else if(type > 2)
        {
            type = 1;
        }
        else if(type == 0 && mTrackCount > 1)
        {
            type = 1;
        }
        mType = type;
    }

    public int getType()
    {
        return mType;
    }

    public int getTrackCount()
    {
        return mTrackCount;
    }

    public void setResolution(int res)
    {
        if(res >= 0)
        {
            mResolution = res;
        }
    }

    public int getResolution()
    {
        return mResolution;
    }

    public long getLengthInTicks()
    {
        long length = 0;
        for(MidiTrack T : mTracks)
        {
            long l = T.getLengthInTicks();
            if(l > length)
            {
                length = l;
            }
        }
        return length;
    }
    
    public void setTrackCount(int i){
    	mTrackCount = i;
    }
    
    public ArrayList<MidiTrack> getTracks()
    {
        return mTracks;
    }

    public void addTrack(MidiTrack T)
    {
        addTrack(T, mTracks.size());
    }

    public void addTrack(MidiTrack T, int pos)
    {

        if(pos > mTracks.size())
        {
            pos = mTracks.size();
        }
        else if(pos < 0)
        {
            pos = 0;
        }

        mTracks.add(pos, T);
        mTrackCount = mTracks.size();
        mType = mTrackCount > 1 ? 1 : 0;
    }

    public void removeTrack(int pos)
    {
        if(pos < 0 || pos >= mTracks.size())
        {
            return;
        }
        mTracks.remove(pos);
        mTrackCount = mTracks.size();
        mType = mTrackCount > 1 ? 1 : 0;
    }

    public void writeToFile(File outFile) throws FileNotFoundException, IOException
    {
        FileOutputStream fout = new FileOutputStream(outFile);

        fout.write(IDENTIFIER);
        fout.write(MidiUtil.intToBytes(6, 4));
        fout.write(MidiUtil.intToBytes(mType, 2));
        fout.write(MidiUtil.intToBytes(mTrackCount, 2));
        fout.write(MidiUtil.intToBytes(mResolution, 2));

        for(MidiTrack T : mTracks)
        {
            T.writeToFile(fout);
        }

        fout.flush();
        fout.close();
    }

    private void initFromBuffer(byte[] buffer)
    {
        if(!MidiUtil.bytesEqual(buffer, IDENTIFIER, 0, 4))
        {
            System.out.println("File identifier not MThd. Exiting");
            mType = 0;
            mTrackCount = 0;
            mResolution = DEFAULT_RESOLUTION;
            return;
        }

        mType = MidiUtil.bytesToInt(buffer, 8, 2);
        mTrackCount = MidiUtil.bytesToInt(buffer, 10, 2);
        mResolution = MidiUtil.bytesToInt(buffer, 12, 2);
    }
}
