#ifndef __libvsq_h__
#define __libvsq_h__
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
#include <stdlib.h>
#include "cp932.h"

using namespace std;

namespace vsq{

inline vector<string> util_split( string str, string delim ){
    vector<string> result;
    int cutAt;
    while( (cutAt = str.find_first_of( delim )) != str.npos ){
        if( cutAt > 0 ){
            result.push_back( str.substr( 0, cutAt ) );
        }
        str = str.substr( cutAt + 1 );
    }
    if( str.length() > 0 ){
        result.push_back( str );
    }
    return result;
}

inline string string_replace( string str, string before, string after ){
    int start = 0;
    int index = str.find( before, start );
    string newone = str;
    while( index < 0 ){
        newone = newone.substr( 0, index ) + after + newone.substr( index + before.length() + 1 );
        start = index + before.length() + 1 + (after.length() - before.length());
        index = newone.find( before, start );
    }
    return newone;
}

template<class T> T parse( string s ){
    ostringstream oss( "" );
    T ret;
    oss >> ret;
    return ret;
}

typedef unsigned char byte;

struct TColor{
    int R;
    int G;
    int B;
};

class StreamReader{
public:
    StreamReader( string path ){
        m_ifs.open( path.c_str() );
    };

    void Close(){
        m_ifs.close();
    };

    string ReadLine(){
        string ret;
        if( get_line( &m_ifs, &ret ) ){
            return ret;
        }else{
            return NULL;
        }
    };
    int Peek(){
        return m_ifs.peek();
    };
private:
    ifstream m_ifs;
    static bool get_line( ifstream *ifs, string *str ){
        char ch;
        if( ifs->eof() ){
            return false;
        }
        while( ifs->get( ch ) ){
            if( ch == 0x0d ){
                if( ifs->get( ch ) ){
                    if( ch != 0x0a ){
                        ifs->seekg( -1, ios::cur );
                    }
                }
                break;
            }else if( ch == 0x0a ){
                break;
            }
            str->append( 1, ch );
        }
        return true;
    };
};

/// <summary>
/// メモリー上でテキストファイルを扱うためのクラス．
/// </summary>
class TextMemoryStream {
public:
    /// <summary>
    /// 
    /// </summary>
    /// <param name="s"></param>
    void Write( string value ){
        m_ms << value;
    };
    void Rewind(){
        m_ms.seekg( 0, ios::beg );
    };
    void WriteLine( string s ){
        m_ms << s << "\x0d\x0a";
    };
    void Close(){
        m_ms.str( "" );
    };
    int Peek(){
        long current = m_ms.tellg();
        int ret = m_ms.get();
        if( m_ms.gcount() <= 0 ){
            m_ms.seekg( current, ios::beg );
            return -1;
        }
        m_ms.seekg( current, ios::beg );
        return ret;
    };
    string ReadLine(){
        ostringstream buffer;
        char value;
        char ret;
        ret = m_ms.get();
        while ( ret >= 0 ) {
            value = (char)ret;
            if ( value == NEW_LINE[0] ) {
                char next;
                long current = m_ms.tellg(); //0x0Dを検出した直後のストリームの位置
                for ( int i = 1; i < 2; i++ ) {
                    ret = m_ms.get();
                    if ( ret >= 0 ) {
                        next = ret;
                        if ( next != NEW_LINE[i] ) {
                            m_ms.seekg( current, ios::beg );
                            break;
                        }
                    }
                }
                break;
            }
            buffer << value;
            ret = m_ms.get();
        }
        return buffer.str();
    };
    void Dispose(){
        m_ms.str( "" );
    };
    TextMemoryStream( string path ){
        m_ms.str( "" );
        //if ( File.Exists( path ) ) {
        StreamReader sr( path );
        while( sr.Peek() >= 0 ){
            string line = sr.ReadLine();
            m_ms << line << "\x0d\x0a";
        }
        sr.Close();
        m_ms.seekg( 0, ios::beg );
        NEW_LINE[0] = '\x0d';
        NEW_LINE[1] = '\x0a';
    };
    TextMemoryStream(){
        m_ms.str( "" );
        NEW_LINE[0] = '\x0d';
        NEW_LINE[1] = '\x0a';
    };
private:
    //FileAccess m_access;
    stringstream m_ms;
    //Encoding m_enc;
    char NEW_LINE[2];
};

class VsqCommon {
public:
    string Version;
    string Name;
    string Color;
    int DynamicsMode;
    int PlayMode;

