using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Xml;
using System.Linq;
using System.Collections.Generic;
using DataContracts;
using System.Text.RegularExpressions;

namespace Ato.EN.IntegrationServices.CodeGenerationRPTTAXPOS
{
    public partial class RPTTAXPOS2019Validator
    {
        /// <summary>
        /// The max parameter name length is restricted by the SBR1 and SBR2 schemas - the lowest common denominator of SBR 1 is used as the default
        /// </summary>
        private int _maxParameterNameLength;

        /// <summary>
        /// The max parameter value length is restricted by the SBR1 and SBR2 schemas
        /// </summary>
        private int _maxParameterValueLength;

        /// <summary>
        /// The SBR1 and ebms3 schemas do not allow parameter names or values to be the empty string
        /// </summary>
        private string _emptyParameterValue;

        /// <summary>
        /// Initializes a new instance of the <see cref="RPTTAXPOS2019Validator" /> class.
        /// </summary>
        /// <param name="maxParameterNameLength">Maximum length of the parameter name.</param>
        /// <param name="maxParameterValueLength">Maximum length of the parameter value.</param>
        /// <param name="emptyParameterValue">This value will be used in place of any parameter values that result in a null or empty value.</param>
        public RPTTAXPOS2019Validator(int maxParameterNameLength = 20, int maxParameterValueLength = 4096, string emptyParameterValue = "EMPTY")
        {
            _maxParameterNameLength = maxParameterNameLength;
            _maxParameterValueLength = maxParameterValueLength;
            _emptyParameterValue = emptyParameterValue;
        }

        #region Functions
        private static IEnumerable<string> Union(IEnumerable<string> list1, IEnumerable<string> list2)
        {
            IEnumerable<string> response;

            if (list1 == null && list2 == null)
            {
                response = null;
            }
            else if (list1 == null)
            {
                response = list2.Distinct();
            }
            else if (list2 == null)
            {
                response = list1.Distinct();
            }
            else
            {
                response = list1.Union(list2);
            }

            return response;
        }

        private static bool IsMatch(int? field, string expression, RegexOptions options = RegexOptions.None)
        {
            if (field == null)
                return false;
            else
                return Regex.IsMatch(Convert.ToString(field.Value), expression, options);
        }

        private static bool IsMatch(string field, string expression, RegexOptions options = RegexOptions.None)
        {
            if (field == null)
                return false;
            else
                return Regex.IsMatch(field, expression, options);
        }

        // This is just context and tuple counts where they are integer values - easier that changing the parsing logic to just return the value
        private static int Count(int count)
        {
            return count;
        }


        private static int Count<T>(IEnumerable<T> values)
        {
            return values == null ? 0 : values.Where(f => f != null).Count();
        }


        private static int Count<T>(ICollection<T> values)
        {
            return values == null ? 0 : values.Where(f => f != null).Count();
        }


        private static bool exists(bool value)
        {
            return value;
        }

        private static string GetValueOrEmpty(bool? val)
        {
            return (val.HasValue) ? val.ToString().ToLower() : string.Empty;
        }

        private static string GetValueOrEmpty(DateTime? val)
        {
            return (val.HasValue) ? val.ToString() : string.Empty;
        }

        private static string GetValueOrEmpty(string val)
        {
            return !string.IsNullOrWhiteSpace(val) ? val : string.Empty;
        }

        private static string GetValueOrEmpty(decimal? val)
        {
            return (val.HasValue) ? val.ToString() : string.Empty;
        }

        private static string GetValueOrEmpty(int? val)
        {
            return (val.HasValue) ? val.ToString() : string.Empty;
        }

         /// <summary>
         /// Get string value between [first] a and [last] b.
         /// </summary>
        public static string Between(string value, string a, string b)
        {
            int posA = value.IndexOf(a);
            int posB = value.LastIndexOf(b);
            if (posA == -1)
            {
                return "";
            }
            if (posB == -1)
            {
                return "";
            }
            int adjustedPosA = posA + a.Length;
            if (adjustedPosA >= posB)
            {
                return "";
            }
            return value.Substring(adjustedPosA, posB - adjustedPosA);
        }

         /// <summary>
         /// Get string value after [first] a.
         /// </summary>
        public static string Before(string value, string a)
        {
            int posA = value.IndexOf(a);
            if (posA == -1)
            {
                return "";
            }
            return value.Substring(0, posA);
        }

         /// <summary>
         /// Get string value after [last] a.
         /// </summary>
        public static string After(string value, string a)
        {
            int posA = value.LastIndexOf(a);
            if (posA == -1)
            {
                return "";
            }
            int adjustedPosA = posA + a.Length;
            if (adjustedPosA >= value.Length)
            {
                return "";
            }
            return value.Substring(adjustedPosA);
        }

        private static int Length(object field)
        {
            if (field == null)
            return 0;
            else
            return field.ToString().Trim().Length;
        }

        private static bool NotSameValues(IEnumerable<object> nodes)
        {
            if (nodes == null)
                return false;

            object[] nodesArray = nodes.Cast<object>().ToArray();
            return NotSameValues(nodesArray);
        }

        private static bool NotSameValues(params object[] nodes)
        {
            if (nodes == null)
                return false;

            return ((from x in nodes select x).Distinct().Count() == nodes.Count());
        }

        private static bool HasDuplicateValues(IEnumerable<object> nodes)
        {
            if (nodes == null)
                return false;

            object[] nodesArray = nodes.Cast<object>().ToArray();
            return HasDuplicateValues(nodesArray);
        }

        private static bool HasDuplicateValues(params object[] nodes)
        {
           if (nodes == null)
                return false;

            nodes = nodes.Where(x => x != null).ToArray();
            return !((from x in nodes select x).Distinct().Count() == nodes.Count());
        
        }

        private int DuplicateValueIndex(IEnumerable<object> values)
        {
            int response = 0;
            var hashset = new HashSet<object>();
            foreach (var value in values)
            {
                if (!hashset.Add(value))
                {
                    return response;
                }
                response++;
            }
            return response;
        }

        private int DuplicateValueIndex<T>(IEnumerable<T?> values) where T : struct
        {
            int response = 0;
            var hashset = new HashSet<T?>();
            foreach (var value in values)
            {
                if (!hashset.Add(value))
                {
                    return response;
                }
                response++;
            }
            return response;
        }

        private static bool IsDate(object value)
        {
            DateTime dateValue;
            return (value != null && DateTime.TryParse(value.ToString(), out dateValue));
        }

        public DateTime AsDate(string dateAsString)
        {
            DateTime response = DateTime.MinValue;
            DateTime date;

            if (DateTime.TryParse(dateAsString, out date))
            {
                response = date;
            }

            return response;
        }

        public DateTime Date(DateTime? datetime)
        {                   
            return datetime.GetValueOrDefault().Date;
        }

				//The logic in After function expects "---" for day and "--" for month. 
				//Since hyphen was missing the date always returned null
        public DateTime? ConvertToDate(int day, int month, int year)
        {
					return ConvertToDate(day == 0 ? null : "---" + day.ToString(), month == 0 ? null : "--" + month.ToString(), year == 0 ? null : year.ToString());
        }

        public DateTime? ConvertToDate(string day, string month, string year)
        {
            DateTime? response;
            DateTime result;

            if (year == null || month == null || day == null)
            {
                return null;
            }
            string dateAsString = year + "-" + After(month, "--") + "-" + After(day, "---");
            if (DateTime.TryParse(dateAsString, out result))
            {
                response = result;
            }
            else
            {
                response = null;
            }
            return response;
        }


