﻿using System;
using System.Collections.Generic;
using System.Text;

namespace IndianHealthService.BMXNet
{
    /// <summary>
    /// Common M[umps] related function libraries implemented with static members.
    /// </summary>    
    public static class M
    {

        #region Piece Functions

        /// <summary>
        /// M-like utility method to answer the number of pieces limited by aDelimiter. Corresponds to M's $L(STRING,DELIMITER)
        /// </summary>
        /// <param name="thePieces">Delimited input string e.g. one^two^three</param>
        /// <param name="aDelimiter">Delimiter character or string, commonly ~ ^ or |</param>
        /// <returns>Count of pieces in thePieces delimited by aDelimiter</returns>
        /// <example>
        /// <code>
        ///     String team="DOE,JOHN^SMITH, PETER^JONES, SALLY";
        ///     if (M.PieceLength(team,"~") >=5 ) {
        ///         this.PlayBasketBall();
        ///     } 
        ///     else 
        ///     {
        ///         this.PlayTrackAndField();
        ///     }
        /// </code>
        /// </example>
        public static int PieceLength(string thePieces, string aDelimiter)
        {
            char[] cDelim = aDelimiter.ToCharArray();
            string[] cSplit = thePieces.Split(cDelim);
            return cSplit.GetLength(0);
        }


        /// <summary>
        /// M-like utility method to answer the piece at a 1-based offset.  Corresponds to M's $$Piece function
        /// </summary>
        /// <param name="thePieces">Delimited input string e.g. one^two^three</param>
        /// <param name="aDelimiter">Delimiter character or string, commonly ~ ^ or |</param>
        /// <param name="nthPiece">The 1-based index of the piece</param>
        /// <returns>The piece at the index, or an empty string if the index is higher than the explicit number of pieces</returns>
        /// <example>
        /// <code>
        ///     String demographics="DOE,JOHN~555-55-5555~34~M";
        ///     int age=0;
        ///     if (int.TryParse(M.Piece(demographics,"~",3), out age)) {
        ///         System.Console.Writeln(M.Piece(demographics,"~",1)+" is "+ (age >= 18 ) ? "an adult" : "a child");
        ///     }
        /// </code>
        /// </example>
        public static string Piece(string thePieces, string aDelimiter, int nthPiece)
        {
            try
            {
                char[] cDelim = aDelimiter.ToCharArray();
                string[] cSplit = thePieces.Split(cDelim);
                int nLength = cSplit.GetLength(0);
                return ((nLength < nthPiece) || (nthPiece < 1)) ? "" : cSplit[nthPiece - 1];
            }
            catch (Exception bmxEx)
            {
                string sMessage = bmxEx.Message + bmxEx.StackTrace;
                throw new BMXNetException(sMessage);
            }

        }

        /// <summary>
        /// M-like utility method to answer a delimited string of pieces between two 1-based offsets
        /// </summary>
        /// <param name="thePieces">Delimited input string e.g. one^two^three</param>
        /// <param name="aDelimiter">Delimiter character or string, commonly ~ ^ or |</param>
        /// <param name="nthStart">Starting 1-based index of the pieces to copy from</param>
        /// <param name="mthEnd">Ending 1-based index of the pieces to copy to</param>
        /// <returns>The delimited string from start to end, including delimitered blanks for indexes higher than the explicit number of pieces</returns>     
        public static string Piece(string thePieces, string aDelimiter, int nthStart, int mthEnd)
        {
            try
            {
                if (nthStart < 0)
                    nthStart = 1;

                if (mthEnd< nthStart)
                    return "";

                if (mthEnd == nthStart)
                    return Piece(thePieces, aDelimiter, nthStart);

                char[] cDelim = aDelimiter.ToCharArray();
                string[] cSplit = thePieces.Split(cDelim);
                int nLength = cSplit.GetLength(0);
                if ((nLength < nthStart) || (nthStart < 1))
                    return "";

                //nNumber = 1-based index of the starting element to return
                //nLength = count of elements
                //nEnd = 1-based index of last element to return
                //nCount = number of elements past nNumber to return

                //convert nNumber to 0-based index:
                nthStart--;

                //convert nEnd to 0-based index;
                mthEnd--;

                //Calculate nCount;
                int nCount = mthEnd - nthStart + 1;

                //Adjust nCount for number of elements
                if (nCount + nthStart >= nLength)
                {
                    nCount = nLength - nthStart;
                }

                string sRet = string.Join(aDelimiter, cSplit, nthStart, nCount);
                return sRet;
            }
            catch (Exception bmxEx)
            {
                string sMessage = bmxEx.Message + bmxEx.StackTrace;
                throw new BMXNetException(sMessage);
            }
        }

        #endregion Piece Functions

        /// <summary>
        /// M-like utility method to convert from a DateTime to the FileMan data/time format
        /// </summary>
        /// <param name="aDateTime">DateTime to convert</param>
        /// <returns>String representing aDateTime in FileMan format</returns>
        /// <exception cref="Exception">Exception thrown in conversion fails</exception>
        public static string DateToFileMan(DateTime aDateTime)
        {
            int yearsSince1700 = (int)aDateTime.Year - 1700;
            TimeSpan timeOfDay = aDateTime.TimeOfDay;

            if (timeOfDay.TotalSeconds == 0)
            {
                return yearsSince1700.ToString("000") + aDateTime.ToString("MMdd");
            }
            else
            {
                return yearsSince1700.ToString("000") + ((timeOfDay.Seconds == 0) ? aDateTime.ToString("MMdd.HHmm") : aDateTime.ToString("MMdd.HHmmss"));
            }
        }


        /// <summary>
        /// M-like utility method to convert from a FileMan date/time formatted String to a .NET DateTime
        /// </summary>
        /// <param name="aString">date/time formated FileMan string</param>
        /// <returns>.NET DateTime object from parsing input FileMan string</returns>
        /// <exception cref="Exception">Exception thrown in conversion fails</exception>  
        public static DateTime DateFromFileMan(string aString)
        {
            String[] peices = aString.Split(new char[] { '.' });

            if (peices.Length == 1)
            {
                return new DateTime(
                        int.Parse(peices[0].Substring(0, 3)) + 1700,
                        int.Parse(peices[0].Substring(3, 2)),
                        int.Parse(peices[0].Substring(5, 2)));
            }
            else if (peices.Length >= 2)
            {
                String timePiece = peices[1].PadRight(6, '0');

                return new DateTime(
                   int.Parse(peices[0].Substring(0, 3)) + 1700,
                   int.Parse(peices[0].Substring(3, 2)),
                   int.Parse(peices[0].Substring(5, 2)),
                   int.Parse(timePiece.Substring(0, 2)),
                   int.Parse(timePiece.Substring(2, 2)),
                   int.Parse(timePiece.Substring(4, 2)));
            }
            else
            {
                throw new Exception("Invalid FileMan DateTime");
            }
        }

    }
}