    /// <summary>
    /// 各パラメータを指定したコンストラクタ
    /// </summary>
    /// <param name="name">トラック名</param>
    /// <param name="color">Color値（意味は不明）</param>
    /// <param name="dynamics_mode">DynamicsMode（デフォルトは1）</param>
    /// <param name="play_mode">PlayMode（デフォルトは1）</param>
    VsqCommon( string name, TColor color, int dynamics_mode, int play_mode );

    /// <summary>
    /// MetaTextのテキストファイルからのコンストラクタ
    /// </summary>
    /// <param name="sr">読み込むテキストファイル</param>
    /// <param name="last_line">読み込んだ最後の行が返される</param>
    VsqCommon( TextMemoryStream sr, string& last_line );

    /// <summary>
    /// インスタンスの内容をテキストファイルに出力します
    /// </summary>
    /// <param name="sw">出力先</param>
    void write( TextMemoryStream& sw );

    /// <summary>
    /// VsqCommon構造体を構築するテストを行います
    /// </summary>
    /// <returns>テストに成功すればtrue、そうでなければfalse</returns>
    static bool test();
};

enum VsqHandleType {
    Lyric,
    Vibrato,
    Singer
};

const int _NUM_SYMBOL_VOWEL_JP = 5;
static string _SYMBOL_VOWEL_JP[_NUM_SYMBOL_VOWEL_JP] = {
    "a",
    "i",
    "M",
    "e",
    "o",
};

const int _NUM_SYMBOL_CONSONANT_JP = 36;
static string _SYMBOL_CONSONANT_JP[_NUM_SYMBOL_CONSONANT_JP] = {
    "k",
    "k'",
    "g",
    "g'",
    "N",
    "N'",
    "s",
    "S",
    "z",
    "Z",
    "dz",
    "dZ",
    "t",
    "t'",
    "ts",
    "tS",
    "d",
    "d'",
    "n",
    "J",
    "h",
    "h\\",
    "C",
    "p\\",
    "p\\'",
    "b",
    "b'",
    "p",
    "p'",
    "m",
    "m'",
    "j",
    "4",
    "4'",
    "w",
    "N\\",
};

const int _NUM_SYMBOL_EN = 53;
static string _SYMBOL_EN[_NUM_SYMBOL_EN] = {
    "@",
    "V",
    "e",
    "e",
    "I",
    "i:",
    "{",
    "O:",
    "Q",
    "U",
    "u:",
    "@r",
    "eI",
    "aI",
    "OI",
    "@U",
    "aU",
    "I@",
    "e@",
    "U@",
    "O@",
    "Q@",
    "w",
    "j",
    "b",
    "d",
    "g",
    "bh",
    "dh",
    "gh",
    "dZ",
    "v",
    "D",
    "z",
    "Z",
    "m",
    "n",
    "N",
    "r",
    "l",
    "l0",
    "p",
    "t",
    "k",
    "ph",
    "th",
    "kh",
    "tS",
    "f",
    "T",
    "s",
    "S",
    "h",
};

class VsqPhoneticSymbol {
public:
    static bool IsConsonant( string symbol ) {
        for( int i = 0; i < _NUM_SYMBOL_CONSONANT_JP; i++ ){
            string s = _SYMBOL_CONSONANT_JP[i];
            if ( s == symbol ) {
                return true;
            }
        }
        return false;
    }
    static bool IsValidSymbol( string symbol ) {
        for( int i = 0; i < _NUM_SYMBOL_VOWEL_JP; i++ ){
            string s = _SYMBOL_VOWEL_JP[i];
            if ( s == symbol ) {
                return true;
            }
        }
        for( int i = 0; i < _NUM_SYMBOL_CONSONANT_JP; i++ ){
            string s = _SYMBOL_CONSONANT_JP[i];
            if ( s == symbol ) {
                return true;
            }
        }
        for( int i = 0; i < _NUM_SYMBOL_EN; i++ ){
            string s = _SYMBOL_EN[i];
            if ( s == symbol ) {
                return true;
            }
        }
        return false;
    }
};


/// <summary>
/// VsqHandleに格納される歌詞の情報を扱うクラス。
/// </summary>
class Lyric {
private:
    string m_phrase;
    vector<string> m_phonetic_symbol;
    float d1;
    vector<int> m_consonant_adjustment;
    bool m_protected;

    Lyric(){
    }