        public DateTime? ConvertToDate(string day, string month, int year)
        {
            DateTime? response;
            DateTime result;

            if (year == 0 || month == null || day == null)
            {
                return null;
            }
            string dateAsString = year.ToString() + "-" + After(month, "--") + "-" + After(day, "---");
            if (DateTime.TryParse(dateAsString, out result))
            {
                response = result;
            }
            else
            {
                response = null;
            }
            return response;
        }


        private static int Day(string dateAsString)
        {
            int response = 0;
            DateTime date;

            if (DateTime.TryParse(dateAsString, out date))
            {
                response = date.Day;
            }

            return response;
        }


        private static int Day(DateTime? date)
        {
            if (date == null)
                return 0;
            else
                return date.Value.Day;

        }


        private static string Month(string dateAsString)
        {
            string response = null;
            DateTime date;

            if (DateTime.TryParse(dateAsString, out date))
            {
                response = date.ToString("MMMM");
            }
            else
            {
                return "NotAMonth";
            }

            return response;
        }

        private static string Month(DateTime? date)
        {
            if (date == null)
                return "NotAMonth";
            else
                return date.Value.ToString("MMMM");
        }


        private static int MonthAsInt(string dateAsString)
        {
            int response = 0;
            DateTime date;

            if (DateTime.TryParse(dateAsString, out date))
            {
                response = date.Month;
            }

            return response;
        }

        private static int MonthAsInt(DateTime? date)
        {
            if (date == null)
            {
                return 0;
            }
            return date.Value.Month;
        }


        private static int Year(string dateAsString)
        {
            int response = 0;
            DateTime date;

            if (DateTime.TryParse(dateAsString, out date))
            {
                response = date.Year;
            }

            return response;
        }

        private static int Year(DateTime? date)
        {
            if (date == null)
                return 0;
            else
                return date.Value.Year;
        }


        private static int CurrentFinancialYear()
        {
            return DateToFinancialYear(DateTime.Now, 7);
        }

        private static int FinancialYear(string dateAsString)
        {
            return DateToFinancialYear(dateAsString, 7);
        }

        private static int FinancialYear(DateTime? date)
        {
            return DateToFinancialYear(date, 7);
        }

        private static int DateToFinancialYear(string dateAsString, int startingMonth)
        {
            int response = 0;
            DateTime date;
            if (DateTime.TryParse(dateAsString, out date))
            {
                response = DateToFinancialYear(date, startingMonth);
            }

            return response;
        }
        private static int DateToFinancialYear(DateTime? date, int startingMonth)
        {
            int response;
            if (date == null)
            {
                response = 0;
            }
            else
            {
                int year = date.Value.Year;
                int month = date.Value.Month;

                if (startingMonth > month)
                    response = year;
                else
                    response = year + 1;
            }
            return response;
        }


        private static int FBTYear(string dateAsString)
        {
            int response = 0;
            DateTime date;

            if (DateTime.TryParse(dateAsString, out date))
                response = FBTYear(date);
            return response;
        }

        private static int FBTYear(DateTime? date)
        {
            if (date == null)
            {
                return 0;
            }
            else
            {
                if (date.Value.Month > 3)
                    return date.Value.Year + 1;
                else
                    return date.Value.Year;
            }
        }

        private static DateTime? AddMonthsToDate(DateTime? dateTime, int months)
        {
            return dateTime == null ? null : (DateTime?)dateTime.Value.AddMonths(months);
        }

        private static bool IsNumeric(object value)
        {
            decimal numbervalue;
            return (value != null && decimal.TryParse(value.ToString(), out numbervalue));
        }

        private static bool NotMonetary(decimal? field, string sign, int digits, int decimals)
        {
            if (field == null)
            {
                return false;
            }
            else
            {
                string signExpression;
                string decimalExpression;
                int digitsToUse = digits - decimals;

                if (sign == "U")
                    signExpression = "^";
                else
                    signExpression = "^-?";

                if (decimals > 0)
                    decimalExpression = @"(\.\d{1," + decimals + "})?$";
                else
                    decimalExpression = @"$";

                return !(Regex.IsMatch(field.Value.ToString("0.#########################"), signExpression + @"\d{1," + digitsToUse + "}" + decimalExpression));
            }
        }

        private static bool NotNumeric(decimal? field, string sign, int digits, int decimals)
        {
            if (field == null)
            {
                return false;
            }
            else
            {
                string signExpression;
                string decimalExpression;
                int digitsToUse = digits - decimals;

                if (sign == "U")
                    signExpression = "^";
                else
                    signExpression = "^-?";

                if (decimals > 0)
                    decimalExpression = @"(\.\d{1," + decimals + "})?$";
                else
                    decimalExpression = @"$";

                return !(Regex.IsMatch(field.Value.ToString("0.#########################"), signExpression + @"\d{1," + digitsToUse + "}" + decimalExpression));
            }
        }

        private static bool NotNumeric(int? field, string sign, int digits, int decimals = 0)
        {
            if (field == null)
            {
                return false;
            }
            else
            {
                string signExpression;

                if (sign == "U")
                    signExpression = "^";
                else
                    signExpression = "^-?";

                return !(Regex.IsMatch(field.Value.ToString(), signExpression + @"\d{1," + digits + "}$"));
            }
        }

        private static bool NotNumeric(long? field, string sign, int digits, int decimals = 0)
        {
            if (field == null)
            {
                return false;
            }
            else
            {
                string signExpression;

                if (sign == "U")
                    signExpression = "^";
                else
                    signExpression = "^-?";

                return !(Regex.IsMatch(field.Value.ToString(), signExpression + @"\d{1," + digits + "}$"));
            }
        }


        private static bool OutsideRange(decimal field, decimal expression, int range)
        {
            bool response;

            response = (field < (expression - range)) || (field > (expression + range));

            return response;
        }


        private static bool FailsUSIAlgorithm(string usi, string abn)
        {
            bool response = true;
            if (usi == null || abn == null)
            {
                response = false;
            }
            else
            {
                usi = usi.Trim();
                abn = abn.Trim();
                if (usi.Length == 14)
                {
                    int numeric;
                    if (usi.Substring(0, 11) == abn && int.TryParse(usi.Substring(11, 3), out numeric))
                        response = false;

                }
                else if (usi.Length == 9)
                {

                    if (Regex.IsMatch(usi, @"^([a-zA-Z]{3}\d{4}[a-zA-Z]{2})$"))
                        response = false;
                }
            }
            return response;
        }

        private static bool FailsTANAlgorithm(string tan)
        {
            bool response;
            decimal decimalTan;

            if (tan == null)
                return false;

            tan = tan.Trim();

            if (!decimal.TryParse(tan, out decimalTan))
                return true;

            if (tan.Length != 8)
                return true;

            decimal tanSum =
                7 * int.Parse(tan.Substring(0, 1)) +
                9 * int.Parse(tan.Substring(1, 1)) +
                8 * int.Parse(tan.Substring(2, 1)) +
                4 * int.Parse(tan.Substring(3, 1)) +
                6 * int.Parse(tan.Substring(4, 1)) +
                3 * int.Parse(tan.Substring(5, 1)) +
                5 * int.Parse(tan.Substring(6, 1)) +
                1 * int.Parse(tan.Substring(7, 1));

            if ((tanSum % 11) == 0)
                response = false;
            else
                response = true;

            return response;
        }


