using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using lkfxdok.c;

namespace lkfxdok.LKF
{


    [Flags]
    public enum ContextFlag : int
    {
        BMG = 1,            // BMG-Modus
        Flag1 = 2,
        Flag2 = 4,
        Standort = 1<<3,    // Schalted Standort-Info aus (>2015) bzw. ein (<=2014)
        FillBMG = 1<<4,     // bei der Ausgabe die BMG-Felder (Altersbereich / Neonat) befüllen
        UTF8 = 1<<5,        // Schaltet UTF8 aus (>2017) bzw. ein (<=2016)
        QuickScore = 1<<6, 
        Flag7 = 1<<7,
        PrivateSpezlist = 1<<8,
        PrivateKostlist = 1<<9,
        PrivateMELKAList = 1<<10,
        NeoPaed = 1<<11,
        NichtFonds = 1<<12,
        NurMBDS = 1<<13,
        Intensiv = 1<<14,
        Flags15 = 1<<15,
        Flags16 = 1<<16,
        ExtendedErrorInfo = 1<<17,
        OSG = 1<<18,
        Flags19 = 1<<19,
        Flags20 = 1<<20,
        Sim1 = 1<<21,
        Sim2 = 1<<22,
        OSGWarn = 1<<23,
        Flags24 = 1<<24,
        Flags25 = 1<<25,
        Sim3 = 1<<26,
        Sim4 = 1<<27
    }

    public class Context
    {
        internal LKFContext _context;

        public Context ()
        {
           _context = new LKFContext();
        }

        public int Flags
        {
            get
            {
                return _context.Flags;
            }
            set
            {
                _context.Flags = value;
            }
        }

        public ContextFlag ContextFlags
        {
            get
            {
                return (ContextFlag)_context.Flags;
            }
            set
            {
                _context.Flags = (int) value;
            }
        }

        public int Modelljahr
        {
            get
            {
                return _context.Modelljahr;
            }
            set
            {
                _context.Modelljahr = value;
            }
        }

        public void SetMetaData (LKFMetaData metaData)
        {
           _context.SetMetaData(metaData);
        }

        public int Abrechnungsjahr
        {
            get
            {
                return _context.Abrechnungsjahr;
            }
            set
            {
                _context.Abrechnungsjahr = value;
            }
        }

        public void SetAbrechnungsPeriode(int jahr, int period_val, int lfdnr, LKFContextPeriode periode, int flags)
        {
            _context.SetAbrechnungsPeriode(jahr, period_val, lfdnr, periode, flags);
        }

        public LKFContext C
        {
            get
            {
                return _context;
            }
        }

        public void Execute (Aufenthalt aufenthalt)
        {
            aufenthalt.Execute(this);
        }
    }

    public class ContextWithData : Context, IDisposable
    {
        LKFContextWithData _contextWithData;
        public ic.IntContext IntContext { get; private set; }
        public stat.StatContext StatContext { get; private set; }

        public ContextWithData()
        {
            _contextWithData = new LKFContextWithData();
            _context = _contextWithData;
        }

        protected ContextWithData (LKFContextWithData ctx)
        {
            _contextWithData = ctx;
            _context = ctx;
        }

        public void WriteFile(LKFContextFileType fileType, string outputPath )
        {
            _contextWithData.WriteFile(fileType, outputPath);
        }

        public void WriteFile(LKFContextFileType fileType, StreamWriter writer)
        {
            _contextWithData.WriteFile(fileType, writer);
        }

        public void WriteCSVFile(int[] fields, string outputPath)
        {
            _contextWithData.WriteCSVFile(fields, outputPath);
        }

        public void WriteCSVFile(int[] fields, StreamWriter writer)
        {
            _contextWithData.WriteCSVFile(fields, writer);
        }

        public IEnumerable<LKFLine> AufenthaltLKFLines
        {
            get
            {
                foreach (var a in Aufenthalte)
                {
                    foreach (var l in a.LKFLines) yield return l;
                }
            }
        }

        public IEnumerable<String> AufenthaltLines => AufenthaltLKFLines.Select(x => x.Line);
 
