﻿/******************************************************************************
 プロジェクト: loxXmlUtils
 ライセンス: MIT License (http://opensource.org/licenses/mit-license.php)
   http://sourceforge.jp/projects/opensource/wiki/licenses%2FMIT_license
 *****************************************************************************/
/******************************************************************************
 Copyright (c) 2012-2015 synctam

 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in
 the Software without restriction, including without limitation the rights to
 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 of the Software, and to permit persons to whom the Software is furnished to do
 so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *****************************************************************************/
//using Google.GData.Spreadsheets;
//using GoogleDocUtils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Uguu.IO.Csv;

namespace loxXmlUtils
{
    public class TJxRul
    {
        private enum TNodeMode
        {
            None,
            Tables,
            Cols,
            Rows,
            SingleArray
        }

        protected List<TJxRulItemBase> FItemList = new List<TJxRulItemBase>();
        public List<TJxRulItemBase> ItemList { get { return this.FItemList; } }

        private string FCategory1 = null;
        private string FCategory2 = null;

        private TNodeMode FNodeMode = new TNodeMode();

        private Stack<TNodeMode> FNodeModeStack = new Stack<TNodeMode>();

        protected XmlDocument FXmlDoc = new XmlDocument();

        private TJxRulItemBase FItem = null;

        private List<TJxRulTransCsv> FGoogleTransList = new List<TJxRulTransCsv>();

        public TJxFlashCard FalshCard { get; set; }

        private TJxFlashCard.TConvertMode FConvertMode = TJxFlashCard.TConvertMode.ProperNounJapanese;
        public TJxFlashCard.TConvertMode ConvertMode { set { this.FConvertMode = value; } get { return this.FConvertMode; } }

        protected bool FisTranslate = false;

        private int SimpleNo = 0;

        public void loadFromXml(string FileName, Encoding encXML = null)
        {
            if (encXML == null) encXML = Encoding.GetEncoding("UTF-8");
            using (StreamReader fStream = new StreamReader(FileName, encXML)) {
                this.FXmlDoc.Load(fStream);
            }
            this.FNodeMode = TNodeMode.None;
            XmlElement elem = this.FXmlDoc.DocumentElement;
            if (elem.HasChildNodes == true) {
                XmlNode childNode = elem.FirstChild;
                readNode(childNode);
            }
            Console.WriteLine("loadFromXml {0}", this.FItemList.Count);
        }