        private static bool FailsABNAlgorithm(string abn)
        {
            bool response;
            decimal decimalAbn;

            if (abn == null)
                return false;

            abn = abn.Trim();

            if (!decimal.TryParse(abn, out decimalAbn))
                return true;

            if (abn.Length != 11)
                return true;

            decimal abnSum =
                10 * (int.Parse(abn.Substring(0, 1)) - 1) +
                1 * int.Parse(abn.Substring(1, 1)) +
                3 * int.Parse(abn.Substring(2, 1)) +
                5 * int.Parse(abn.Substring(3, 1)) +
                7 * int.Parse(abn.Substring(4, 1)) +
                9 * int.Parse(abn.Substring(5, 1)) +
                11 * int.Parse(abn.Substring(6, 1)) +
                13 * int.Parse(abn.Substring(7, 1)) +
                15 * int.Parse(abn.Substring(8, 1)) +
                17 * int.Parse(abn.Substring(9, 1)) +
                19 * int.Parse(abn.Substring(10, 1));

            if ((abnSum % 89) == 0)
                response = false;
            else
                response = true;

            return response;
        }
        private static bool FailsACNAlgorithm(string acn)
        {
            bool response;
            decimal decimalAbn;
            if (acn == null)
                return false;

            acn = acn.Trim();

            if (!decimal.TryParse(acn, out decimalAbn))
                return true;

            if (acn.Length != 9)
                return true;

            decimal abnSum =
                8 * int.Parse(acn.Substring(0, 1)) +
                7 * int.Parse(acn.Substring(1, 1)) +
                6 * int.Parse(acn.Substring(2, 1)) +
                5 * int.Parse(acn.Substring(3, 1)) +
                4 * int.Parse(acn.Substring(4, 1)) +
                3 * int.Parse(acn.Substring(5, 1)) +
                2 * int.Parse(acn.Substring(6, 1)) +
                1 * int.Parse(acn.Substring(7, 1));

            decimal checkDigit = int.Parse(acn.Substring(8, 1));
            decimal acnRemainder = abnSum % 10;

            if (((10 - acnRemainder) % 10) == checkDigit)
                response = false;
            else
                response = true;

            return response;
        }


        private static bool FailsTFNAlgorithm(string tfn)
        {
            bool response;
            decimal decimalTfn;

            if (tfn == null)
                return false;

            tfn = tfn.Trim();
            tfn = Regex.Replace(tfn, "^0+", "");

            if (!decimal.TryParse(tfn, out decimalTfn))
                return true;

            if (tfn.Length < 8)
                return true;


            decimal tfn1To7Sum =
                1 * int.Parse(tfn.Substring(0, 1)) +
                4 * int.Parse(tfn.Substring(1, 1)) +
                3 * int.Parse(tfn.Substring(2, 1)) +
                7 * int.Parse(tfn.Substring(3, 1)) +
                5 * int.Parse(tfn.Substring(4, 1)) +
                8 * int.Parse(tfn.Substring(5, 1)) +
                6 * int.Parse(tfn.Substring(6, 1));

            decimal tfn8 = 9 * int.Parse(tfn.Substring(7, 1));

            if (tfn.Length == 8)
            {
                decimal tFNLg8WSum9 = 10 * int.Parse(tfn.Substring(7, 1));
                decimal tFNLg8WSum = tfn1To7Sum + tFNLg8WSum9;

                if ((tFNLg8WSum % 11) == 0)
                    response = false;
                else
                    response = true;
            }
            else if (tfn.Length == 9)
            {
                decimal tfn9 = 10 * int.Parse(tfn.Substring(8, 1));
                decimal tFNLg9WSum = tfn1To7Sum + tfn8 + tfn9;

                if ((tFNLg9WSum % 11) == 0)
                    response = false;
                else
                    response = true;
            }
            else
            {
                response = true;
            }

            return response;
        }


        private static decimal ConditionalValue(bool expression, decimal? trueVal, decimal? falseVal)
        {
             return expression ? trueVal.GetValueOrDefault() : falseVal.GetValueOrDefault();
        }

        private static decimal AsNumeric(string value)
        {
             decimal numberValue;
             decimal.TryParse(value, out numberValue);
             return numberValue;
        }

        private static bool RegexMatch(int? field, string expression, string flags="")
        {
            return IsMatch(field, expression, GetRegexOption(flags));
        }

        private static bool RegexMatch(string field, string expression, string flags="")
        {
            return IsMatch(field, expression, GetRegexOption(flags));
        }

        private static RegexOptions GetRegexOption(string flags)
        {
            RegexOptions options = RegexOptions.None;

            char[] characters = flags.ToCharArray();

            foreach (char character in characters)
            {
                switch (character)
                {
                    case 'i':                        
                        options = options | RegexOptions.IgnoreCase;
                        break;
                    case 'm':                        
                        options = options | RegexOptions.Multiline;
                        break;
                    case 's':                        
                            options = options | RegexOptions.Singleline;
                        break;
                    case 'n':                        
                            options = options | RegexOptions.ExplicitCapture;
                        break;
                    case 'x':                       
                            options = options | RegexOptions.IgnorePatternWhitespace;
                        break;
                }
            }

            return options;
        }

        /// <summary>
        /// Returns an occurrence index as [occurrenceIndex] of occurrenceIndex > 0, otherwise the empty string
        /// </summary>
        /// <param name="occurrenceIndex">Index of the occurrence.</param>
        /// <returns>Occurrence in XPath [#] format</returns>
        public string OccurrenceIndex(int occurrenceIndex)
        {
            return occurrenceIndex > 0 ? "[" + occurrenceIndex + "]" : "";
        }

        #endregion // Functions

        public RPTTAXPOS2019 ConsumedReport { get; private set; }

        private static Dictionary<string, ProcessMessageDocument> _processMessageDocuments = new Dictionary<string,ProcessMessageDocument>();

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public List<ProcessMessageDocument> ValidateReport(RPTTAXPOS2019 report)
        {
            List<ProcessMessageDocument> response = new List<ProcessMessageDocument>();
            ProcessMessageDocument processMessage;
            ProcessMessageParameter parameter;
            bool assertion;

            this.ConsumedReport = report;
    
            #region VR.ATO.GEN.001021
    
            /*  VR.ATO.GEN.001021
            Entity Identifier Scheme is not set to http://www.ato.gov.au/tfn
    
            Legacy Rule Format:
            IF Identifier Scheme <> “http://www.ato.gov.au/tfn”
               RETURN VALIDATION MESSAGE
            ENDIF

            Technical Business Rule Format:
            (^Context <> NULL) AND (^IdentifierScheme <> 'http://www.ato.gov.au/tfn')
    
            Data Elements:
    
            ^IdentifierScheme = RP
    
            ^Context = RP
            */
            assertion = (report.RPExists != false && report.RPIdentifierScheme != @"http://www.ato.gov.au/tfn");
            if (assertion)
            {
                processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.GEN.001021", Severity = ProcessMessageSeverity.Error,
                    Description = @"Invalid identifier scheme.",
                    LongDescription = @"Invalid identifier scheme.  Indentifier scheme must be set to ""http://www.ato.gov.au/tfn""",
                    Location = "/xbrli:xbrl/xbrli:context" + OccurrenceIndex(report.RPOccurrenceIndex) + "/xbrli:entity/xbrli:identifier/@scheme",
                    Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.GEN.001021"} },
                };
                response.Add(processMessage);
            }
            #endregion // VR.ATO.GEN.001021
    
            #region VR.ATO.RPTTAXPOS.000001
    