        public IEnumerable<LKFLine> LKFLines
        {
            get
            {
                var kalines = GetKALines();
                foreach (var l in kalines.PrefixLKFLines)
                    yield return l;
                foreach (var l in AufenthaltLKFLines)
                    yield return l;
                foreach (var l in kalines.SuffixLKFLines)
                {
                    yield return l;
                }
            }
        }

        public IEnumerable<String> Lines => LKFLines.Select(l => l.Line);

        public void WriteDataFile (string outputPath)
        {
            var sw = new StreamWriter(outputPath);
            foreach (var l in Lines)
            {
                sw.WriteLine(l);
            }
            sw.Close();
        }

        public void LoadFile (string fileName)
        {
            _contextWithData.LoadFile(fileName);
        }

        public void AddBuffer (byte[] byteBuffer)
        {
            _contextWithData.AddBuffer(byteBuffer);
        }

        public uint AufenthaltCount
        {
            get
            {
                return _contextWithData.AufenthaltCount;
            }
        }

        public Aufenthalt GetAufenthalt (uint i)
        {
            var aufenthalt = _contextWithData.GetAufenthalt(i);
            return new Aufenthalt(aufenthalt);
        }

        public Aufenthalt AddCopy (Aufenthalt aufenthalt)
        {
            return aufenthalt.CopyTo(this);            
        }

        public IEnumerable<Aufenthalt> Aufenthalte
        {
            get
            {
                var count = _contextWithData.AufenthaltCount;
                for (uint i = 0; i < count; i++)
                {
                    yield return GetAufenthalt(i);
                }
            }
        }

        public void PlausCheck ()
        {
            _contextWithData.PlausCheck();
        }

        public void Scoring ()
        {
            Scoring(true, true);
        }

        public int PlausCheck(out string error)
        {
            return _contextWithData.PlausCheck (out error);
        }

        public int Scoring(out string error)
        {
            return _contextWithData.Scoring(out error);
        }

        public void Scoring (bool intScoring, bool stat)
        {
            _contextWithData.Scoring();
            if (intScoring)
            {
                var intContext = _contextWithData.IntScoring();
                IntContext = new ic.IntContext(this, intContext);
            }
            else
            {
                IntContext = null;
            }

            if (stat)
            {
                var statContext = _contextWithData.Stat();
                StatContext = new stat.StatContext(this, statContext);
            }
            else
            {
                StatContext = null;
            }
        }

        public LKFContextWithData CD
        {
            get
            {
                return _contextWithData;
            }
        }

        public KA.Lines GetKALines()
        {
            return new KA.Lines(this);
        }

        public errors.ContextErrorObjectList GetErrors (errors.ContextErrorFlags flags, errors.ContextErrorField[] fields) => new errors.ContextErrorObjectList(this, flags, fields);
        public errors.ContextErrorObjectList GetErrors(errors.ContextErrorFlags flags, int[] fields) => new errors.ContextErrorObjectList(this, flags, fields);
        public errors.ContextErrorObjectList GetErrors(errors.ContextErrorFlags flags) => new errors.ContextErrorObjectList(this, flags, (int[])null);

        public errors.ContextErrorStringList GetErrorStrings(errors.ContextErrorFlags flags, errors.ContextErrorField[] fields) => new errors.ContextErrorStringList(this, flags, fields);
        public errors.ContextErrorStringList GetErrorStrings(errors.ContextErrorFlags flags, int[] fields) => new errors.ContextErrorStringList(this, flags, fields);
        public errors.ContextErrorObjectList GetErrorStrings(errors.ContextErrorFlags flags) => new errors.ContextErrorObjectList(this, flags, (int[]) null);

        public void Dispose()
        {
            _contextWithData.Dispose();
            _contextWithData = null;
            _context = null;
        }

        public uint ErrorCount =>_contextWithData.ErrorCount;

        public uint WarningCount => _contextWithData.WarningCount;

        public uint InfoCount => _contextWithData.InfoCount;

        public uint AcceptedErrorCount => _contextWithData.AcceptedErrorCount;

        public uint AcceptedWarningCount => _contextWithData.AcceptedWarningCount;

        public uint AcceptedInfoCount => _contextWithData.AcceptedInfoCount;

    }

    public class ContextForStat : ContextWithData
    {
        public ContextForStat() : base(new LKFContextForStat())
        {
        }
    }
}