        /// <summary>
        /// XMLのノードを読み日本語化情報を抽出する。子ノードがある場合は再帰的に処理する。
        /// </summary>
        /// <param name="node">ノード</param>
        protected void readNode(XmlNode node)
        {
            // Nodeに入った時の処理
            switch (node.Name.ToLower()) {
                case "dictionary":
                    onEnterDictonaryNode(node);
                    break;
                case "singlearray":
                    onEnterSingleArrayNode(node);
                    break;
                case "simple":
                    onEnterSimpleNode(node);
                    break;
                case "null":
                    onEnterNullNode(node);
                    break;
                default:
                    break;
            }

            // 子ノードがあればそれを処理
            if (node.HasChildNodes) {
                foreach (XmlNode nodeData in node.ChildNodes) {
                    FNodeModeStack.Push(this.FNodeMode); // Node情報を保存
                    readNode(nodeData); // 再帰で子ノードの処理
                    this.FNodeMode = FNodeModeStack.Pop(); // Node情報を復帰
                }
            }

            // ノードを抜けた時の処理
            switch (node.Name.ToLower()) {
                case "dictionary":
                    onLeaveDictonaryNode();
                    break;
                case "singlearray":
                    onLeaveSingleArrayNode();
                    break;
                case "simple":
                    onLeaveSimpleNode();
                    break;
                case "null":
                    onLeaveNullNode();
                    break;
                default:
                    break;
            }
        }
        private void onEnterDictonaryNode(XmlNode node)
        {
            foreach (XmlAttribute attr in node.Attributes) {
                switch (attr.Value) {
                    case "Tables":
                        this.FNodeMode = TNodeMode.Tables;
                        break;
                    case "Cols":
                        this.FNodeMode = TNodeMode.Cols;
                        break;
                    case "Rows":
                        this.FNodeMode = TNodeMode.Rows;
                        break;
                    default:
                        //throw new Exception("unknown attr name");
                        break;
                }
            }
        }
        private void onLeaveDictonaryNode()
        {
            // skip
        }
        //*********************************************************************************************************
        private void onEnterSingleArrayNode(XmlNode node)
        {
            this.FNodeMode = TNodeMode.SingleArray;
            this.FItem = TJxRulItemUtils.makeJxRulItem(this.FCategory1);
            this.SimpleNo = 0;
        }
        private void onLeaveSingleArrayNode()
        {
            this.FItemList.Add(this.FItem);
            this.FItem = null;
        }
        //*********************************************************************************************************
        private void onEnterSimpleNode(XmlNode node)
        {
            switch (this.FNodeMode) {
                case TNodeMode.Tables:
                    this.FCategory1 = getCategory(node.Attributes);
                    break;
                case TNodeMode.Cols:
                    break;
                case TNodeMode.Rows:
                    this.FCategory2 = getCategory(node.Attributes); // 未使用だと思う
                    break;
                case TNodeMode.SingleArray:
                    this.FItem.ItemDetailList.Add(getAttrValue(node.Attributes));
                    break;
                default:
                    break;
            }
        }
        private void onLeaveSimpleNode()
        {
            this.SimpleNo++;
        }
        private string getCategory(XmlAttributeCollection xmlAttributeCollection)
        {
            string result = "";
            if (xmlAttributeCollection.Count > 0) {
                foreach (XmlAttribute attr in xmlAttributeCollection) {
                    if (attr.Name == "value") {
                        result = attr.Value;
                    }
                }
            }
            return result;
        }
        private string getAttrValue(XmlAttributeCollection xmlAttributeCollection)
        {
            string result = "";
            if (xmlAttributeCollection.Count > 0) {
                foreach (XmlAttribute attr in xmlAttributeCollection) {
                    if (attr.Name == "value") {
                        result = attr.Value;
                        if (this.FisTranslate) {
                            // ここでXML内を日本語化する
                            TJxRulItemBase obj = TJxRulItemUtils.makeJxRulItem(this.FCategory1);
                            if (obj.checkID(SimpleNo)) {
                                attr.InnerText = this.TranslateE2J(attr.Value, this.FConvertMode); // 固有名詞日本語版@@@@
                            }
                        }
                    }
                }
            }
            return result;
        }
        private string TranslateE2J(string English, TJxFlashCard.TConvertMode ConvertMode)
        {
            // @@@@@@@@@@@@@@@@ 日本語化ロジックを追加すること
            string result = English;
            foreach (TJxRulTransCsv data in this.FGoogleTransList) {
                if (TJxCommon.unEscapeControlChar(data.English.Replace("'", "").Replace(" ", "")) == English.Replace("'", "").Replace(" ", "")) {
                    result = data.Japanese;
                    result = this.FalshCard.Convert(result, ConvertMode);
                    break;
                }
            }
            result = TJxCommon.unEscapeControlChar(result);
            return result;
        }
        //*********************************************************************************************************
        private void onEnterNullNode(XmlNode node)
        {
            switch (this.FNodeMode) {
                case TNodeMode.Tables:
                    break;
                case TNodeMode.Cols:
                    break;
                case TNodeMode.Rows:
                    break;
                case TNodeMode.SingleArray:
                    this.FItem.ItemDetailList.Add("");
                    this.SimpleNo++;
                    break;
                default:
                    break;
            }
        }
        private void onLeaveNullNode()
        {
            // skip
        }
        //*********************************************************************************************************
        public override string ToString()
        {
            StringBuilder result = new StringBuilder(2048);
            foreach (TJxRulItemBase data in this.FItemList) {
                result.Append(data.ToString());
                result.Append("\r\n");
            }
            return result.ToString();
        }
        //*********************************************************************************************************
        public void saveToXml(string FileName)
        {
            this.FNodeMode = TNodeMode.None;
            this.FisTranslate = true;
            this.FItemList.Clear(); // v.1.3 初期化漏れ
            XmlElement elem = this.FXmlDoc.DocumentElement;
            if (elem.HasChildNodes == true) {
                XmlNode childNode = elem.FirstChild;
                readNode(childNode);
            }




            this.FXmlDoc.Save(FileName);
        }
        //*********************************************************************************************************
        public void saveToCsv(string FileName, Encoding encXML = null)
        {
            if (encXML == null) encXML = Encoding.GetEncoding("UTF-8");
            using (StreamWriter writer = new StreamWriter(FileName, false, encXML)) {
                using (CsvWriter csv = new CsvWriter(writer)) {
                    csv.Write("[[Sequence]]");
                    csv.Write("[[Category1]]");
                    csv.Write("[[Category2]]");
                    csv.Write("[[Category3]]");
                    csv.Write("[[Text]]");
                    csv.WriteNewLine();
                    int no = 0;
                    foreach (TJxRulItemBase data in this.FItemList) {
                        foreach (TJxRulItemBase.TJxCsvData aaa in data.ToCsv()) {
                            if (aaa.Text != "") {
                                no++;
                                csv.Write(no.ToString());
                                csv.Write(aaa.Category1);
                                csv.Write(aaa.Category2);
                                csv.Write(aaa.Category3);
                                csv.Write(aaa.Text);
                                csv.WriteNewLine();
                            }
                        }
                    }
                }
            }
        }
        //*********************************************************************************************************
        //*********************************************************************************************************
        internal class TJxRulTransCsv
        {
            public int Seq { get; set; }
            public string Category1 { get; set; }
            public string Category2 { get; set; }
            public string Category3 { get; set; }
            public string English { get; set; }
            public string Japanese { get; set; }
            public string GoogleTrans { get; set; }
            public string Memo { get; set; }
            public string MemoAdmin { get; set; }
        }
        /// <summary>
        /// Googleの翻訳シートのCSVを読み込み翻訳リストを作成する
        /// </summary>
        /// <param name="FileName">翻訳シートのファイル名</param>
        /// <param name="encCSV">文字コード</param>
        internal void makeGoogleTransCsvList(string FileName, Encoding encCSV = null)
        {
            if (encCSV == null) encCSV = Encoding.UTF8;
            using (StreamReader reader = new StreamReader(FileName, encCSV)) {
                using (CsvReader csvReader = new CsvReader(reader, "\n", ",", false)) {
                    this.FGoogleTransList.Clear();
                    while (csvReader.Read()) {
                        if (csvReader[0].StartsWith("[[")) {
                            // 1行目は見出し行なのでスキップ
                        } else {
                            TJxRulTransCsv data = new TJxRulTransCsv();
                            data.Seq = int.Parse(csvReader[0]);
                            data.Category1 = csvReader[1];
                            data.Category2 = csvReader[2];
                            data.Category3 = csvReader[3];
                            data.English = csvReader[4];
                            data.Japanese = csvReader[5];
                            data.GoogleTrans = csvReader[6];
                            data.Memo = csvReader[7];
                            data.MemoAdmin = csvReader[8];
                            if ((data.Japanese != "") && (data.Japanese != null)) { // 翻訳済みデータのみ登録
                                this.FGoogleTransList.Add(data);
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Googleの翻訳シートをネット経由で読み込み翻訳リストを作成する
        /// </summary>
        //internal void makeGoogleTransList(string appName, string user, string ClientID, string ClientSecret, string BookName, string SheetName = null)
        //{
        //    if (string.IsNullOrWhiteSpace(BookName)) {
        //        BookName = TJxCommon.BOOK_NAME;
        //    }
        //    if (string.IsNullOrWhiteSpace(SheetName)) {
        //        SheetName = "SOL.jxrul";
        //    }
        //    this.FGoogleTransList.Clear();
        //    try {
        //        JxGoogleSpreadsheet ggl = new JxGoogleSpreadsheet();
        //        ListFeed sheet = ggl.getTablebyBookAndSheet(appName, user, ClientID, ClientSecret, BookName, SheetName);
        //        foreach (ListEntry row in sheet.Entries) {
        //            TJxRulTransCsv data = new TJxRulTransCsv();
        //            data.Seq = int.Parse(row.Elements[0].Value);
        //            data.Category1 = row.Elements[1].Value;
        //            data.Category2 = row.Elements[2].Value;
        //            data.Category3 = row.Elements[3].Value;
        //            data.English = row.Elements[4].Value;
        //            data.Japanese = row.Elements[5].Value;
        //            data.GoogleTrans = row.Elements[6].Value;
        //            data.Memo = row.Elements[7].Value;
        //            data.MemoAdmin = row.Elements[8].Value;
        //            //item.Dummy = row.Elements[9].Value;
        //            if (!string.IsNullOrWhiteSpace(data.Japanese)) {
        //                // 翻訳済みデータのみ登録
        //                this.FGoogleTransList.Add(data);
        //            }
        //        }
        //        Console.WriteLine("Googleスプレッドシート処理件数({0})：{1}", SheetName, this.FGoogleTransList.Count);
        //    }
        //    catch (ArgumentException ex) {
        //        Console.Error.WriteLine(ex.StackTrace);
        //        Console.Error.WriteLine(ex.Message);
        //        throw;
        //    }
        //}
        /// <summary>
        /// 確認用CSVファイルを出力する。
        /// </summary>
        /// <param name="FileNameOut"></param>
        /// <param name="encCSV"></param>
        internal void checkTrans(string FileNameOut, Encoding encCSV = null)
        {
            List<TJxRulTransCsv> csvOutList = new List<TJxRulTransCsv>();
            if (encCSV == null) encCSV = Encoding.UTF8;
            int seq = 0;
            foreach (TJxRulItemBase item in this.FItemList) {
                TJxRulTransCsv outItem = new TJxRulTransCsv();
                outItem.Category1 = item.Category;
                outItem.Category2 = "";
                outItem.Category3 = "";
                foreach (KeyValuePair<string, string> pair in item.ToDictionary()) {
                    if (pair.Key == "ID") {
                        outItem.Category2 = pair.Value;
                    } else {
                        outItem.Category3 = pair.Key;
                        outItem.English = pair.Value;
                        outItem.Japanese = "";
                        outItem.GoogleTrans = "";
                        outItem.Memo = "";
                        outItem.MemoAdmin = "";
                        seq++;
                        outItem.Seq = seq;
                        foreach (TJxRulTransCsv trans in FGoogleTransList) {
                            if (outItem.English == trans.English) {
                                outItem.GoogleTrans = trans.GoogleTrans;
                                outItem.Japanese = trans.Japanese;
                                outItem.Memo = trans.Memo;
                                outItem.MemoAdmin = trans.MemoAdmin;
                                break;
                            }
                        }
                        csvOutList.Add(outItem);
                    }
                }
            }

            using (StreamWriter writer = new StreamWriter(FileNameOut, false, Encoding.UTF8)) {
                using (CsvWriter csv = new CsvWriter(writer)) {
                    //csv.Write("[[FileName]]");
                    csv.Write("[[Seq]]");
                    csv.Write("[[Category1]]");
                    csv.Write("[[Category2]]");
                    csv.Write("[[Category3]]");
                    csv.Write("[[English]]");
                    csv.Write("[[Japanese]]");
                    csv.Write("[[Google翻訳]]");
                    csv.Write("[[メモ]]");
                    csv.Write("[[管理用メモ]]");
                    csv.WriteNewLine();
                    foreach (TJxRulTransCsv data in csvOutList) {
                        //csv.Write(data.FileName);
                        csv.Write(data.Seq.ToString());
                        csv.Write(data.Category1);
                        csv.Write(data.Category2);
                        csv.Write(data.Category3);
                        csv.Write(data.English);
                        csv.Write(data.Japanese);
                        csv.Write(data.GoogleTrans);
                        csv.Write(data.Memo);
                        csv.Write(data.MemoAdmin);
                        csv.WriteNewLine();
                    }
                }
            }
        }
        //*********************************************************************************************************
        //*********************************************************************************************************
    }
}