    /// <summary>
    /// バイト並びsearchの中に含まれるバイト並びvalueの位置を探します。
    /// </summary>
    /// <param name="search">検索対象のバイト並び</param>
    /// <param name="value">検索するバイト並び</param>
    /// <returns>valueが見つかればそのインデックスを、見つからなければ-1を返します</returns>
    static int mIndexOf( vector<unsigned char> search, vector<unsigned char> value ) {
        int i, j;
        int search_length = search.size();
        int value_length = value.size();
        
        // 検索するバイト並びが、検索対象のバイト並びより長いとき。
        // 見つかるわけない
        if ( value_length > search_length ) {
            return -1;
        }

        // i : 検索の基点
        for ( i = 0; i <= search_length - value_length; i++ ) {
            bool failed = false;
            for ( j = 0; j < value_length; j++ ) {
                if ( search[i + j] != value[j] ) {
                    failed = true;
                    break;
                }
            }
            if ( !failed ) {
                return i;
            }
        }
        return -1;
    }

    
    /// <summary>
    /// 文字がプリント出力可能かどうかを判定します
    /// </summary>
    /// <param name="ch"></param>
    /// <returns></returns>
    static bool isprint( char ch ) {
        if ( 32 <= (int)ch && (int)ch <= 126 ) {
            return true;
        } else {
            return false;
        }
    }

public:
    bool PhoneticSymbolProtected() {
            return m_protected;
    }

    void PhoneticSymbolProtected( bool value ){
        m_protected = value;
    }

    float UnknownFloat() {
        return d1;
    }

    void UnknownFloat( float value ){
        d1 = value;
    }

    vector<int> ConsonantAdjustment() {
        return m_consonant_adjustment;
    }

    /// <summary>
    /// 歌詞、発音記号を指定したコンストラクタ
    /// </summary>
    /// <param name="phrase">歌詞</param>
    /// <param name="phonetic_symbol">発音記号</param>
    Lyric( string phrase, string phonetic_symbol ) {
        m_phrase = phrase;
        PhoneticSymbol( phonetic_symbol );
        d1 = 0.000000f;
    }
    
    /// <summary>
    /// この歌詞のフレーズを取得または設定します。
    /// </summary>
    string Phrase() {
        return m_phrase;
    }
    
    void Phrase( string value ){
        m_phrase = value;
    }

    /// <summary>
    /// この歌詞の発音記号を取得または設定します。
    /// </summary>
    string PhoneticSymbol() {
        string ret = m_phonetic_symbol[0];
        for ( int i = 1; i < m_phonetic_symbol.size(); i++ ) {
            ret += " " + m_phonetic_symbol[i];
        }
        return ret;
    }

    void PhoneticSymbol( string value ){
        string s = string_replace( value, "  ", " " );
        m_phonetic_symbol = util_split( s, " " );
        for ( int i = 0; i < m_phonetic_symbol.size(); i++ ) {
            m_phonetic_symbol[i] = string_replace(  m_phonetic_symbol[i], "\\\\", "\\" );
        }
        m_consonant_adjustment = vector<int>( m_phonetic_symbol.size() );
        for ( int i = 0; i < m_phonetic_symbol.size(); i++ ) {
            if ( VsqPhoneticSymbol::IsConsonant( m_phonetic_symbol[i] ) ) {
                m_consonant_adjustment[i] = 64;
            } else {
                m_consonant_adjustment[i] = 0;
            }
        }
    }

    vector<string> PhoneticSymbolList(){
        vector<string> ret = vector<string>( m_phonetic_symbol.size() );
        for ( int i = 0; i < m_phonetic_symbol.size(); i++ ) {
            ret[i] = m_phonetic_symbol[i];
        }
        return ret;
    }

    /// <summary>
    /// 文字列からのコンストラクタ
    /// </summary>
    /// <param name="_line">生成元の文字列</param>
    Lyric( string _line ) {
        if ( _line.length() <= 0 ) {
            m_phrase = "a";
            PhoneticSymbol( "a" );
            d1 = 1.0f;
            m_protected = false;
        } else {
            vector<string> spl = util_split( _line, "," );
            int c_length = spl.size() - 3;
            if ( spl.size() < 4 ) {
                m_phrase = "a";
                PhoneticSymbol( "a" );
                d1 = 0.0f;
                m_protected = false;
            } else {
                m_phrase = decode( spl[0] );
                PhoneticSymbol( decode( spl[1] ) );
                d1 = parse<float>( spl[2] );
                m_protected = (spl[spl.size() - 1] == "0") ? false : true;
            }
        }
    }