            /*  VR.ATO.RPTTAXPOS.000001
            Mandatory field not supplied.
    
            Legacy Rule Format:
            IF [RPTTAXPOS2] = NULL
               RETURN VALIDATION MESSAGE
            ENDIF
            
            [RPTTAXPOS2] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Indicator

            Technical Business Rule Format:
            ^RPTTAXPOS2 = NULL
    
            Data Elements:
    
            ^RPTTAXPOS2 = RP:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Indicator
            */
            assertion = (report.RPTTAXPOS2 == null);
            if (assertion)
            {
                processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.RPTTAXPOS.000001", Severity = ProcessMessageSeverity.Error,
                    Description = @"Mandatory field not supplied.",
                    LongDescription = @"Please disclose whether you had any Category A or B Reportable tax positions.",
                    Location = "/xbrli:xbrl/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Indicator",
                    Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000001"} },
                };
                processMessage.Parameters.Add(new ProcessMessageParameter
                    { Name = "RPTTAXPOS2", Value = GetValueOrEmpty(report.RPTTAXPOS2) });
    
                response.Add(processMessage);
            }
            #endregion // VR.ATO.RPTTAXPOS.000001
    
            #region VR.ATO.RPTTAXPOS.000002
    
            /*  VR.ATO.RPTTAXPOS.000002
            Number of category A or B Reportable tax positions must be supplied if answer to 'Did you have any Category A or B Reportable tax positions for the income year you are reporting for?' is Yes.
    
            Legacy Rule Format:
            IF [RPTTAXPOS2] = TRUE AND [RPTTAXPOS3] = NULL
               RETURN VALIDATION MESSAGE
            ENDIF
            
            [RPTTAXPOS2] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Indicator
            [RPTTAXPOS3] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Count

            Technical Business Rule Format:
            ^RPTTAXPOS2 = TRUE AND ^RPTTAXPOS3 = NULL
    
            Data Elements:
    
            ^RPTTAXPOS3 = RP:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Count
    
            ^RPTTAXPOS2 = RP:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Indicator
            */
            assertion = (report.RPTTAXPOS2 == true && report.RPTTAXPOS3 == null);
            if (assertion)
            {
                processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.RPTTAXPOS.000002", Severity = ProcessMessageSeverity.Error,
                    Description = @"Number of Category A or B Reportable tax positions must be supplied.",
                    LongDescription = @"Please disclose the number of Category A or B Reportable tax positions you have. If you do not have any Category A or B reportable tax positions, please indicate FALSE to 'Did you have any Category A or B Reportable tax positions for the income year you are reporting for?'.",
                    Location = "/xbrli:xbrl/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Count",
                    Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000002"} },
                };
                processMessage.Parameters.Add(new ProcessMessageParameter
                    { Name = "RPTTAXPOS2", Value = GetValueOrEmpty(report.RPTTAXPOS2) });
    
                processMessage.Parameters.Add(new ProcessMessageParameter
                    { Name = "RPTTAXPOS3", Value = report.RPTTAXPOS3.GetValueOrDefault().ToString() });
    
