/*
 * Lyric.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 jp.sourceforge.lipsync.bocoree.*;

/**
 * VsqHandleに格納される歌詞の情報を扱うクラス。
 */
public class Lyric {
    //private String m_phrase;
    public String Phrase;
    private String[] m_phonetic_symbol;
    //private float UnknownFloat;
    public float UnknownFloat;
    private int[] m_consonant_adjustment;
    private boolean m_protected;
    public boolean PhoneticSymbolProtected;

    /*public proprety boolean PhoneticSymbolProtected {
    get {
    return m_protected;
    }
    set {
    m_protected = value;
    }
    };*/
    /*public property float UnknownFloat {
    get {
    return UnknownFloat;
    }
    set {
    UnknownFloat = value;
    }
    };*/
    public int[] getConsonantAdjustment() {
        return m_consonant_adjustment;
    }

    /**
     * このオブジェクトの簡易コピーを取得します。
     * @returns このインスタンスの簡易コピー
     */
    public Lyric clone() {
        Lyric result = new Lyric();
        result.Phrase = this.Phrase;
        result.m_phonetic_symbol = (String[]) this.m_phonetic_symbol.clone();
        result.UnknownFloat = this.UnknownFloat;
        result.m_consonant_adjustment = (int[]) this.m_consonant_adjustment.clone();
        result.m_protected = m_protected;
        return result;
    }

    /**
     * 歌詞、発音記号を指定したコンストラクタ
     * @param phrase 歌詞
     * @param phonetic_symbol 発音記号
     */
    public Lyric( String phrase, String phonetic_symbol ) {
        Phrase = phrase;
        setPhoneticSymbol( phonetic_symbol );
        UnknownFloat = 0.000000f;
    }

    private Lyric() {
    }


    /*// <summary>
    /// この歌詞のフレーズを取得または設定します。
    /// </summary>
    public proprety String Phrase {
    get {
    return m_phrase;
    }
    set {
    m_phrase = value;
    }
    };*/
    /// <summary>
    /// この歌詞の発音記号を取得または設定します。
    /// </summary>
    public String getPhoneticSymbol() {
        String ret = m_phonetic_symbol[0];
        for ( int i = 1; i < m_phonetic_symbol.length; i++ ) {
            ret += " " + m_phonetic_symbol[i];
        }
        return ret;
    }

    public void setPhoneticSymbol( String value ) {
        String s = value.replace( "  ", " " );
        m_phonetic_symbol = s.split( " ", 16 );
        for ( int i = 0; i < m_phonetic_symbol.length; i++ ) {
            m_phonetic_symbol[i] = m_phonetic_symbol[i].replace( "\\\\", "\\" );
        }
        m_consonant_adjustment = new int[m_phonetic_symbol.length];
        for ( int i = 0; i < m_phonetic_symbol.length; i++ ) {
            if ( VsqPhoneticSymbol.IsConsonant( m_phonetic_symbol[i] ) ) {
                m_consonant_adjustment[i] = 64;
            } else {
                m_consonant_adjustment[i] = 0;
            }
        }
    }

    public String[] getPhoneticSymbolList() {
        String[] ret = new String[m_phonetic_symbol.length];
        for ( int i = 0; i < m_phonetic_symbol.length; i++ ) {
            ret[i] = m_phonetic_symbol[i];
        }
        return ret;
    }


/// <summary>
/// 文字列からのコンストラクタ
/// </summary>
/// <param name="_line">生成元の文字列</param>
    public Lyric( String _line ) {
        if ( _line.length() <= 0 ) {
            Phrase = "a";
            setPhoneticSymbol( "a" );
            UnknownFloat = 1.0f;
            m_protected = false;
        } else {
            String[] spl = _line.split( "," );
            int c_length = spl.length - 3;
            if ( spl.length < 4 ) {
                Phrase = "a";
                setPhoneticSymbol( "a" );
                UnknownFloat = 0.0f;
                m_protected = false;
            } else {
                Phrase = decode( spl[0] );
                setPhoneticSymbol( decode( spl[1] ) );
                UnknownFloat = Float.valueOf( spl[2] );
                m_protected = (spl[spl.length - 1] == "0") ? false : true;
            }

        }
    }


    /// <summary>
    /// mIndexOfのテストメソッド。search, valueをいろいろ変えてテストする事。
    /// </summary>
    /// <returns></returns>
    public static boolean test_mIndexOf() {
        byte[] search = { 0, 12, 3, 5, 16, 34 };
        byte[] value = { 16, 34 };
        if ( mIndexOf( search, value ) == 4 ) {
            return true;
        } else {
            return false;
        }

    }