    /// <summary>
    /// mIndexOfのテストメソッド。search, valueをいろいろ変えてテストする事。
    /// </summary>
    /// <returns></returns>
    static bool test_mIndexOf() {
        vector<unsigned char> search;
        vector<unsigned char> value;
        unsigned char src_search[6] = { 0, 12, 3, 5, 16, 34 };
        unsigned char src_value[2] = { 16, 34 };
        for( int i = 0; i < 6; i++ ){
            search.push_back( src_search[i] );
        }
        for( int i = 0; i < 2; i++ ){
            value.push_back( src_value[i] );
        }
        if ( mIndexOf( search, value ) == 4 ) {
            return true;
        } else {
            return false;
        }
    }

    /// <summary>
    /// エスケープされた\"や、\x**を復帰させます
    /// </summary>
    /// <param name="_string">デコード対象の文字列</param>
    /// <returns>デコード後の文字列</returns>
    static string decode( string _string ) {
        string result = _string;
        result = string_replace( result, "\\\"", "" );
        vector<unsigned char> str;
        for( int i = 0; i < result.length(); i++ ){
            str.push_back( (unsigned char)result[i] );
        }

        vector<unsigned char> x16;
        string xx = "\\x";
        for( int i = 0; i < xx.length(); i++ ){
            x16.push_back( (unsigned char)xx[i] );
        }
        int index = mIndexOf( str, x16 );
        while ( index >= 0 ) {
            char chr_byte[2];
            chr_byte[0] = str[index + 2];
            chr_byte[1] = str[index + 3];
            string chr( chr_byte );
            char *endstr;
            int chrcode = strtol( chr.c_str(), &endstr, 16 );
            str[index] = (byte)chrcode;
            for ( int i = index + 4; i < str.size(); i++ ) {
                str[i - 3] = str[i];
            }
            int length = str.size() - 3;
            vector<byte> new_str;
            for ( int i = 0; i < length; i++ ) {
                new_str.push_back( str[i] );
            }
            Array.Resize( ref str, length );
            str = new_str;
            index = mIndexOf( str, x16 );
        }
        return cp932_convert( str );
    }


    /// <summary>
    /// 与えられた文字列の中の2バイト文字を\x**の形式にエンコードします。
    /// </summary>
    /// <param name="item">エンコード対象</param>
    /// <returns>エンコードした文字列</returns>
    static vector<wchar_t> encode( string item ) {
        //Encoding sjis = Encoding.GetEncoding( 932 );
        unsigned char[] bytea = cp932_convert( item );//            sjis.GetBytes( item );
        string result = "";
        for ( int i = 0; i < bytea.Length; i++ ) {
            if ( isprint( (char)bytea[i] ) ) {
                result += (char)bytea[i];
            } else {
                result += "\\x" + Convert.ToString( bytea[i], 16 );
            }
        }
        wchar_t[] res = result.ToCharArray();
        return res;
    }


    /// <summary>
    /// 与えられた文字列をShift_JISとみなし、byte[]に変換しさらにchar[]に変換したもの返します
    /// </summary>
    /// <param name="item">変換元の文字列</param>
    /// <returns>変換後のchar[]</returns>
    static vector<wchar_t> encodeEx( string item ) {
        //Encoding sjis = Encoding.GetEncoding( 932 );
        byte[] dat = cp932_convert( item );//            sjis.GetBytes( item );
        char[] result = new char[dat.Length];
        for ( int i = 0; i < dat.Length; i++ ) {
            result[i] = (char)dat[i];
        }
        return result;
    }


    /// <summary>
    /// このインスタンスを文字列に変換します
    /// </summary>
    /// <param name="a_encode">2バイト文字をエンコードするか否かを指定するフラグ</param>
    /// <returns>変換後の文字列</returns>
    string ToString( bool a_encode ) {
        string result;
        if ( a_encode ) {
            string njp = new string( encode( this.Phrase ) );
            result = "\"" + njp + "\",\"" + this.PhoneticSymbol + "\"," + d1.ToString( "0.000000" );
        } else {
            result = "\"";
            //Encoding sjis = Encoding.GetEncoding( 932 );
            byte[] dat = cp932.convert( this.Phrase );//                sjis.GetBytes( this.Phrase );
            for ( int i = 0; i < dat.Length; i++ ) {
                result += (char)dat[i];
            }
            result += "\",\"" + this.PhoneticSymbol + "\"," + d1.ToString( "0.000000" );
            result = result.Replace( "\\\\", "\\" );
        }
        for ( int i = 0; i < m_consonant_adjustment.Length; i++ ) {
            result += "," + m_consonant_adjustment[i];
        }
        if ( m_protected ) {
            result += ",1";
        } else {
            result += ",0";
        }
        return result;
    }

