//  MeCab -- Yet Another Part-of-Speech and Morphological Analyzer
//
//  Copyright(C) 2001-2006 Taku Kudo <taku@chasen.org>
//  Copyright(C) 2004-2006 Nippon Telegraph and Telephone Corporation
using System;
using System.Collections.Generic;
using System.Text;

namespace NMeCab.Core
{
    public class Writer
    {
        private const string FloatFormat = "f6";

        private delegate void WriteAction(StringBuilder os, MeCabNode bosNode);
        private WriteAction write;

        private string outputFormatType;

        public string OutputFormatType
        {
            get
            {
                return this.outputFormatType;
            }
            set
            {
                this.outputFormatType = value;
                switch (value)
                {
                    case "lattice":
                        this.write = this.WriteLattice;
                        break;
                    case "wakati":
                        this.write = this.WriteWakati;
                        break;
                    case "none":
                        this.write = this.WriteNone;
                        break;
                    case "dump":
                        this.write = this.WriteDump;
                        break;
                    case "em":
                        this.write = this.WriteEM;
                        break;
                    default:
                        throw new ArgumentOutOfRangeException("value", value, "Not supported Format");
                }
            }
        }

        public void Open(MeCabParam param)
        {
            this.OutputFormatType = param.OutputFormatType;
        }

        public void Write(StringBuilder os, MeCabNode bosNode)
        {
            this.write(os, bosNode);
        }

        public void WriteLattice(StringBuilder os, MeCabNode bosNode)
        {
            for (MeCabNode node = bosNode.Next; node.Next != null; node = node.Next)
            {
                os.Append(node.Surface);
                os.Append("\t");
                os.Append(node.Feature);
                os.AppendLine();
            }
            os.AppendLine("EOS");
        }

        public void WriteWakati(StringBuilder os, MeCabNode bosNode)
        {
            MeCabNode node = bosNode.Next;
            if (node.Next != null)
            {
                os.Append(node.Surface);
                for (node = node.Next; node.Next != null; node = node.Next)
                {
                    os.Append(" ");
                    os.Append(node.Surface);
                }
            }
            os.AppendLine();
        }

        public void WriteNone(StringBuilder os, MeCabNode bosNode)
        {
            // do nothing
        }

        public void WriteUser(StringBuilder os, MeCabNode bosNode)
        {
            throw new NotImplementedException();
        }

        public void WriteEM(StringBuilder os, MeCabNode bosNode)
        {
            const float MinProb = 0.0001f;
            for (MeCabNode node = bosNode; node != null; node = node.Next)
            {
                if (node.Prob >= MinProb)
                {
                    os.Append("U\t");
                    if (node.Stat == MeCabNodeStat.Bos)
                        os.Append("BOS");
                    else if (node.Stat == MeCabNodeStat.Eos)
                        os.Append("EOS");
                    else
                        os.Append(node.Surface);
                    os.Append("\t").Append(node.Feature);
                    os.Append("\t").Append(node.Prob.ToString(FloatFormat));
                    os.AppendLine();
                }
                for (MeCabPath path = node.LPath; path != null; path = path.LNext)
                {
                    if (path.Prob >= MinProb)
                    {
                        os.Append("B\t").Append(path.LNode.Feature);
                        os.Append("\t").Append(node.Feature);
                        os.Append("\t").Append(path.Prob.ToString(FloatFormat));
                        os.AppendLine();
                    }
                }
            }
            os.AppendLine("EOS");
        }

        public void WriteDump(StringBuilder os, MeCabNode bosNode)
        {
            for (MeCabNode node = bosNode; node != null; node = node.Next)
            {
#if NeedId
                os.Append(node.Id).Append(" ");
#endif
                if (node.Stat == MeCabNodeStat.Bos)
                    os.Append("BOS");
                else if (node.Stat == MeCabNodeStat.Eos)
                    os.Append("EOS");
                else
                    os.Append(node.Surface);

                os.Append(" ").Append(node.Feature);
                os.Append(" ").Append(node.BPos);
                os.Append(" ").Append(node.EPos);
                os.Append(" ").Append(node.RCAttr);
                os.Append(" ").Append(node.LCAttr);
                os.Append(" ").Append(node.PosId);
                os.Append(" ").Append(node.CharType);
                os.Append(" ").Append((int)node.Stat);
                os.Append(" ").Append(node.IsBest ? "1" : "0");
                os.Append(" ").Append(node.Alpha.ToString(FloatFormat));
                os.Append(" ").Append(node.Beta.ToString(FloatFormat));
                os.Append(" ").Append(node.Prob.ToString(FloatFormat));
                os.Append(" ").Append(node.Cost);

                for (MeCabPath path = node.LPath; path != null; path = path.LNext)
                {
#if NeedId
                    os.Append(" ").Append(path.LNode.Id);
#endif
                    os.Append(" ");
                    os.Append(":").Append(path.Cost);
                    os.Append(":").Append(path.Prob.ToString(FloatFormat));
                }

                os.AppendLine();
            }
        }

        public unsafe void WriteNode(StringBuilder os, char* p, string sentence, MeCabNode node)
        {
            for (; *p != 0x0; p++)
            {
                switch (*p)
                {
                    default: os.Append(*p); break;
                    case '%':
                        switch (*++p)
                        {
                            default: os.Append("unkonwn meta char ").Append(*p); break;
                            case 'S': os.Append(sentence); break;
                            case 'L': os.Append(sentence.Length); break;
                            case 'm': os.Append(node.Surface); break;
                            case 'M': os.Append(sentence, 
                                                node.BPos - node.RLength + node.Length, 
                                                node.RLength); 
                                                break;
                                 
                        }
                        break;
                }
            }
        }
    }
}
