/*
 * VsqTrack.java
 * Copyright (c) 2008 kbinani
 *
 * This file is part of jp.sourceforge.lipsync.vsq.
 *
 * jp.sourceforge.lipsync.vsq is free software; you can redistribute it and/or
 * modify it under the terms of the BSD License.
 *
 * jp.sourceforge.lipsync.vsq 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.
 */
package jp.sourceforge.lipsync.vsq;

import java.util.*;
import java.io.*;

public class VsqTrack implements Cloneable {
    private String m_name;
    private VsqMetaText m_meta_text;
    private Vector<MidiEvent> m_midi_event;
    private int m_edited_start = Integer.MAX_VALUE;
    private int m_edited_end = Integer.MIN_VALUE;

    public VsqEventIterator singerEventEnumerator() {
        return m_meta_text.getEventList().iterator( VsqEventIteratorMode.Singer );
    }

    public VsqEventIterator noteEventEnumerator() {
        return m_meta_text.getEventList().iterator( VsqEventIteratorMode.Anote );
    }

    public void printMetaText( TextMemoryStream sw, boolean encode, long eos, int start ) {
        m_meta_text.print( sw, encode, eos, start );
    }

    public void printMetaText( String file ) throws IOException {
        TextMemoryStream tms = new TextMemoryStream();
        int count = m_meta_text.getEventList().size();
        int clLast = m_meta_text.getEventList().get( count - 1 ).Clock + 480;
        m_meta_text.print( tms, true, (long)clLast, 0 );
        BufferedWriter sw = new BufferedWriter( new FileWriter( file ) );
        tms.rewind();
        while ( tms.peek() >= 0 ) {
            String line = tms.readLine();
            sw.write( line + "\n" );
        }
    }

    /**
     * for future implement:<pre>
     *property VsqMaster Master {
     *    public get {
     *        return m_meta_text.master;
     *    }
     *    protected set {
     *        m_meta_text.master = value;
     *    }
     *};</pre>
     * @return
     */
    public VsqMaster getMaster() {
        return m_meta_text.master;
    }

    protected void setMaster( VsqMaster value ) {
        m_meta_text.master = value;
    }

    /**
     * for future implement:<pre>
     *property VsqMixer Mixer {
     *    public get {
     *        return m_meta_text.mixer;
     *    }
     *    protected set {
     *        m_meta_text.mixer = value;
     *    }
     *};</pre>
     * @return
     */
    public VsqMixer getMixer() {
        return m_meta_text.mixer;
    }

    protected void setMixer( VsqMixer value ) {
        m_meta_text.mixer = value;
    }

    public VsqBPList getVsqBPList( VsqCurveType curveType ) {
        return m_meta_text.getVsqBPList( curveType );
    }

    public void setVsqBPList( VsqCurveType curveType, VsqBPList value ) {
        m_meta_text.setVsqBPList( curveType, value );
    }

    public VsqEventList getEvents() {
        return m_meta_text.getEventList();
    }

    /**
     * for future implement:<pre>
     * property int EditedStart {
     *     public get {
     *         return m_edited_start;
     *     }
     *     protected set {
     *         if ( value &lt; m_edited_start ) {
     *              m_edited_start = value;
     *          }
     *      }
     * };</pre>
     */
    public int getEditedStart(){
        return m_edited_start;
    }
    protected void setEditedStart( int value ){
        m_edited_start = value;
    }
    public int getEditedEnd() {
        return m_edited_end;
    }

    protected void setEditedEnd( int value ) {
        if ( m_edited_end < value ) {
            m_edited_end = value;
        }
    }

    public void resetEditedArea() {
        m_edited_start = Integer.MAX_VALUE;
        m_edited_end = Integer.MIN_VALUE;
    }

    public Object clone() {
        VsqTrack res = new VsqTrack();
        res.m_name = m_name;
        if ( m_meta_text != null ) {
            res.m_meta_text = (VsqMetaText)m_meta_text.clone();
        }
        if ( m_midi_event != null ) {
            res.m_midi_event = new Vector<MidiEvent>();
            for ( int i = 0; i < m_midi_event.size(); i++ ) {
                MidiEvent item = m_midi_event.get( i );
                res.m_midi_event.add( (MidiEvent)item.clone() );
            }
        }
        res.m_edited_start = m_edited_start;
        res.m_edited_end = m_edited_end;
        return res;
    }

    private VsqTrack() {
    }