    /// <summary>
    /// Lyricインスタンスを構築するテストを行います
    /// </summary>
    /// <returns>テストに成功すればtrue、そうでなければfalseを返します</returns>
    static bool test() {
        string line = "\\\"\\x82\\xe7\\\",\\\"4 a\\\",1.000000,64,1,1";
        //Console.WriteLine( "Lyric.test; line=" + line );
        Lyric lyric( line );
        if ( lyric.Phrase == "ら" &&
            lyric.PhoneticSymbol == "4 a" &&
            lyric.d1 == 1.0 &&
            lyric.m_consonant_adjustment[0] == 64 &&
            lyric.m_consonant_adjustment[1] == 1 &&
            lyric.m_consonant_adjustment[2] == 1 ) {
            return true;
        } else {
            return false;
        }
    }

};

/// <summary>
/// ハンドルを取り扱います。ハンドルにはLyricHandle、VibratoHandleおよびIconHandleがある
/// </summary>
class VsqHandle {
private:
    VsqHandleType m_type;
    /// <summary>
    /// インスタンスをコンソール画面に出力します
    /// </summary>
    void Print() {
        string result = this.ToString();
        Console.WriteLine( result );
    }
public:
    int Index;
    string IconID;
    string IDS;
    Lyric L0;
    int Original;
    string Caption;
    int Length;
    int StartDepth;
    VibratoBPList DepthBP;
    int StartRate;
    VibratoBPList RateBP;
protected:
    int m_language;
    int m_program;
    protected VsqHandle() {
    }
public:
    LyricHandle ConvertToLyricHandle() {
        LyricHandle ret = new LyricHandle();
        ret.L0 = (Lyric)L0;
        ret.m_type = m_type;
        ret.Index = Index;
        return ret;
    }

    VibratoHandle ConvertToVibratoHandle() {
        VibratoHandle ret = new VibratoHandle();
        ret.m_type = m_type;
        ret.Index = Index;
        ret.Caption = Caption;
        ret.DepthBP = (VibratoBPList)DepthBP.Clone();
        ret.IconID = IconID;
        ret.IDS = IDS;
        ret.Index = Index;
        ret.Length = Length;
        ret.Original = Original;
        ret.RateBP = (VibratoBPList)RateBP.Clone();
        ret.StartDepth = StartDepth;
        ret.StartRate = StartRate;
        return ret;
    }

    IconHandle ConvertToIconHandle() {
        IconHandle ret = new IconHandle();
        ret.m_type = m_type;
        ret.Index = Index;
        ret.Caption = Caption;
        ret.IconID = IconID;
        ret.IDS = IDS;
        ret.Index = Index;
        ret.Language( m_language );
        ret.Length = Length;
        ret.Original = Original;
        ret.Program( m_program );
        return ret;
    }

    VsqHandleType type() {
        return m_type;
    }

    void type( VsqHandleType value ) {
        m_type = value;
    }

    /// <summary>
    /// インスタンスをストリームに書き込みます。
    /// encode=trueの場合、2バイト文字をエンコードして出力します。
    /// </summary>
    /// <param name="sw">書き込み対象</param>
    /// <param name="encode">2バイト文字をエンコードするか否かを指定するフラグ</param>
    void write( TextMemoryStream sw, bool encode ) {
        sw.WriteLine( this.ToString( encode ) );
    }

