/*
 * RsiWriter.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.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Xml;

using Boare.Lib.AppUtil;

namespace LipSync {

    public class RsiWriter {
        #region Constant String
        static readonly string[] mouth_set = new string[]{
            "a",
            "aa",
            "i",
            "u",
            "e",
            "o",
            "xo",
            "nn"
        };
        static readonly string[] mouth_set_ja = new string[]{
            "あ",
            "ああ",
            "い",
            "う",
            "え",
            "お",
            "ぉ",
            "ん"
        }; // mouth_setと同順で対応して無いとNG
        static readonly string[] dict_text = new string[]{
            "あ,a",
            "か,a",
            "が,a",
            "さ,a",
            "ざ,a",
            "た,a",
            "だ,a",
            "な,a",
            "は,a",
            "ば,a",
            "ぱ,a",
            "ま,a",
            "や,a",
            "ら,a",
            "わ,a",
            "い,i",
            "き,i",
            "ぎ,i",
            "し,i",
            "じ,i",
            "ち,i",
            "ぢ,i",
            "に,i",
            "ひ,i",
            "び,i",
            "ぴ,i",
            "み,i",
            "り,i",
            "う,u",
            "く,u",
            "ぐ,u",
            "す,u",
            "ず,u",
            "つ,u",
            "づ,u",
            "ぬ,u",
            "ふ,u",
            "ぶ,u",
            "む,u",
            "ゆ,u",
            "る,u",
            "え,e",
            "け,e",
            "げ,e",
            "せ,e",
            "ぜ,e",
            "て,e",
            "で,e",
            "ね,e",
            "へ,e",
            "べ,e",
            "め,e",
            "れ,e",
            "お,o",
            "こ,o",
            "ご,o",
            "そ,o",
            "ぞ,o",
            "と,o",
            "ど,o",
            "の,o",
            "ほ,o",
            "ぼ,o",
            "ぽ,o",
            "も,o",
            "よ,o",
            "ろ,o",
            "を,xo",
            "ん,nn"
        };
        static readonly string[] dict_symbol = new string[]{
            "a,a",
            "i,i",
            "M,u",
            "e,e",
            "o,o",
            "k,xo",
            "k',i",
            "g,e",
            "g',i",
            "s,a",
            "S,i",
            "z,u",
            "Z,i",
            "dz,u",
            "dZ,i",
            "t,e",
            "t',i",
            "ts,u",
            "tS,i",
            "d,a",
            "d',i",
            "n,a",
            "J,i",
            "h,a",
            @"h\,xo",
            "C,i",
            @"p\,u",
            @"p\',i",
            "b,o",
            "b',i",
            "p,o",
            "p',i",
            "m,a",
            "m',i",
            "j,u",
            "4,aa",
            "4',i",
            "w,a",
            @"N\,nn"
        };
        #endregion

        public static string _( string s ) {
            return Messaging.GetMessage( s );
        }

        /// <summary>
        /// a, aa, i, u, e, o, xo, nnを、あ・ああ・い・う・え・お・ぉ・んに変換します。それ以外は""を返す
        /// </summary>
        /// <param name="symbol"></param>
        /// <returns></returns>
        private static string Translate( string symbol ) {
            for ( int i = 0; i < mouth_set.Length; i++ ) {
                if ( mouth_set[i] == symbol ) {
                    return mouth_set_ja[i];
                }
            }
            return "";
        }


        /// <summary>
        /// 与えられたタイトルの画像が口パク用の基本画像セットの物かどうかを判定します
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        private static bool IsImageNameForMouth( string name ) {
            foreach ( string s in mouth_set ) {
                if ( s == name ) {
                    return true;
                }
            }
            return false;
        }


        public static void Write( Character3 character, string fpath ) {
            string image_folder_name = Path.GetFileNameWithoutExtension( fpath );
            string image_folder = Path.Combine( Path.GetDirectoryName( fpath ), image_folder_name );
            if ( Directory.Exists( image_folder ) ) {
                DialogResult res = MessageBox.Show(
                    _( "Image directory already exists. Would you like to overwrite them?" ),
                    _( "warning" ),
                    MessageBoxButtons.YesNoCancel,
                    MessageBoxIcon.Exclamation,
                    MessageBoxDefaultButton.Button2 );
                if ( res == DialogResult.No ) {
                    using ( OpenFileDialog dlg = new OpenFileDialog() ) {
                        dlg.FileName = Path.GetDirectoryName( fpath );
                        if ( dlg.ShowDialog() == DialogResult.OK ) {
                            image_folder = dlg.FileName;
                            image_folder_name = Path.GetFileName( image_folder );
                        } else {
                            return;
                        }
                    }
                } else if ( res == DialogResult.Cancel ) {
                    return;
                }
            } else {
                Directory.CreateDirectory( image_folder );
            }
#if DEBUG
            Common.DebugWriteLine( "image_folder_name=" + image_folder_name );
            Common.DebugWriteLine( "image_folder=" + image_folder );
#endif
            XmlTextWriter doc = new XmlTextWriter( fpath, Encoding.UTF8 );
            doc.Formatting = Formatting.Indented;
            doc.Indentation = 2;
            doc.IndentChar = ' ';

            // タグが未指定な物のgroup名を決めておく
            string group_name_for_empty = "Another";
            bool found = true;
            int count = 0;
            while ( found ) {
                found = false;
                foreach ( ImageEntry img in character ) {
                    if ( img.tag == group_name_for_empty ) {
                        found = true;
                        break;
                    }
                }
                if ( found ) {
                    count++;
                    group_name_for_empty = "Another" + count;
                }
            }

            doc.WriteStartElement( "SingerConfig" );
            {
                doc.WriteElementString( "Title", character.Name );
                doc.WriteElementString( "Size", character.Size.Width + "," + character.Size.Height );
                doc.WriteElementString( "RootImageName", "root" );
                List<string> sets = new List<string>(); // root以下のsetのリスト

                #region "SingerConfig/Images"
                doc.WriteStartElement( "Images" );
                {
                    // トリガー込み
                    foreach ( ImageEntry img in character ) {
                        if ( IsImageNameForMouth( img.title ) ) {
                            continue;
                        }
                        doc.WriteStartElement( "Image" );
                        {
                            doc.WriteAttributeString( "center", (-img.XOffset) + ", " + (-img.YOffset) );
                            doc.WriteAttributeString( "name", img.title );
                            doc.WriteAttributeString( "size", img.Image.Width + ", " + img.Image.Height );
                            string img_fname = img.title + ".png";
                            img.Image.Save( Path.Combine( image_folder, img_fname ), System.Drawing.Imaging.ImageFormat.Png );
                            doc.WriteAttributeString( "path", image_folder_name + "/" + img_fname );
                            doc.WriteAttributeString( "trigger", img.title );
                        }
                        doc.WriteEndElement();
                    }
                    // 基本口画像。トリガー指定の無い奴も出力しないといけない？（未確認
                    foreach ( ImageEntry img in character ) {
                        if ( !IsImageNameForMouth( img.title ) ) {
                            continue;
                        }

                        // 自動用
                        doc.WriteStartElement( "Image" );
                        {
                            doc.WriteAttributeString( "center", (-img.XOffset) + ", " + (-img.YOffset) );
                            doc.WriteAttributeString( "name", img.title );
                            doc.WriteAttributeString( "size", img.Image.Width + ", " + img.Image.Height );
                            string img_fname = img.title + ".png";
                            img.Image.Save( Path.Combine( image_folder, img_fname ), System.Drawing.Imaging.ImageFormat.Png );
                            doc.WriteAttributeString( "path", image_folder_name + "/" + "mouth_" + img_fname );
                        }
                        doc.WriteEndElement();

                        // トリガー用
                        doc.WriteStartElement( "Image" );
                        {
                            doc.WriteAttributeString( "center", (-img.XOffset) + ", " + (-img.YOffset) );
                            doc.WriteAttributeString( "name", "mouth_" + img.title );
                            doc.WriteAttributeString( "size", img.Image.Width + ", " + img.Image.Height );
                            string img_fname = "mouth_" + img.title + ".png";
                            img.Image.Save( Path.Combine( image_folder, img_fname ), System.Drawing.Imaging.ImageFormat.Png );
                            doc.WriteAttributeString( "path", image_folder_name + "/" + img_fname );
                            doc.WriteAttributeString( "trigger", Translate( img.title ) );
                        }
                        doc.WriteEndElement();
                    }

                    // root/mouth_auto_set
                    doc.WriteStartElement( "Set" );
                    {
                        doc.WriteAttributeString( "name", "mouth_auto_set" );
                        doc.WriteAttributeString( "size", character.Size.Width + "," + character.Size.Height );
                        doc.WriteAttributeString( "count", 1 + "" );
                        doc.WriteAttributeString( "trigger", "mouth_auto" );

                        doc.WriteStartElement( "Image" );
                        {
                            doc.WriteAttributeString( "index", 0 + "" );
                            doc.WriteAttributeString( "imageName", "mouth" );
                            doc.WriteAttributeString( "offset", "0,0" );
                        }
                        doc.WriteEndElement();
                        sets.Add( "mouth_auto" );
                    }
                    doc.WriteEndElement();

                    // root/{tag}_manual
                    List<string> listed_tag = new List<string>(); // 出力済みのタグのリスト
                    bool added = true;
                    while ( added ) {
                        added = false;
                        // 次に出力すべきタグを検索
                        string next_tag = "";
                        List<string> print = new List<string>();
                        foreach ( ImageEntry img in character ) {
                            bool found2 = false;
                            foreach ( string s in listed_tag ) {
                                if ( s == img.tag ) {
                                    found2 = true;
                                    break;
                                }
                            }
                            if ( !found2 ) {
                                if ( img.tag == "" ) {
                                    next_tag = group_name_for_empty;
                                } else {
                                    next_tag = img.tag;
                                }
                                break;
                            }
                        }

                        if ( next_tag != "" ) {
                            added = true;
                            if ( next_tag == group_name_for_empty ) {
                                listed_tag.Add( "" );
                            } else {
                                listed_tag.Add( next_tag );
                            }
                            foreach ( ImageEntry img in character ) {
                                if ( img.tag == next_tag ) {
                                    print.Add( img.title );
                                }
                            }
                            foreach ( string s in print ) {
                                // 本体画像が含まれてたらキャンセル
                                if ( s == "base" ) {
                                    print.Clear();
                                    break;
                                }
                            }
                            if ( print.Count > 0 ) {
                                doc.WriteStartElement( "Set" );
                                {
                                    doc.WriteAttributeString( "name", next_tag + "_manual_set" );
                                    doc.WriteAttributeString( "trigger", next_tag + "_manual" );
                                    sets.Add( next_tag + "_manual" );
                                    doc.WriteAttributeString( "size", character.Size.Width + ", " + character.Size.Height );
                                    doc.WriteAttributeString( "count", print.Count + "" );
                                    for ( int i = 0; i < print.Count; i++ ) {
                                        doc.WriteStartElement( "Image" );
                                        {
                                            doc.WriteAttributeString( "index", i + "" );
                                            if ( IsImageNameForMouth( print[i] ) ) {
                                                doc.WriteAttributeString( "imageName", "mouth_" + print[i] );
                                            } else {
                                                doc.WriteAttributeString( "imageName", print[i] );
                                            }
                                            doc.WriteAttributeString( "offset", "0,0" );
                                        }
                                        doc.WriteEndElement();
                                    }
                                }
                                doc.WriteEndElement();
                            }
                        } else {
                            break;
                        }
                    }

                    // root
                    doc.WriteStartElement( "Set" );
                    {
                        doc.WriteAttributeString( "name", "root" );
                        doc.WriteAttributeString( "size", character.Size.Width + ", " + character.Size.Height );
                        doc.WriteAttributeString( "count", (1 + sets.Count) + "" );

                        doc.WriteStartElement( "Image" );
                        {
                            doc.WriteAttributeString( "index", 0 + "" );
                            doc.WriteAttributeString( "imageName", "base" );
                            doc.WriteAttributeString( "offset", "0,0" );
                        }
                        doc.WriteEndElement();

                        for ( int i = 0; i < sets.Count; i++ ) {
                            doc.WriteStartElement( "Image" );
                            {
                                doc.WriteAttributeString( "index", (i + 1) + "" );
                                doc.WriteAttributeString( "imageName", sets[i] + "_set" );
                                doc.WriteAttributeString( "offset", "0,0" );
                            }
                            doc.WriteEndElement();
                        }

                    }
                    doc.WriteEndElement();
                }
                doc.WriteEndElement();
                #endregion

                #region "SingerConfig/Triggers"
                doc.WriteStartElement( "Triggers" );
                {
                    foreach ( string s in sets ) {
                        doc.WriteStartElement( "Trigger" );
                        {
                            doc.WriteAttributeString( "group", s + "_control" );
                            doc.WriteAttributeString( "name", s );
                            if ( s == "mouth_auto" ) {
                                doc.WriteAttributeString( "default", "on" );
                            }
                        }
                        doc.WriteEndElement();
                    }

                    foreach ( ImageEntry img in character ) {
                        if ( img.title == "base" ) {
                            continue;
                        }
                        doc.WriteStartElement( "Trigger" );
                        {
                            if ( img.tag == "" ) {
                                doc.WriteAttributeString( "group", group_name_for_empty );
                            } else {
                                doc.WriteAttributeString( "group", img.tag );
                            }
                            if ( IsImageNameForMouth( img.title ) ) {
                                doc.WriteAttributeString( "name", Translate( img.title ) );
                            } else {
                                doc.WriteAttributeString( "name", img.title );
                            }
                        }
                        doc.WriteEndElement();
                    }

                    Dictionary<string, string> dic = new Dictionary<string, string>();
                    foreach ( ImageEntry img in character ) {
                        if ( img.tag == "" || img.title == "base" ) {
                            continue;
                        }
                        if ( dic.ContainsKey( img.tag ) ) {
                            dic[img.tag] = dic[img.tag] + "\n" + img.title;
                        } else {
                            dic[img.tag] = img.title;
                        }
                    }
                    foreach ( string key in dic.Keys ) {
                        string items = dic[key];
                        string[] spl = items.Split( "\n".ToCharArray() );
                        doc.WriteStartElement( "Selection" );
                        {
                            foreach ( string s in spl ) {
                                doc.WriteStartElement( "Item" );
                                {
                                    doc.WriteAttributeString( "Trigger", s );
                                }
                                doc.WriteEndElement();
                            }
                        }
                        doc.WriteEndElement();
                    }
                }
                doc.WriteEndElement();
                #endregion

                #region "SingerConfig/Mouths"
                doc.WriteStartElement( "Mouths" );
                {
                    doc.WriteStartElement( "Default" );
                    {
                        doc.WriteAttributeString( "imageName", "nn" );
                    }
                    doc.WriteEndElement();
                    foreach ( string s in dict_text ) {
                        string[] spl = s.Split( ",".ToCharArray() );
                        doc.WriteStartElement( "Attach" );
                        {
                            doc.WriteAttributeString( "text", spl[0] );
                            doc.WriteAttributeString( "imageName", spl[1] );
                        }
                        doc.WriteEndElement();
                    }
                    foreach ( string s in dict_symbol ) {
                        string[] spl = s.Split( ",".ToCharArray() );
                        doc.WriteStartElement( "Attach" );
                        {
                            doc.WriteAttributeString( "symbol", spl[0] );
                            doc.WriteAttributeString( "imageName", spl[1] );
                        }
                        doc.WriteEndElement();
                    }
                }
                doc.WriteEndElement();
                #endregion

            }
            doc.WriteEndElement();
            doc.Close();
        }
    }
}
