/*
 * VowelType.cs
 * Copyright (c) 2007-2009 kbinani
 *
 * This file is part of LipSync.
 *
 * LipSync is free software; you can redistribute it and/or
 * modify it under the terms of the BSD License.
 *
 * LipSync 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.
 */
using System;
using System.Collections.Generic;

namespace LipSync {

    public struct MouthSet {
        public VowelType prep;
        public VowelType main;
    }

    /// <summary>
    /// 口の形の種類を定義するクラス。javaぽい
    /// </summary>
    public struct VowelType {
        public static List<string> m_list_nn = new List<string>( new string[] { "b", "p", "m", "b'", "p'", "m'" } );
        public static List<string> m_list_i = new List<string>( new string[] { "k'", "g'", "S", "dZ", "tS", "J", "C" } );
        public static List<string> m_list_u = new List<string>( new string[] { @"p\", @"p\'", "w", "ts", "dz" } );
        public static VowelType def = new VowelType( -1 );
        public static VowelType a = new VowelType( 0 );
        public static VowelType aa = new VowelType( 1 );
        public static VowelType i = new VowelType( 2 );
        public static VowelType u = new VowelType( 3 );
        public static VowelType e = new VowelType( 4 );
        public static VowelType o = new VowelType( 5 );
        public static VowelType xo = new VowelType( 6 );
        public static VowelType nn = new VowelType( 7 );

        private int m_iValue;

        public static bool IsRegisteredToNN( string type ) {
            foreach ( string s in m_list_nn ) {
                if ( type == s ) {
                    return true;
                }
            }
            return false;
        }

        public bool Equals( VowelType item ) {
            if ( this.m_iValue == item.m_iValue ) {
                return true;
            } else {
                return false;
            }
        }

        public static bool IsRegisteredToI( string type ) {
            foreach ( string s in m_list_i ) {
                if ( type == s ) {
                    return true;
                }
            }
            return false;
        }

        public static bool IsRegisteredToU( string type ) {
            foreach ( string s in m_list_u ) {
                if ( type == s ) {
                    return true;
                }
            }
            return false;
        }

        private VowelType( int value ) {
            this.m_iValue = value;
        }

        public int value {
            get {
                return m_iValue;
            }
        }

        new public string ToString() {
            switch ( m_iValue ) {
                case -1:
                    return "def";
                case 0:
                    return "a";
                case 1:
                    return "aa";
                case 2:
                    return "i";
                case 3:
                    return "u";
                case 4:
                    return "e";
                case 5:
                    return "o";
                case 6:
                    return "xo";
                case 7:
                    return "nn";
                default:
                    return "def";
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="lyric"></param>
        /// <returns></returns>
        public static MouthSet AttachEx( string lyric ) {
            string[] spl = lyric.Split( " ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );
            string vowel, consonant;
            MouthSet result = new MouthSet();
            result.main = VowelType.def;
            result.prep = VowelType.def;
            if ( spl.Length == 0 ) {
                return result;
            }
            if ( spl.Length == 1 ) {
                vowel = spl[0];
                consonant = "";
            } else {
                consonant = spl[0];
                vowel = spl[1];
            }
            switch ( vowel ) {
                case "a":
                    result.main = VowelType.a;
                    break;
                case "i":
                    result.main = VowelType.i;
                    break;
                case "M":
                    result.main = VowelType.u;
                    break;
                case "e":
                    result.main = VowelType.e;
                    break;
                case "o":
                    result.main = VowelType.o;
                    break;
                // 以下、子音のみの発音が指定される場合
                case "n":
                case "N":
                case "J":
                case "m":
                case "N\\\\":
                case "N\\":
                case "N'":
                case "m'":
                    result.main = VowelType.nn;
                    break;
                case "ts":
                case "s":
                    result.main = VowelType.u;
                    break;
                case "@": //the sun
                case "V": //strut
                case "{": //trap
                case "aI": //buy
                case "aU": //loud
                case "Q@": //star
                    result.main = VowelType.a;
                    break;
                case "I": //kit
                case "i:": //beef
                case "I@": //beer
                    result.main = VowelType.i;
                    break;
                case "U": //put
                case "u:": //boot
                case "U@": //poor
                    result.main = VowelType.u;
                    break;
                //case "e": //them
                case "@r": //maker
                case "eI": //pay
                case "e@": //bear
                    result.main = VowelType.e;
                    break;
                case "O:": //taught
                case "Q": //lot
                case "OI": //boy
                case "@U": //boat
                case "O@": //pour
                    result.main = VowelType.o;
                    break;
            }
            switch ( consonant ) {
                case "g":
                case "N":
                case "s":
                case "z":
                case "t":
                case "d":
                case "k":
                case "Z":
                case "t'":
                case "d'":
                case "h":
                case "h\\\\":
                case "j":
                case "4'":
                case "N'":
                case "n":
                    break;
                case "b":
                case "p":
                case "m":
                case "b'":
                case "p'":
                case "m'":
                    //foreach ( string s in Form1.Instatnce.Config.LIST_NN ) {
                    foreach ( string s in m_list_i ) {
                        if ( s == consonant ) {
                            result.prep = VowelType.nn;
                            break;
                        }
                    }
                    break;
                case "p\\\\":
                case "p\\\\'":
                case "w":
                case "ts":
                case "dz":
                    //foreach ( string s in Form1.Instatnce.Config.LIST_U ) {
                    foreach ( string s in m_list_u ) {
                        if ( s == consonant ) {
                            result.prep = VowelType.u;
                            break;
                        } else {
                            if ( s == @"p\" && consonant == "p\\\\" ) {
                                result.prep = VowelType.u;
                            } else if ( s == @"p\'" && consonant == "p\\\\'" ) {
                                result.prep = VowelType.u;
                            }
                        }
                    }
                    break;
                case "k'":
                case "g'":
                case "S":
                case "dZ":
                case "tS":
                case "J":
                case "C":
                    //foreach ( string s in Form1.Instatnce.Config.LIST_I ) {
                    foreach ( string s in m_list_i ) {
                        if ( s == consonant ) {
                            result.prep = VowelType.i;
                        }
                    }
                    break;
                case "y":  //[en]  yellow
                    result.prep = VowelType.i;
                    break;
                //case "w":  //[en]  way
                //    result.prep = VowelType.u;
                //    break;
                case "bh": //[en] big
                case "v":  //[en] vote
                //case "m":  //[en] mind
                case "ph": //[en] peace
                    result.prep = VowelType.nn;
                    break;
            }
            return result;
        }

        public static VowelType Attach( string alyric ) {
            string lyric = RemoveNonKanaLetter( alyric );
            switch ( lyric ) {
                case "あ":
                case "か":
                case "が":
                case "さ":
                case "ざ":
                case "た":
                case "だ":
                case "な":
                case "は":
                case "ば":
                case "ぱ":
                case "ま":
                case "や":
                case "ら":
                case "わ":
                case "a":
                /*case "s":
                case "d":
                case "n":
                case "h":
                case "m":
                case "w":
                case "na":*/
                case "しゃ":
                case "ふぁ":
                case "ちゃ":
                case "りゃ":
                case "じゃ":
                    return VowelType.a;

                /*case "4":
                    return VowelType.aa;*/

                case "い":
                case "き":
                case "ぎ":
                case "し":
                case "じ":
                case "ち":
                case "ぢ":
                case "に":
                case "ひ":
                case "び":
                case "ぴ":
                case "み":
                case "り":
                case "i":
                /*case "k'":
                case "g'":
                case "S":
                case "Z":
                case "dZ":
                case "t'":
                case "tS":
                case "d'":
                case "J":
                case "C":
                case "p\\\\'":
                case "b'":
                case "p'":
                case "m'":
                case "4'":*/
                case "ふぃ":
                    return VowelType.i;

                case "う":
                case "く":
                case "ぐ":
                case "す":
                case "ず":
                case "つ":
                case "づ":
                case "ぬ":
                case "ふ":
                case "ぶ":
                case "む":
                case "ゆ":
                case "る":
                case "M":
                /*case "z":
                case "dz":
                case "ts":
                case "p\\\\":
                case "j":
                case "u":*/
                case "ちゅ":
                case "りゅ":
                //case "U":
                case "しゅ":
                case "じゅ":
                    return VowelType.u;

                case "え":
                case "け":
                case "げ":
                case "せ":
                case "ぜ":
                case "て":
                case "で":
                case "ね":
                case "へ":
                case "べ":
                case "め":
                case "れ":
                case "e":
                /*case "g":
                case "t":*/
                case "いぇ":
                    return VowelType.e;

                case "お":
                case "こ":
                case "ご":
                case "そ":
                case "ぞ":
                case "と":
                case "ど":
                case "の":
                case "ほ":
                case "ぼ":
                case "ぽ":
                case "も":
                case "よ":
                case "ろ":
                case "o":
                /*case "b":
                case "p":
                case "yo":
                case "wo":*/
                case "うぉ":
                case "しょ":
                case "ちょ":
                case "りょ":
                case "じょ":
                    return VowelType.o;

                //case "k":
                case "を":
                    //case "h\\\\":
                    return VowelType.xo;

                case "ん":
                    //case "N\\\\":
                    return VowelType.nn;

                default:
                    /*if ( lyric != "ー" ) {
                        Form1.Instatnce.ErrorLog( "LipSync.VowelType.attach", "error", "cannot attach: \"" + lyric + "\"" );
                    }*/
                    return VowelType.def;
            }
        }

        /// <summary>
        /// 文字列strから、平仮名でない文字を除去します。カタカナの場合は平仮名に変換します。
        /// VOCALOIDが使用する発音記号の場合はそのまま残ります
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private static string RemoveNonKanaLetter( string str ) {
            bool changed = true;
            string result = str;
            while ( changed ) {
                changed = false;
                for ( int i = 0; i < result.Length; i++ ) {
                    char ch = result[i];
                    bool iskatakana = IsKatakana( ch );
                    bool ishiragana = IsHiragana( ch );
                    bool isphonetic = IsPhoneticSymbol( ch );
                    //System.Windows.Forms.MessageBox.Show( "ch,IsKatakana,IsHiragana=" + ch + "," + iskatakana + "," + ishiragana );
                    if ( !iskatakana && !ishiragana && !isphonetic ) {
                        string sch = new string( ch, 1 );
                        result = result.Replace( sch, "" );
                        changed = true;
                        break;
                    } else if ( iskatakana ) {
                        result = result.Replace( ch, (char)((int)ch - 96) );
                        changed = true;
                        break;
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 文字letterが平仮名かどうかを判定します。
        /// </summary>
        /// <param name="letter"></param>
        /// <returns></returns>
        private static bool IsHiragana( char letter ) {
            int code = (int)letter;
            if ( 0x3041 <= code && code <= 0x3093 ) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// 文字letterがカタカナかどうかを判定します
        /// </summary>
        /// <param name="letter"></param>
        /// <returns></returns>
        private static bool IsKatakana( char letter ) {
            int code = (int)letter;
            if ( 0x30A1 <= code && code <= 0x30F6 ) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// 文字letterがvocaloidの発音記号かどうかを判定します
        /// </summary>
        /// <param name="letter"></param>
        /// <returns></returns>
        private static bool IsPhoneticSymbol( char letter ) {
            switch ( letter ) {
                case 'a':
                case 'i':
                case 'M':
                case 'e':
                case 'o':
                case 'k':
                case 'g':
                case 'N':
                case 's':
                case 'z':
                case 't':
                case 'd':
                case '\'':
                case 'S':
                case 'Z':
                case 'n':
                case 'h':
                case '\\':
                case 'p':
                case 'b':
                case 'm':
                case 'j':
                case '4':
                case 'w':
                case 'J':
                case 'C':
                case 'r'://br1-5
                case '1':
                case '2':
                case '3':
                case '5':
                    return true;
                default:
                    return false;
            }
        }

        /// <summary>
        /// 文字letterがかな文字かどうかを判定します。
        /// </summary>
        /// <param name="letter"></param>
        /// <returns></returns>
        private static bool IsKana( char letter ) {
            if ( IsHiragana( letter ) && IsKatakana( letter ) ) {
                return true;
            } else {
                return false;
            }
        }
    }

}