    /// <summary>
    /// FileStreamから読み込みながらコンストラクト
    /// </summary>
    /// <param name="sr">読み込み対象</param>
    VsqHandle( TextMemoryStream sr, int value, ref string last_line ) {
        this.Index = value;
        string[] spl;
        string[] spl2;

        // default値で梅
        this.type( VsqHandleType.Vibrato );
        IconID = "";
        IDS = "normal";
        L0 = new Lyric( "" );
        Original = 0;
        Caption = "";
        Length = 0;
        StartDepth = 0;
        DepthBP = null;
        int depth_bp_num = 0;
        StartRate = 0;
        RateBP = null;
        int rate_bp_num = 0;
        m_language = 0;
        m_program = 0;

        string tmpDepthBPX = "";
        string tmpDepthBPY = "";
        string tmpRateBPX = "";
        string tmpRateBPY = "";

        // "["にぶち当たるまで読込む
        last_line = sr.ReadLine();
        while ( !last_line.StartsWith( "[" ) ) {
            spl = last_line.Split( new char[] { '=' } );
            switch ( spl[0] ) {
                case "Language":
                    m_language = int.Parse( spl[1] );
                    break;
                case "Program":
                    m_program = int.Parse( spl[1] );
                    break;
                case "IconID":
                    IconID = spl[1];
                    break;
                case "IDS":
                    IDS = spl[1];
                    break;
                case "Original":
                   Original = int.Parse( spl[1] );
                    break;
                case "Caption":
                    Caption = spl[1];
                    for ( int i = 2; i < spl.Length; i++ ) {
                        Caption += "=" + spl[i];
                    }
                    break;
                case "Length":
                    Length = int.Parse( spl[1] );
                    break;
                case "StartDepth":
                    StartDepth = int.Parse( spl[1] );
                    break;
                case "DepthBPNum":
                    depth_bp_num = int.Parse( spl[1] );
                    break;
                case "DepthBPX":
                    tmpDepthBPX = spl[1];
                    break;
                case "DepthBPY":
                    tmpDepthBPY = spl[1];
                    break;
                case "StartRate":
                    StartRate = int.Parse( spl[1] );
                    break;
                case "RateBPNum":
                    rate_bp_num = int.Parse( spl[1] );
                    break;
                case "RateBPX":
                    tmpRateBPX = spl[1];
                    break;
                case "RateBPY":
                    tmpRateBPY = spl[1];
                    break;
                case "L0":
                    m_type = VsqHandleType.Lyric;
                    L0 = new Lyric( spl[1] );
                    break;
            }
            if ( sr.Peek() < 0 ) {
                break;
            }
            last_line = sr.ReadLine();
        }
        if ( IDS != "normal" ) {
            type( VsqHandleType.Singer );
        } else if ( IconID != "" ) {
            type( VsqHandleType.Vibrato );
        } else {
            type( VsqHandleType.Lyric );
        }

        // RateBPX, RateBPYの設定
        if ( this.type() == VsqHandleType.Vibrato ) {
            if ( rate_bp_num > 0 ) {
                float[] rate_bp_x = new float[rate_bp_num];
                spl2 = tmpRateBPX.Split( new char[] { ',' } );
                for ( int i = 0; i < rate_bp_num; i++ ) {
                    rate_bp_x[i] = float.Parse( spl2[i] );
                }

                int[] rate_bp_y = new int[rate_bp_num];
                spl2 = tmpRateBPY.Split( new char[] { ',' } );
                for ( int i = 0; i < rate_bp_num; i++ ) {
                    rate_bp_y[i] = int.Parse( spl2[i] );
                }
                RateBP = new VibratoBPList( rate_bp_x, rate_bp_y );
            } else {
                //m_rate_bp_x = null;
                //m_rate_bp_y = null;
                RateBP = new VibratoBPList();
            }

            // DepthBPX, DepthBPYの設定
            if ( depth_bp_num > 0 ) {
                float[] depth_bp_x = new float[depth_bp_num];
                spl2 = tmpDepthBPX.Split( new char[] { ',' } );
                for ( int i = 0; i < depth_bp_num; i++ ) {
                    depth_bp_x[i] = float.Parse( spl2[i] );
                }

                int[] depth_bp_y = new int[depth_bp_num];
                spl2 = tmpDepthBPY.Split( new char[] { ',' } );
                for ( int i = 0; i < depth_bp_num; i++ ) {
                    depth_bp_y[i] = int.Parse( spl2[i] );
                }
                DepthBP = new VibratoBPList( depth_bp_x, depth_bp_y );
            } else {
                DepthBP = new VibratoBPList();
                //m_depth_bp_x = null;
                //m_depth_bp_y = null;
            }
        } else {
            DepthBP = new VibratoBPList();
            RateBP = new VibratoBPList();
        }
    }

    /// <summary>
    /// ハンドル指定子（例えば"h#0123"という文字列）からハンドル番号を取得します
    /// </summary>
    /// <param name="_string">ハンドル指定子</param>
    /// <returns>ハンドル番号</returns>
    static int HandleIndexFromString( string _string ) {
        string[] spl = _string.Split( new char[] { '#' } );
        return int.Parse( spl[1] );
    }


    /// <summary>
    /// インスタンスをテキストファイルに出力します
    /// </summary>
    /// <param name="sw">出力先</param>
    void Print( StreamWriter sw ) {
        string result = this.ToString();
        sw.WriteLine( result );
    }

