// #define USE_MASKED_INPUT
using System;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
namespace PhoneNumberValidation
{
public class ValidatingEventArgs : EventArgs
{
public bool IsValid { get; set; }
public string Message { get; set; }
}
interface IValidatedInput
{
string Input { get; }
bool IsValid { get; }
event EventHandler<ValidatingEventArgs> Validating;
string ReadInput(string prompt);
}
class ValidatedInput : IValidatedInput
{
public string Input { get; protected set; }
public bool IsValid { get; protected set; }
public event EventHandler<ValidatingEventArgs> Validating;
public virtual string ReadInput(string prompt)
{
while (true)
{
Console.Write(prompt);
Input = Console.ReadLine();
if (IsInputValid(out string message))
return Input;
Console.WriteLine(message);
}
}
protected virtual bool IsInputValid(out string message)
{
var args = new ValidatingEventArgs();
args.IsValid = true;
args.Message = String.Empty;
OnValidating(args);
message = args.Message;
IsValid = args.IsValid;
return args.IsValid;
}
protected virtual void OnValidating(ValidatingEventArgs args) => Validating?.Invoke(this, args);
}
#if USE_MASKED_INPUT
class ConsoleState : IDisposable
{
public int CursorLeft { get; set; }
public int CursorTop { get; set; }
public ConsoleColor ForegroundColor { get; set; }
public ConsoleColor BackgroundColor { get; set; }
public ConsoleState()
{
Capture();
}
public void Capture()
{
CursorLeft = Console.CursorLeft;
CursorTop = Console.CursorTop;
ForegroundColor = Console.ForegroundColor;
BackgroundColor = Console.BackgroundColor;
}
public void Restore()
{
Console.CursorLeft = CursorLeft;
Console.CursorTop = CursorTop;
Console.ForegroundColor = ForegroundColor;
Console.BackgroundColor = BackgroundColor;
}
public void Dispose()
{
Restore();
}
}
class MaskedInput : ValidatedInput
{
public string Mask { get; }
public MaskedInput(string mask)
{
Mask = mask;
}
public override string ReadInput(string prompt)
{
using (var consoleState = new ConsoleState())
{
do
{
consoleState.Restore();
DrawPrompt(prompt);
DrawMask();
ReadInputInternal();
} while (!IsInputValid(out string message));
consoleState.Capture();
}
return Input;
}
void DrawPrompt(string prompt) => Console.Write(prompt);
void DrawMask()
{
using (var consoleState = new ConsoleState())
{
Console.BackgroundColor = ConsoleColor.Black;
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.Write(Mask);
consoleState.Restore();
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.Write(Input);
}
}
void ReadInputInternal()
{
//$ TODO: Implement event for each keyed input to allow live validation.
//$ TODO: Implement accepting only keys that match the mask.
//$ TODO: Implement pressing escape.
//$ TODO: Implement backspacing and arrowing.
//$ TODO: Implement insert and overwride.
Input = Console.ReadLine();
}
}
#endif // USE_MASKED_INPUT
class Program
{
static void Main(string[] args)
{
#if USE_MASKED_INPUT
var validatedInput = new MaskedInput("999 999 9999");
#else
var validatedInput = new ValidatedInput();
#endif
//
// Uncomment one of the validation options below:
//
// validatedInput.Validating += OnValidating_Regex;
// validatedInput.Validating += OnValidating_Linq;
validatedInput.Validating += OnValidating_StringOps;
var phoneNumber = validatedInput.ReadInput("Enter mobile number: ");
if (validatedInput.IsValid)
Console.WriteLine("{0} is {1}.", phoneNumber, validatedInput.IsValid ? "valid" : "invalid");
}
static void OnValidating_Regex(object obj, ValidatingEventArgs args)
{
IValidatedInput validatedInput = (IValidatedInput) obj;
args.IsValid = Regex.Match(validatedInput.Input, @"11[123](-|\s)*[0-9]{3}(-|\s)*[0-9]{4}").Success;
args.Message = args.IsValid ? "" : "Must match expected pattern.";
}
static string[] ValidPrefixes = new string[] { "111", "112", "113" };
static void OnValidating_Linq(object obj, ValidatingEventArgs args)
{
IValidatedInput validatedInput = (IValidatedInput)obj;
var input = validatedInput.Input;
var charArray = input.ToCharArray();
var digits = charArray.Where(ch => char.IsDigit(ch)).ToList();
args.IsValid = false;
args.Message = "Must contain 10 digits.";
if (digits.Count != 10)
return;
args.Message = "Must consist of only digits, whitespace, and dashes.";
if (!input.ToCharArray().All(ch => char.IsDigit(ch) || char.IsWhiteSpace(ch) || ch == '-'))
return;
args.Message = "Prefix must be one of " + string.Join(", ", ValidPrefixes);
var prefix = digits.Take(3).Aggregate("", (s, ch) => s += ch);
if (!ValidPrefixes.Contains(prefix))
return;
args.Message = "";
args.IsValid = true;
}
static void OnValidating_StringOps(object obj, ValidatingEventArgs args)
{
IValidatedInput validatedInput = (IValidatedInput)obj;
var input = validatedInput.Input;
string digits = "";
foreach (char ch in input)
{
if (char.IsDigit(ch))
{
digits += ch;
}
else if (char.IsWhiteSpace(ch) || ch == '-')
{
// do nothing
}
else
{
args.IsValid = false;
args.Message = "Must consist of only digits, whitespace, and dashes.";
return;
}
}
if (digits.Length != 10)
{
args.IsValid = false;
args.Message = "Must contain 10 digits.";
return;
}
var prefix = digits.Substring(0, 3);
if (!ValidPrefixes.Contains(prefix))
{
args.IsValid = false;
args.Message = "Prefix must be one of " + string.Join(", ", ValidPrefixes);
}
}
}
}