                response.Add(processMessage);
            }
            #endregion // VR.ATO.RPTTAXPOS.000002
    
            #region VR.ATO.RPTTAXPOS.000003
    
            /*  VR.ATO.RPTTAXPOS.000003
            Number of Category A or B Reportable tax positions must not exceed 20.
    
            Legacy Rule Format:
            IF [RPTTAXPOS3] > 20
               RETURN VALIDATION MESSAGE
            ENDIF
            
            [RPTTAXPOS3] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Count

            Technical Business Rule Format:
            ^RPTTAXPOS3 > 20
    
            Data Elements:
    
            ^RPTTAXPOS3 = RP:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Count
            */
            assertion = (report.RPTTAXPOS3.GetValueOrDefault() > 20);
            if (assertion)
            {
                processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.RPTTAXPOS.000003", Severity = ProcessMessageSeverity.Error,
                    Description = @"Number of Category A or B Reportable tax positions must not exceed 20.",
                    LongDescription = @"The RTP schedule allows you to disclose a maximum of 20 Category A or B reportable tax positions. If you have more than 20 Category A or B positions please consider whether the positions are similar and should therefore be grouped, and whether they are all material. If you still have more than 20 reportable tax positions, please disclose 20 of the most material positions and email the additional RTP Category, basis for position and concise description for the other positions to the tax office (see RTP schedule instructions for further guidance).",
                    Location = "/xbrli:xbrl/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Count",
                    Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000003"} },
                };
                processMessage.Parameters.Add(new ProcessMessageParameter
                    { Name = "RPTTAXPOS3", Value = report.RPTTAXPOS3.GetValueOrDefault().ToString() });
    
                response.Add(processMessage);
            }
            #endregion // VR.ATO.RPTTAXPOS.000003
    
            #region VR.ATO.RPTTAXPOS.000005
    
            /*  VR.ATO.RPTTAXPOS.000005
            Reportable Tax positions Category A or B supplied must equal the number of Category A or B Reportable tax positions.
    
            Legacy Rule Format:
            IF [RPTTAXPOS3] <> NULL AND (COUNT(TUPLE(rpttaxpos.0001.lodge.req.xx.xx:ReportableTaxPositionCategoryAAndB)) = 0 OR COUNT(TUPLE(rpttaxpos.0001.lodge.req.xx.xx:ReportableTaxPositionCategoryAAndB)) > [RPTTAXPOS3])
               RETURN VALIDATION MESSAGE
            ENDIF
            
            [RPTTAXPOS3] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Count

            Technical Business Rule Format:
            ^RPTTAXPOS3 <> NULL AND (Count(^ReportableTaxPositionCategoryAAndB) = 0 OR Count(^ReportableTaxPositionCategoryAAndB) > ^RPTTAXPOS3)
    
            Data Elements:
    
            ^RPTTAXPOS3 = RP:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Count
    
            ^ReportableTaxPositionCategoryAAndB = RP:ReportableTaxPositionCategoryAAndB
            */
            assertion = (report.RPTTAXPOS3 != null && (Count(report.ReportableTaxPositionCategoryAAndBCollectionCount) == 0 || Count(report.ReportableTaxPositionCategoryAAndBCollectionCount) > report.RPTTAXPOS3.GetValueOrDefault()));
            if (assertion)
            {
                processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.RPTTAXPOS.000005", Severity = ProcessMessageSeverity.Error,
                    Description = @"Reportable Tax positions Category A or B supplied must equal the number of Category A or B Reportable tax positions.",
                    Location = "/xbrli:xbrl/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryAB.Count",
                    Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000005"} },
                };
                processMessage.Parameters.Add(new ProcessMessageParameter
                    { Name = "RPTTAXPOS3", Value = report.RPTTAXPOS3.GetValueOrDefault().ToString() });
    
                response.Add(processMessage);
            }
            #endregion // VR.ATO.RPTTAXPOS.000005
    
            #region VR.ATO.RPTTAXPOS.000009
    
            /*  VR.ATO.RPTTAXPOS.000009
            Mandatory field not supplied.
    
            Legacy Rule Format:
            IF [RPTTAXPOS9] = NULL
               RETURN VALIDATION MESSAGE
            ENDIF
            
            [RPTTAXPOS9] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Indicator

            Technical Business Rule Format:
            ^RPTTAXPOS9 = NULL
    
            Data Elements:
    
            ^RPTTAXPOS9 = RP:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Indicator
            */
            assertion = (report.RPTTAXPOS9 == null);
            if (assertion)
            {
                processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.RPTTAXPOS.000009", Severity = ProcessMessageSeverity.Error,
                    Description = @"Mandatory field not supplied.",
                    LongDescription = @"Please disclose whether you had any Category C Reportable tax positions.",
                    Location = "/xbrli:xbrl/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Indicator",
                    Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000009"} },
                };
                processMessage.Parameters.Add(new ProcessMessageParameter
                    { Name = "RPTTAXPOS9", Value = GetValueOrEmpty(report.RPTTAXPOS9) });
    
                response.Add(processMessage);
            }
            #endregion // VR.ATO.RPTTAXPOS.000009
    
            #region VR.ATO.RPTTAXPOS.000010
    
            /*  VR.ATO.RPTTAXPOS.000010
            Reportable Tax positions Category C supplied must equal the number of Category C Reportable tax positions.
    
            Legacy Rule Format:
            IF [RPTTAXPOS10] <> NULL AND (COUNT(TUPLE(rpttaxpos.0001.lodge.req.xx.xx:ReportableTaxPositionCategoryC)) = 0 OR COUNT(TUPLE(rpttaxpos.0001.lodge.req.xx.xx:ReportableTaxPositionCategoryC)) > [RPTTAXPOS10])
               RETURN VALIDATION MESSAGE
            ENDIF
            
            [RPTTAXPOS10] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Count

            Technical Business Rule Format:
            ^RPTTAXPOS10 <> NULL AND (Count(^ReportableTaxPositionCategoryC) = 0 OR Count(^ReportableTaxPositionCategoryC) > ^RPTTAXPOS10)
    
            Data Elements:
    
            ^RPTTAXPOS10 = RP:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Count
    
            ^ReportableTaxPositionCategoryC = RP:ReportableTaxPositionCategoryC
            */
            assertion = (report.RPTTAXPOS10 != null && (Count(report.ReportableTaxPositionCategoryCCollectionCount) == 0 || Count(report.ReportableTaxPositionCategoryCCollectionCount) > report.RPTTAXPOS10.GetValueOrDefault()));
            if (assertion)
            {
                processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.RPTTAXPOS.000010", Severity = ProcessMessageSeverity.Error,
                    Description = @"Reportable Tax positions Category C supplied must equal the number of Category C Reportable tax positions.",
                    Location = "/xbrli:xbrl/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Count",
                    Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000010"} },
                };
                processMessage.Parameters.Add(new ProcessMessageParameter
                    { Name = "RPTTAXPOS10", Value = report.RPTTAXPOS10.GetValueOrDefault().ToString() });
    
                response.Add(processMessage);
            }
            #endregion // VR.ATO.RPTTAXPOS.000010
    
            #region VR.ATO.RPTTAXPOS.000011
    
            /*  VR.ATO.RPTTAXPOS.000011
            Number of category C Reportable tax positions must be supplied if answer to 'Did you have any Category C Reportable tax positions for the income year you are reporting for?' is Yes.
    
            Legacy Rule Format:
            IF [RPTTAXPOS9] = TRUE AND [RPTTAXPOS10] = NULL
               RETURN VALIDATION MESSAGE
            ENDIF
            
            [RPTTAXPOS9] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Indicator
            [RPTTAXPOS10] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Count

            Technical Business Rule Format:
            ^RPTTAXPOS9 = TRUE AND ^RPTTAXPOS10 = NULL
    
            Data Elements:
    
            ^RPTTAXPOS10 = RP:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Count
    
            ^RPTTAXPOS9 = RP:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Indicator
            */
            assertion = (report.RPTTAXPOS9 == true && report.RPTTAXPOS10 == null);
            if (assertion)
            {
                processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.RPTTAXPOS.000011", Severity = ProcessMessageSeverity.Error,
                    Description = @"Number of category C Reportable tax positions must be supplied.",
                    LongDescription = @"Please disclose the number of Category C Reportable tax positions you have. If you do not have any Category C reportable tax positions, please indicate FALSE to 'Did you have any Category C Reportable tax positions for the income year you are reporting for?'.",
                    Location = "/xbrli:xbrl/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Count",
                    Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000011"} },
                };
                processMessage.Parameters.Add(new ProcessMessageParameter
                    { Name = "RPTTAXPOS9", Value = GetValueOrEmpty(report.RPTTAXPOS9) });
    
                processMessage.Parameters.Add(new ProcessMessageParameter
                    { Name = "RPTTAXPOS10", Value = report.RPTTAXPOS10.GetValueOrDefault().ToString() });
    
                response.Add(processMessage);
            }
            #endregion // VR.ATO.RPTTAXPOS.000011
    
            #region VR.ATO.RPTTAXPOS.000012
    
            /*  VR.ATO.RPTTAXPOS.000012
            Number of Category C Reportable tax positions must not exceed 100.
    
            Legacy Rule Format:
            IF [RPTTAXPOS10] > 100
               RETURN VALIDATION MESSAGE
            ENDIF
            
            [RPTTAXPOS10] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Count

            Technical Business Rule Format:
            ^RPTTAXPOS10 > 100
    
            Data Elements:
    
            ^RPTTAXPOS10 = RP:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Count
            */
            assertion = (report.RPTTAXPOS10.GetValueOrDefault() > 100);
            if (assertion)
            {
                processMessage = new ProcessMessageDocument()
                {
                    Code = "CMN.ATO.RPTTAXPOS.000012", Severity = ProcessMessageSeverity.Error,
                    Description = @"Number of Category C Reportable tax positions must not exceed 100.",
                    LongDescription = @"The RTP schedule allows you to disclose a maximum of 100 Category C reportable tax positions. If you have more than 100 Category A or B positions please consider whether the positions are similar and should therefore be grouped. If you still have more than 100 reportable tax positions please disclose 100 of the most material positions and email the additional RTP Category, Category C question number and subcategory as well as any optional comments to the tax office (see RTP schedule instructions for further guidance).",
                    Location = "/xbrli:xbrl/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryC.Count",
                    Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000012"} },
                };
                processMessage.Parameters.Add(new ProcessMessageParameter
                    { Name = "RPTTAXPOS10", Value = report.RPTTAXPOS10.GetValueOrDefault().ToString() });
    
                response.Add(processMessage);
            }
            #endregion // VR.ATO.RPTTAXPOS.000012

            #region Repeating report.ReportableTaxPositionCategoryAAndBCollection
            if (report.ReportableTaxPositionCategoryAAndBCollection != null)
            {    
                int itemIndex1 = 0;
                foreach (RPTTAXPOS2019.ReportableTaxPositionCategoryAAndB reportableTaxPositionCategoryAAndB in report.ReportableTaxPositionCategoryAAndBCollection)
                {
                    itemIndex1++;
            
                    #region VR.ATO.RPTTAXPOS.000004
            
                    /*  VR.ATO.RPTTAXPOS.000004
                    Number of Category A or B Reportable tax positions must follow the format '20xx-xxx'.
    
                    Legacy Rule Format:
                    WHERE IN TUPLE(rpttaxpos.0001.lodge.req.xx.xx:ReportableTaxPositionCategoryAAndB)
                    IF RegexMatch([RPTTAXPOS4],'20[0-9]{2}-[0-9]{3}$','i') = FALSE
                      RETURN VALIDATION MESSAGE
                    ENDIF
                    
                    [RPTTAXPOS4] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryReferenceNumber.Identifier IN TUPLE(rpttaxpos.0001.lodge.req.02.00:ReportableTaxPositionCategoryAAndB)
        
                    Technical Business Rule Format:
                    RegexMatch(^RPTTAXPOS4,'20[0-9]{2}-[0-9]{3}$','i') = FALSE
            
                    Data Elements:
            
                    ^RPTTAXPOS4 = RP:ReportableTaxPositionCategoryAAndB:RegulatoryDisclosures.ReportableTaxPositionCategoryReferenceNumber.Identifier
                    */
                    assertion = (RegexMatch(reportableTaxPositionCategoryAAndB.RPTTAXPOS4, @"20[0-9]{2}-[0-9]{3}$", @"i") == false);
                    if (assertion)
                    {
                        processMessage = new ProcessMessageDocument()
                        {
                            Code = "CMN.ATO.RPTTAXPOS.000004", Severity = ProcessMessageSeverity.Error,
                            Description = @"Number of Category A or B Reportable tax positions must follow the format '20xx-xxx'.",
                            Location = "/xbrli:xbrl/tns:ReportableTaxPositionCategoryAAndB" + OccurrenceIndex(reportableTaxPositionCategoryAAndB.OccurrenceIndex) + "/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryReferenceNumber.Identifier",
                            Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000004"} },
                        };
                        processMessage.Parameters.Add(new ProcessMessageParameter
                            { Name = "RPTTAXPOS4", Value = reportableTaxPositionCategoryAAndB.RPTTAXPOS4 });
            
                        response.Add(processMessage);
                    }
                    #endregion // VR.ATO.RPTTAXPOS.000004
            
                    #region VR.ATO.RPTTAXPOS.000007
            
                    /*  VR.ATO.RPTTAXPOS.000007
                    Concise description must be provided if answer to 'Did you have any Category A or B Reportable tax positions for the income year you are reporting for?' is Yes.
    
                    Legacy Rule Format:
                    WHERE IN TUPLE(rpttaxpos.0001.lodge.req.xx.xx:ReportableTaxPositionCategoryAAndB)
                    IF [RPTTAXPOS7] = BLANK
                       RETURN VALIDATION MESSAGE
                    ENDIF
                    
                    [RPTTAXPOS7] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryDescription.Text IN TUPLE(rpttaxpos.0001.lodge.req.02.00:ReportableTaxPositionCategoryAAndB)
        
                    Technical Business Rule Format:
                    ^RPTTAXPOS7 = BLANK
            
                    Data Elements:
            
                    ^RPTTAXPOS7 = RP:ReportableTaxPositionCategoryAAndB:RegulatoryDisclosures.ReportableTaxPositionCategoryDescription.Text
                    */
                    assertion = (string.IsNullOrWhiteSpace(reportableTaxPositionCategoryAAndB.RPTTAXPOS7) == true);
                    if (assertion)
                    {
                        processMessage = new ProcessMessageDocument()
                        {
                            Code = "CMN.ATO.RPTTAXPOS.000007", Severity = ProcessMessageSeverity.Error,
                            Description = @"Concise description must be provided.",
                            LongDescription = @"Concise description must be provided if you indicated TRUE to 'Did you have any Category A or B Reportable tax positions for the income year you are reporting for?'.",
                            Location = "/xbrli:xbrl/tns:ReportableTaxPositionCategoryAAndB" + OccurrenceIndex(reportableTaxPositionCategoryAAndB.OccurrenceIndex) + "/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryDescription.Text",
                            Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000007"} },
                        };
                        processMessage.Parameters.Add(new ProcessMessageParameter
                            { Name = "RPTTAXPOS7", Value = reportableTaxPositionCategoryAAndB.RPTTAXPOS7 });
            
                        response.Add(processMessage);
                    }
                    #endregion // VR.ATO.RPTTAXPOS.000007
            
                    #region VR.ATO.RPTTAXPOS.000008
            
                    /*  VR.ATO.RPTTAXPOS.000008
                    Basis for position must be provided if answer to 'Did you have any Category A or B Reportable tax positions for the income year you are reporting for?' is Yes.
    
                    Legacy Rule Format:
                    WHERE IN TUPLE(rpttaxpos.0001.lodge.req.xx.xx:ReportableTaxPositionCategoryAAndB)
                    IF [RPTTAXPOS8] = BLANK
                       RETURN VALIDATION MESSAGE
                    ENDIF
                    
                    [RPTTAXPOS8] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionBasis.Text IN TUPLE(rpttaxpos.0001.lodge.req.02.00:ReportableTaxPositionCategoryAAndB)
        
                    Technical Business Rule Format:
                    ^RPTTAXPOS8 = BLANK
            
                    Data Elements:
            
                    ^RPTTAXPOS8 = RP:ReportableTaxPositionCategoryAAndB:RegulatoryDisclosures.ReportableTaxPositionBasis.Text
                    */
                    assertion = (string.IsNullOrWhiteSpace(reportableTaxPositionCategoryAAndB.RPTTAXPOS8) == true);
                    if (assertion)
                    {
                        processMessage = new ProcessMessageDocument()
                        {
                            Code = "CMN.ATO.RPTTAXPOS.000008", Severity = ProcessMessageSeverity.Error,
                            Description = @"Basis for position must be provided.",
                            LongDescription = @"Basis for position must be provided if you indicated TRUE to 'Did you have any Category A or B Reportable tax positions for the income year you are reporting for?'.",
                            Location = "/xbrli:xbrl/tns:ReportableTaxPositionCategoryAAndB" + OccurrenceIndex(reportableTaxPositionCategoryAAndB.OccurrenceIndex) + "/tns:RegulatoryDisclosures.ReportableTaxPositionBasis.Text",
                            Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000008"} },
                        };
                        processMessage.Parameters.Add(new ProcessMessageParameter
                            { Name = "RPTTAXPOS8", Value = reportableTaxPositionCategoryAAndB.RPTTAXPOS8 });
            
                        response.Add(processMessage);
                    }
                    #endregion // VR.ATO.RPTTAXPOS.000008
            
                    #region VR.ATO.RPTTAXPOS.000017
            
                    /*  VR.ATO.RPTTAXPOS.000017
                    A character must be one of the following: A to Z a to z 0 to 9 @ $ % & * ( ) - ; : ' " , . ? / or a space character.
    
                    Legacy Rule Format:
                    WHERE IN TUPLE(rpttaxpos.0001.lodge.req.xx.xx:ReportableTaxPositionCategoryAAndB)
                    IF ([RPTTAXPOS7] <> NULLORBLANK) AND (ANY CHARACTER OF [RPTTAXPOS7] <> SET("a-z","A-Z","0-9","@","$","%","&","*","(",")","-",";",":","'",""",",",".","?","/"," "))
                       RETURN VALIDATION MESSAGE
                    ENDIF
                    
                    [RPTTAXPOS7] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryDescription.Text IN TUPLE(rpttaxpos.0001.lodge.req.02.00:ReportableTaxPositionCategoryAAndB)
        
                    Technical Business Rule Format:
                    (^RPTTAXPOS7 <> BLANK) AND (NotCharacterInSet(^RPTTAXPOS7, '"a-z","A-Z","0-9","@","$","%","&","*","(",")","-",";",":","'",""",",",".","?","/"," "'))
            
                    Data Elements:
            
                    ^RPTTAXPOS7 = RP:ReportableTaxPositionCategoryAAndB:RegulatoryDisclosures.ReportableTaxPositionCategoryDescription.Text
                    */
                    assertion = (string.IsNullOrWhiteSpace(reportableTaxPositionCategoryAAndB.RPTTAXPOS7) != true && !(IsMatch(reportableTaxPositionCategoryAAndB.RPTTAXPOS7, @"^[a-zA-Z0-9@\$%&\*\(\)\-;:'"",\.\?/ ]*$",RegexOptions.IgnoreCase)));
                    if (assertion)
                    {
                        processMessage = new ProcessMessageDocument()
                        {
                            Code = "CMN.ATO.RPTTAXPOS.000017", Severity = ProcessMessageSeverity.Error,
                            Description = @"Concise description contains invalid character.",
                            LongDescription = @"Concise description can only contain the following characters: A to Z a to z 0 to 9 ! @ $ % & * ( ) - ; : ' "" , . ? / or a space character.",
                            Location = "/xbrli:xbrl/tns:ReportableTaxPositionCategoryAAndB" + OccurrenceIndex(reportableTaxPositionCategoryAAndB.OccurrenceIndex) + "/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryDescription.Text",
                            Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000017"} },
                        };
                        processMessage.Parameters.Add(new ProcessMessageParameter
                            { Name = "RPTTAXPOS7", Value = reportableTaxPositionCategoryAAndB.RPTTAXPOS7 });
            
                        response.Add(processMessage);
                    }
                    #endregion // VR.ATO.RPTTAXPOS.000017
            
                    #region VR.ATO.RPTTAXPOS.000018
            
                    /*  VR.ATO.RPTTAXPOS.000018
                    A character must be one of the following: A to Z a to z 0 to 9 @ $ % & * ( ) - ; : ' " , . ? / or a space character.
    
                    Legacy Rule Format:
                    WHERE IN TUPLE(rpttaxpos.0001.lodge.req.xx.xx:ReportableTaxPositionCategoryAAndB)
                    IF ([RPTTAXPOS8] <> NULLORBLANK) AND (ANY CHARACTER OF [RPTTAXPOS8] <> SET("a-z","A-Z","0-9","@","$","%","&","*","(",")","-",";",":","'",""",",",".","?","/"," "))
                       RETURN VALIDATION MESSAGE
                    ENDIF
                    
                    [RPTTAXPOS8] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionBasis.Text IN TUPLE(rpttaxpos.0001.lodge.req.02.00:ReportableTaxPositionCategoryAAndB)
        
                    Technical Business Rule Format:
                    (^RPTTAXPOS8 <> BLANK) AND (NotCharacterInSet(^RPTTAXPOS8, '"a-z","A-Z","0-9","@","$","%","&","*","(",")","-",";",":","'",""",",",".","?","/"," "'))
            
                    Data Elements:
            
                    ^RPTTAXPOS8 = RP:ReportableTaxPositionCategoryAAndB:RegulatoryDisclosures.ReportableTaxPositionBasis.Text
                    */
                    assertion = (string.IsNullOrWhiteSpace(reportableTaxPositionCategoryAAndB.RPTTAXPOS8) != true && !(IsMatch(reportableTaxPositionCategoryAAndB.RPTTAXPOS8, @"^[a-zA-Z0-9@\$%&\*\(\)\-;:'"",\.\?/ ]*$",RegexOptions.IgnoreCase)));
                    if (assertion)
                    {
                        processMessage = new ProcessMessageDocument()
                        {
                            Code = "CMN.ATO.RPTTAXPOS.000018", Severity = ProcessMessageSeverity.Error,
                            Description = @"Basis for position contains invalid character.",
                            LongDescription = @"Basis for position can only contain the following characters: A to Z a to z 0 to 9 ! @ $ % & * ( ) - ; : ' "" , . ? / or a space character.",
                            Location = "/xbrli:xbrl/tns:ReportableTaxPositionCategoryAAndB" + OccurrenceIndex(reportableTaxPositionCategoryAAndB.OccurrenceIndex) + "/tns:RegulatoryDisclosures.ReportableTaxPositionBasis.Text",
                            Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000018"} },
                        };
                        processMessage.Parameters.Add(new ProcessMessageParameter
                            { Name = "RPTTAXPOS8", Value = reportableTaxPositionCategoryAAndB.RPTTAXPOS8 });
            
                        response.Add(processMessage);
                    }
                    #endregion // VR.ATO.RPTTAXPOS.000018
                }
            }
        
            #endregion // Foreach loop

            #region Repeating report.ReportableTaxPositionCategoryCCollection
            if (report.ReportableTaxPositionCategoryCCollection != null)
            {    
                int itemIndex1 = 0;
                foreach (RPTTAXPOS2019.ReportableTaxPositionCategoryC reportableTaxPositionCategoryC in report.ReportableTaxPositionCategoryCCollection)
                {
                    itemIndex1++;
            
                    #region VR.ATO.RPTTAXPOS.000013
            
                    /*  VR.ATO.RPTTAXPOS.000013
                    Number of Category C Reportable tax positions must follow the format '20xx-xxx'.
    
                    Legacy Rule Format:
                    WHERE IN TUPLE(rpttaxpos.0001.lodge.req.xx.xx:ReportableTaxPositionCategoryC)
                    IF RegexMatch([RPTTAXPOS11],'20[0-9]{2}-[0-9]{3}$','i') = FALSE
                      RETURN VALIDATION MESSAGE
                    ENDIF
                    
                    [RPTTAXPOS11] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryReferenceNumber.Identifier IN TUPLE(rpttaxpos.0001.lodge.req.02.00:ReportableTaxPositionCategoryC)
        
                    Technical Business Rule Format:
                    RegexMatch(^RPTTAXPOS11,'20[0-9]{2}-[0-9]{3}$','i') = FALSE
            
                    Data Elements:
            
                    ^RPTTAXPOS11 = RP:ReportableTaxPositionCategoryC:RegulatoryDisclosures.ReportableTaxPositionCategoryReferenceNumber.Identifier
                    */
                    assertion = (RegexMatch(reportableTaxPositionCategoryC.RPTTAXPOS11, @"20[0-9]{2}-[0-9]{3}$", @"i") == false);
                    if (assertion)
                    {
                        processMessage = new ProcessMessageDocument()
                        {
                            Code = "CMN.ATO.RPTTAXPOS.000013", Severity = ProcessMessageSeverity.Error,
                            Description = @"Number of Category C Reportable tax positions must follow the format '20xx-xxx'.",
                            Location = "/xbrli:xbrl/tns:ReportableTaxPositionCategoryC" + OccurrenceIndex(reportableTaxPositionCategoryC.OccurrenceIndex) + "/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryReferenceNumber.Identifier",
                            Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000013"} },
                        };
                        processMessage.Parameters.Add(new ProcessMessageParameter
                            { Name = "RPTTAXPOS11", Value = reportableTaxPositionCategoryC.RPTTAXPOS11 });
            
                        response.Add(processMessage);
                    }
                    #endregion // VR.ATO.RPTTAXPOS.000013
            
                    #region VR.ATO.RPTTAXPOS.000016
            
                    /*  VR.ATO.RPTTAXPOS.000016
                    A character must be one of the following: A to Z a to z 0 to 9 ! @ $ % & * ( ) - ; : ' " , . ? / or a space character.
    
                    Legacy Rule Format:
                    WHERE IN TUPLE(rpttaxpos.0001.lodge.req.xx.xx:ReportableTaxPositionCategoryC )
                    IF ([RPTTAXPOS15] <> NULLORBLANK) AND (ANY CHARACTER OF [RPTTAXPOS15] <> SET("a-z","A-Z","0-9","@","$","%","&","*","(",")","-",";",":","'",""",",",".","?","/"," "))
                       RETURN VALIDATION MESSAGE
                    ENDIF
                    
                    [RPTTAXPOS15] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionAdditionalInformation.Text IN TUPLE(rpttaxpos.0001.lodge.req.02.00:ReportableTaxPositionCategoryC)
        
                    Technical Business Rule Format:
                    (^RPTTAXPOS15 <> BLANK) AND (NotCharacterInSet(^RPTTAXPOS15, '"a-z","A-Z","0-9","@","$","%","&","*","(",")","-",";",":","'",""",",",".","?","/"," "'))
            
                    Data Elements:
            
                    ^RPTTAXPOS15 = RP:ReportableTaxPositionCategoryC:RegulatoryDisclosures.ReportableTaxPositionAdditionalInformation.Text
                    */
                    assertion = (string.IsNullOrWhiteSpace(reportableTaxPositionCategoryC.RPTTAXPOS15) != true && !(IsMatch(reportableTaxPositionCategoryC.RPTTAXPOS15, @"^[a-zA-Z0-9@\$%&\*\(\)\-;:'"",\.\?/ ]*$",RegexOptions.IgnoreCase)));
                    if (assertion)
                    {
                        processMessage = new ProcessMessageDocument()
                        {
                            Code = "CMN.ATO.RPTTAXPOS.000016", Severity = ProcessMessageSeverity.Error,
                            Description = @"Optional comments contains invalid character.",
                            LongDescription = @"Optional comments can only contain the following characters: A to Z a to z 0 to 9 ! @ $ % & * ( ) - ; : ' "" , . ? / or a space character.",
                            Location = "/xbrli:xbrl/tns:ReportableTaxPositionCategoryC" + OccurrenceIndex(reportableTaxPositionCategoryC.OccurrenceIndex) + "/tns:RegulatoryDisclosures.ReportableTaxPositionAdditionalInformation.Text",
                            Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000016"} },
                        };
                        processMessage.Parameters.Add(new ProcessMessageParameter
                            { Name = "RPTTAXPOS15", Value = GetValueOrEmpty(reportableTaxPositionCategoryC.RPTTAXPOS15) });
            
                        response.Add(processMessage);
                    }
                    #endregion // VR.ATO.RPTTAXPOS.000016
            
                    #region VR.ATO.RPTTAXPOS.000019
            
                    /*  VR.ATO.RPTTAXPOS.000019
                    RTP Category C question contains invalid number.
    
                    Legacy Rule Format:
                    WHERE IN TUPLE(rpttaxpos.0001.lodge.req.xx.xx:ReportableTaxPositionCategoryC)
                    IF [RPTTAXPOS13] <> SET(1-50)
                       RETURN VALIDATION MESSAGE
                    ENDIF
                    
                    [RPTTAXPOS13] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryCQuestion.Number IN TUPLE(rpttaxpos.0001.lodge.req.02.00:ReportableTaxPositionCategoryC)
        
                    Technical Business Rule Format:
                    ^RPTTAXPOS13 < 1 OR ^RPTTAXPOS13 > 50
            
                    Data Elements:
            
                    ^RPTTAXPOS13 = RP:ReportableTaxPositionCategoryC:RegulatoryDisclosures.ReportableTaxPositionCategoryCQuestion.Number
                    */
                    assertion = (reportableTaxPositionCategoryC.RPTTAXPOS13.GetValueOrDefault() < 1 || reportableTaxPositionCategoryC.RPTTAXPOS13.GetValueOrDefault() > 50);
                    if (assertion)
                    {
                        processMessage = new ProcessMessageDocument()
                        {
                            Code = "CMN.ATO.RPTTAXPOS.000019", Severity = ProcessMessageSeverity.Error,
                            Description = @"Please provide a valid RTP Category C question number.",
                            Location = "/xbrli:xbrl/tns:ReportableTaxPositionCategoryC" + OccurrenceIndex(reportableTaxPositionCategoryC.OccurrenceIndex) + "/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryCQuestion.Number",
                            Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000019"} },
                        };
                        processMessage.Parameters.Add(new ProcessMessageParameter
                            { Name = "RPTTAXPOS13", Value = reportableTaxPositionCategoryC.RPTTAXPOS13.GetValueOrDefault().ToString() });
            
                        response.Add(processMessage);
                    }
                    #endregion // VR.ATO.RPTTAXPOS.000019
            
                    #region VR.ATO.RPTTAXPOS.000020
            
                    /*  VR.ATO.RPTTAXPOS.000020
                    RTP Category C subcategory question contains invalid number.
    
                    Legacy Rule Format:
                    WHERE IN TUPLE(rpttaxpos.0001.lodge.req.xx.xx:ReportableTaxPositionCategoryC)
                    IF [RPTTAXPOS14] <> NULL AND [RPTTAXPOS14] <> SET(1-99)
                       RETURN VALIDATION MESSAGE
                    ENDIF
                    
                    [RPTTAXPOS14] = RPTTAXPOS:RP:bafot.02.36:RegulatoryDisclosures.ReportableTaxPositionCategoryCSubcategory.Number IN TUPLE(rpttaxpos.0001.lodge.req.02.00:ReportableTaxPositionCategoryC)
        
                    Technical Business Rule Format:
                    ^RPTTAXPOS14 <> NULL AND (^RPTTAXPOS14 < 1 OR ^RPTTAXPOS14 > 99)
            
                    Data Elements:
            
                    ^RPTTAXPOS14 = RP:ReportableTaxPositionCategoryC:RegulatoryDisclosures.ReportableTaxPositionCategoryCSubcategory.Number
                    */
                    assertion = (reportableTaxPositionCategoryC.RPTTAXPOS14 != null && (reportableTaxPositionCategoryC.RPTTAXPOS14.GetValueOrDefault() < 1 || reportableTaxPositionCategoryC.RPTTAXPOS14.GetValueOrDefault() > 99));
                    if (assertion)
                    {
                        processMessage = new ProcessMessageDocument()
                        {
                            Code = "CMN.ATO.RPTTAXPOS.000020", Severity = ProcessMessageSeverity.Error,
                            Description = @"Please provide a valid RTP Category C subcategory number.",
                            Location = "/xbrli:xbrl/tns:ReportableTaxPositionCategoryC" + OccurrenceIndex(reportableTaxPositionCategoryC.OccurrenceIndex) + "/tns:RegulatoryDisclosures.ReportableTaxPositionCategoryCSubcategory.Number",
                            Parameters = new ProcessMessageParameters() { new ProcessMessageParameter() {Name = "RuleIdentifier", Value = "VR.ATO.RPTTAXPOS.000020"} },
                        };
                        processMessage.Parameters.Add(new ProcessMessageParameter
                            { Name = "RPTTAXPOS14", Value = reportableTaxPositionCategoryC.RPTTAXPOS14.GetValueOrDefault().ToString() });
            
                        response.Add(processMessage);
                    }
                    #endregion // VR.ATO.RPTTAXPOS.000020
                    }
                }

            #endregion // Foreach loop

            #region Manual Rules
        
            VRATORPTTAXPOS000006(response, report);
           
            VRATORPTTAXPOS000015(response, report);

            #endregion 

            foreach (ProcessMessageDocument currentProcessMessage in response)
            {
                if (currentProcessMessage.Parameters != null)
                {
                    foreach (ProcessMessageParameter currentParameter in currentProcessMessage.Parameters)
                    {
                        if (string.IsNullOrEmpty(currentParameter.Name))
                        {
                            currentParameter.Name = _emptyParameterValue;
                        }

                        if (currentParameter.Name.Length > _maxParameterNameLength)
                        {
                            currentParameter.Name = currentParameter.Name.Substring(0, _maxParameterNameLength - 1);
                        }

                        if (string.IsNullOrEmpty(currentParameter.Value))
                        {
                            currentParameter.Value = _emptyParameterValue;
                        }

                        if (currentParameter.Value.Length > _maxParameterValueLength)
                        {
                            currentParameter.Value = currentParameter.Value.Substring(0, _maxParameterValueLength - 1);
                        }
                    }
                }
            }

            return response;
        }
    }
} 