    /// <summary>
    /// インスタンスを文字列に変換します
    /// </summary>
    /// <param name="encode">2バイト文字をエンコードするか否かを指定するフラグ</param>
    /// <returns>インスタンスを変換した文字列</returns>
    string ToString( bool encode ) {
        string result = "";
        result += "[h#" + Index.ToString( "0000" ) + "]";
        switch ( type() ) {
            case VsqHandleType.Lyric:
                result += Environment.NewLine + "L0=" + L0.ToString( encode );
                break;
            case VsqHandleType.Vibrato:
                result += Environment.NewLine + "IconID=" + IconID + Environment.NewLine;
                result += "IDS=" + IDS + Environment.NewLine;
                result += "Original=" + Original + Environment.NewLine;
                result += "Caption=" + Caption + Environment.NewLine;
                result += "Length=" + Length + Environment.NewLine;
                result += "StartDepth=" + StartDepth + Environment.NewLine;
                result += "DepthBPNum=" + DepthBP.Num() + Environment.NewLine;
                if ( DepthBP.Num() > 0 ) {
                    result += "DepthBPX=" + DepthBP.getElement( 0 ).X.ToString( "0.000000" );
                    for ( int i = 1; i < DepthBP.Num(); i++ ) {
                        result += "," + DepthBP.getElement( i ).X.ToString( "0.000000" );
                    }
                    result += Environment.NewLine + "DepthBPY=" + DepthBP.getElement( 0 ).Y;
                    for ( int i = 1; i < DepthBP.Num(); i++ ) {
                        result += "," + DepthBP.getElement( i ).Y;
                    }
                    result += Environment.NewLine;
                }
                result += "StartRate=" + StartRate + Environment.NewLine;
                result += "RateBPNum=" + RateBP.Num();
                if ( RateBP.Num() > 0 ) {
                    result += Environment.NewLine + "RateBPX=" + RateBP.getElement( 0 ).X.ToString( "0.000000" );
                    for ( int i = 1; i < RateBP.Num(); i++ ) {
                        result += "," + RateBP.getElement( i ).X.ToString( "0.000000" );
                    }
                    result += Environment.NewLine + "RateBPY=" + RateBP.getElement( 0 ).Y;
                    for ( int i = 1; i < RateBP.Num(); i++ ) {
                        result += "," + RateBP.getElement( i ).Y;
                    }
                }
                break;
            case VsqHandleType.Singer:
                result += Environment.NewLine + "IconID=" + IconID + Environment.NewLine;
                result += "IDS=" + IDS + Environment.NewLine;
                result += "Original=" + Original + Environment.NewLine;
                result += "Caption=" + Caption + Environment.NewLine;
                result += "Length=" + Length + Environment.NewLine;
                result += "Language=" + m_language + Environment.NewLine;
                result += "Program=" + m_program;
                break;
            default:
                break;
        }
        return result;
    }
};


class VsqID {
public:
    int value;
    VsqIDType type;
    int IconHandle_index;
    IconHandle IconHandle;
    int Length;
    int Note;
    int Dynamics;
    int PMBendDepth;
    int PMBendLength;
    int PMbPortamentoUse;
    int DEMdecGainRate;
    int DEMaccent;
    int LyricHandle_index;
    LyricHandle LyricHandle;
    int VibratoHandle_index;
    VibratoHandle VibratoHandle;
    int VibratoDelay;

    static VsqID EOS( -1 );

    /// <summary>
    /// IDの番号（ID#****の****）を指定したコンストラクタ。
    /// </summary>
    /// <param name="a_value">IDの番号</param>
    VsqID( int a_value ) {
        value = a_value;
    }