    /**
     * Master Trackを構築
     * @param tempo
     * @param numerator
     * @param denominator
     */
    public VsqTrack( int tempo, int numerator, int denominator ) {
        this.m_name = "Master Track";
        this.m_meta_text = null;
        this.m_midi_event = new Vector<MidiEvent>();
        this.m_midi_event.add( new MidiEvent( "0 Tempo " + tempo ) );
        this.m_midi_event.add( new MidiEvent( "0 TimeSig " + numerator + "/" + denominator + " 24 8" ) );
    }

    /**
     * Master Trackでないトラックを構築。
     * @param name
     * @param singer
     */
    public VsqTrack( String name, String singer ) {
        m_name = name;
        m_meta_text = new VsqMetaText( name, singer );
        m_midi_event = new Vector<MidiEvent>();
    }

    /**
     * 
     * メタテキスト。
     * private property VsqMetaText MetaText {
     *     get {
     *         return m_meta_text;
     *     }
     * };
     */
    protected VsqMetaText getVsqMetaText() {
        return m_meta_text;
    }

    /**
     * トラックの名前。
     * public property String Name {
     *     get {
     *         return m_name;
     *     }
     *     set {
     *         m_name = value;
     *     }
     * };
     */
    public String getName() {
        return m_name;
    }

    public void setName( String value ) {
        m_name = value;
    }

    /**
     * 歌詞の文字数を調べます
     * @returns 
     */
    public int getLyricLength() {
        int counter = 0;
        VsqEventList list = m_meta_text.getEventList();
        for ( int i = 0; i < list.size(); i++ ) {
            if ( list.get( i ).ID.type == VsqIDType.Anote ) {
                counter++;
            }
        }
        return counter;
    }

    /**
     * vsqファイルをmf2t形式にテキスト化されたファイルからコンストラクト。
     * @param lines
     */
    public VsqTrack( Vector<String> lines ) throws IOException {
        m_midi_event = new Vector<MidiEvent>();
        m_name = "";
        String meta_text_path;
        File temp_file = File.createTempFile( "temp", ".bin" );
        meta_text_path = temp_file.getPath();

        TextMemoryStream sw = new TextMemoryStream();
        int signal;
        for ( int j = 0; j < lines.size(); j++ ) {
            String s = lines.get( j );
            String line = s;
            // signalを取得
            int index = line.indexOf( ' ' );
            String str_signal = line.substring( 0, index );
            signal = Integer.parseInt( str_signal );
            String remain = line.substring( index + 1 );

            // イベントの種類で処理を分岐
            String[] spl = remain.split( " " );
            if ( spl[0] == "Meta" && spl[1] == "Text" ) {
                line = line.replace( signal + " Meta Text \"", "" );
                int second_colon = line.indexOf( ":", 3 );
                line = line.substring( second_colon + 1 );
                line = line.substring( 0, line.length() - 1 );
                line = line.replace( "\\n", "\n" );
                sw.write( line );
            } else if ( spl[0] == "Meta" && (spl[1] == "TrkName" || spl[1] == "SeqName") ) {
                m_name = spl[2];
                for ( int i = 3; i < spl.length; i++ ) {
                    m_name += " " + spl[i];
                }
                m_name = m_name.replace( "\"", "" );
                m_name = Lyric.decode( m_name );
            } else {
                m_midi_event.add( new MidiEvent( line ) );
            }
        }
        sw.rewind();
        m_meta_text = new VsqMetaText( sw );
        temp_file.delete();
    }

    /**
     * MidiEventの中からテンポ情報を抽出します
     * @returns
     */
    public Vector<MidiEvent> getTempoList() {
        Vector<MidiEvent> list = new Vector<MidiEvent>();
        for ( int i = 0; i < m_midi_event.size(); i++ ) {
            if ( m_midi_event.get( i ).type == MidiEventType.tempo ) {
                list.add( m_midi_event.get( i ) );
            }
        }
        Collections.sort( list );
        return list;
    }

    /**
     * MidiEventの中から拍子情報を抽出します
     * @returns
     */
    public Vector<MidiEvent> getTimeSigList() {
        Vector<MidiEvent> list = new Vector<MidiEvent>();
        for ( int i = 0; i < m_midi_event.size(); i++ ) {
            if ( m_midi_event.get( i ).type == MidiEventType.time_signal ) {
                list.add( m_midi_event.get( i ) );
            }
        }
        Collections.sort( list );
        return list;
    }

}