    /// <summary>
    /// バイト並びsearchの中に含まれるバイト並びvalueの位置を探します。
    /// </summary>
    /// <param name="search">検索対象のバイト並び</param>
    /// <param name="value">検索するバイト並び</param>
    /// <returns>valueが見つかればそのインデックスを、見つからなければ-1を返します</returns>
    private static int mIndexOf( byte[] search, byte[] value ) {
        int i, j;
        int search_length = search.length;
        int value_length = value.length;

        // 検索するバイト並びが、検索対象のバイト並びより長いとき。
        // 見つかるわけない
        if ( value_length > search_length ) {
            return -1;
        }

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

    /**
     * エスケープされた\"や、\x**を復帰させます
     * @param _String デコード対象の文字列
     * @returns デコード後の文字列
     */
    public static String decode( String _String ) {
        String result = _String;
        result = result.replace( "\\\"", "" );
        //Encoding sjis = Encoding.GetEncoding( 932 );
        //Encoding sjis = Encoding.GetEncoding( "Shift_JIS" );
        byte[] str = result.getBytes();

        //Console.WriteLine( "Lyric.decode; sjis.GetString( str )=" + sjis.GetString( str ) );
        byte[] x16 = "\\x".getBytes();
        int index = mIndexOf( str, x16 );
        while ( index >= 0 ) {
            //Console.WriteLine( "Lyric.decode; index=" + index );
            byte[] chr_byte = new byte[2];
            chr_byte[0] = str[index + 2];
            chr_byte[1] = str[index + 3];
            String chr;
            try {
                chr = new String( chr_byte, "UTF-8" );
            } catch ( Exception e ) {
                chr = "";
            }
            //Console.WriteLine( "Lyric.decode; chr=" + chr );
            int chrcode = Integer.parseInt( chr, 16 );
            str[index] = (byte) chrcode;
            for ( int i = index + 4; i < str.length; i++ ) {
                str[i - 3] = str[i];
            }

            int length = str.length - 3;
            byte[] new_str = new byte[length];
            for ( int i = 0; i < length; i++ ) {
                new_str[i] = str[i];
            }
            str = new_str;
            index = mIndexOf( str, x16 );
        }
        //return sjis.GetString( str );
        return cp932.convert( str );
    }


/// <summary>
/// 与えられた文字列の中の2バイト文字を\x**の形式にエンコードします。
/// </summary>
/// <param name="item">エンコード対象</param>
/// <returns>エンコードした文字列</returns>
    public static char[] encode( String item ) {
        //Encoding sjis = Encoding.GetEncoding( 932 );
        byte[] 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 {
                int a = bytea[i];
                result += "\\x" + Integer.toHexString( (int) bytea[i] );
            }

        }
        char[] res = result.toCharArray();
        return res;
    }


/// <summary>
/// 与えられた文字列をShift_JISとみなし、byte[]に変換しさらにchar[]に変換したもの返します
/// </summary>
/// <param name="item">変換元の文字列</param>
/// <returns>変換後のchar[]</returns>
    public static char[] 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>
    public String toString( boolean a_encode ) {
        String result;
        if ( a_encode ) {
            String njp = new String( encode( this.Phrase ) );
            result = "\"" + njp + "\",\"" + getPhoneticSymbol() + "\"," + String.format( "0.000000", UnknownFloat );
        } 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 += "\",\"" + getPhoneticSymbol() + "\"," + String.format( "0.000000", UnknownFloat );
            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>
/// 文字がプリント出力可能かどうかを判定します
/// </summary>
/// <param name="ch"></param>
/// <returns></returns>
    private static boolean isprint( char ch ) {
        if ( 32 <= (int) ch && (int) ch <= 126 ) {
            return true;
        } else {
            return false;
        }

    }


    /// <summary>
    /// Lyricインスタンスを構築するテストを行います
    /// </summary>
    /// <returns>テストに成功すればtrue、そうでなければfalseを返します</returns>
    public static boolean test() {

        String line = "\\\"\\x82\\xe7\\\",\\\"4 a\\\",1.000000,64,1,1";
        //Console.WriteLine( "Lyric.test; line=" + line );
        Lyric lyric = new Lyric( line );
        if ( lyric.Phrase == "ら" &&
                lyric.getPhoneticSymbol() == "4 a" &&
                lyric.UnknownFloat == 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;
        }
    }

}