    /// <summary>
    /// テキストファイルからのコンストラクタ
    /// </summary>
    /// <param name="sr">読み込み対象</param>
    /// <param name="value"></param>
    /// <param name="last_line">読み込んだ最後の行が返されます</param>
    VsqID( TextMemoryStream sr, int value, string& last_line ) {
        string[] spl;
        this.value = value;
        this.type = VsqIDType.Unknown;
        this.IconHandle_index = -2;
        this.LyricHandle_index = -1;
        this.VibratoHandle_index = -1;
        this.Length = 0;
        this.Note = 0;
        this.Dynamics = 0;
        this.PMBendDepth = 0;
        this.PMBendLength = 0;
        this.PMbPortamentoUse = 0;
        this.DEMdecGainRate = 0;
        this.DEMaccent = 0;
        //this.LyricHandle_index = -2;
        //this.VibratoHandle_index = -2;
        this.VibratoDelay = 0;
        last_line = sr.ReadLine();
        while ( !last_line.StartsWith( "[" ) ) {
            spl = last_line.Split( new char[] { '=' } );
            switch ( spl[0] ) {
                case "Type":
                    if ( spl[1] == "Anote" ) {
                        type = VsqIDType.Anote;
                    } else if ( spl[1] == "Singer" ) {
                        type = VsqIDType.Singer;
                    } else {
                        type = VsqIDType.Unknown;
                    }
                    break;
                case "Length":
                    this.Length = int.Parse( spl[1] );
                    break;
                case "Note#":
                    this.Note = int.Parse( spl[1] );
                    break;
                case "Dynamics":
                    this.Dynamics = int.Parse( spl[1] );
                    break;
                case "PMBendDepth":
                    this.PMBendDepth = int.Parse( spl[1] );
                    break;
                case "PMBendLength":
                    this.PMBendLength = int.Parse( spl[1] );
                    break;
                case "DEMdecGainRate":
                    this.DEMdecGainRate = int.Parse( spl[1] );
                    break;
                case "DEMaccent":
                    this.DEMaccent = int.Parse( spl[1] );
                    break;
                case "LyricHandle":
                    this.LyricHandle_index = VsqHandle.HandleIndexFromString( spl[1] );
                    break;
                case "IconHandle":
                    this.IconHandle_index = VsqHandle.HandleIndexFromString( spl[1] );
                    break;
                case "VibratoHandle":
                    this.VibratoHandle_index = VsqHandle.HandleIndexFromString( spl[1] );
                    break;
                case "VibratoDelay":
                    this.VibratoDelay = int.Parse( spl[1] );
                    break;
                case "PMbPortamentoUse":
                    PMbPortamentoUse = int.Parse( spl[1] );
                    break;
            }
            if ( sr.Peek() < 0 ) {
                break;
            }
            last_line = sr.ReadLine();
        }
    }

    string ToString() {
        string ret = "{Type=" + type;
        switch ( type ) {
            case VsqIDType.Anote:
                ret += ", Length=" + Length;
                ret += ", Note#=" + Note;
                ret += ", Dynamics=" + Dynamics;
                ret += ", PMBendDepth=" + PMBendDepth ;
                ret += ", PMBendLength=" + PMBendLength ;
                ret += ", PMbPortamentoUse=" + PMbPortamentoUse ;
                ret += ", DEMdecGainRate=" + DEMdecGainRate ;
                ret += ", DEMaccent=" + DEMaccent ;
                if ( LyricHandle != null ) {
                    ret += ", LyricHandle=h#" + LyricHandle_index.ToString( "0000" ) ;
                }
                if ( VibratoHandle != null ) {
                    ret += ", VibratoHandle=h#" + VibratoHandle_index.ToString( "0000" );
                    ret += ", VibratoDelay=" + VibratoDelay ;
                }
                break;
            case VsqIDType.Singer:
                ret += ", IconHandle=h#" + IconHandle_index.ToString( "0000" );
                break;
        }
        ret += "}";
        return ret;
    }


    /// <summary>
    /// インスタンスをテキストファイルに出力します
    /// </summary>
    /// <param name="sw">出力先</param>
    void write( TextMemoryStream sw ) {
        sw.WriteLine( "[ID#" + value.ToString( "0000" ) + "]" );
        sw.WriteLine( "Type=" + type );
        switch( type ){
            case VsqIDType.Anote:
                sw.WriteLine( "Length=" + Length );
                sw.WriteLine( "Note#=" + Note );
                sw.WriteLine( "Dynamics=" + Dynamics );
                sw.WriteLine( "PMBendDepth=" + PMBendDepth );
                sw.WriteLine( "PMBendLength=" + PMBendLength );
                sw.WriteLine( "PMbPortamentoUse=" + PMbPortamentoUse );
                sw.WriteLine( "DEMdecGainRate=" + DEMdecGainRate );
                sw.WriteLine( "DEMaccent=" + DEMaccent );
                if ( LyricHandle != null ) {
                    sw.WriteLine( "LyricHandle=h#" + LyricHandle_index.ToString( "0000" ) );
                }
                if ( VibratoHandle != null ) {
                    sw.WriteLine( "VibratoHandle=h#" + VibratoHandle_index.ToString( "0000" ) );
                    sw.WriteLine( "VibratoDelay=" + VibratoDelay );
                }
                break;
            case VsqIDType.Singer:
                sw.WriteLine( "IconHandle=h#" + IconHandle_index.ToString( "0000" ) );
                break;
        }
    }
};

}
#endif // __libvsq_h__
