AusPostCode/Barcode.cs

123 lines
3.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace AusPostCode {
public class Barcode {
public enum BarcodeFragment {
FormatControlCode,
SortingCode,
CustomerInformation,
ErrorCorrection,
}
public enum EncodingFormat {
N,
C,
BarToDecimal,
}
private int _formatCode;
public Dictionary<int, string> FormatTable = new Dictionary<int, string> {
[0] = "Null Customer Barcode",
[11] = "Standard Customer Barcode",
[52] = "Customer Business Reply Paid",
[59] = "Customer Barcode 2",
[62] = "Customer Barcode 3",
[67] = "Customer Business Reply Paid",
[72] = "International Business Reply Paid",
[77] = "International Business Reply Paid",
};
public Barcode(string code) {
Code = code;
// - check for start and end bars
if (Code.Substring(0, 2) != "13") {
Warnings.Add("Couldn't find Start Bars");
Code = $"13{Code}";
}
if (Code.Substring(Code.Length - 3, 2) != "13") {
Warnings.Add("Couldn't find End Bars");
Code = $"{Code}13";
}
// process Format Control Code
_formatCode = int.Parse(Decode(GetFragment(BarcodeFragment.FormatControlCode), EncodingFormat.N));
Console.WriteLine(Format);
// process Sorting Code Field
SortingCode = int.Parse(Decode(GetFragment(BarcodeFragment.SortingCode), EncodingFormat.N));
Console.WriteLine($"Sorting code: {SortingCode}");
}
public int SortingCode { get; set; }
public int CustomerInformation { get; set; }
public string Format => FormatTable.ContainsKey(_formatCode) ? FormatTable[_formatCode] : $"Unknown ({_formatCode})";
private List<string> Warnings { get; set; } = new List<string>();
private string Code { get; set; }
private string GetFragment(BarcodeFragment barcodeFragment) {
return barcodeFragment switch {
BarcodeFragment.FormatControlCode => Code.Substring(2, 4),
BarcodeFragment.SortingCode => Code.Substring(6, 16),
BarcodeFragment.CustomerInformation when _formatCode == 59 => Code.Substring(22, 16),
BarcodeFragment.CustomerInformation when _formatCode == 62 => Code.Substring(22, 31),
BarcodeFragment.CustomerInformation => null, // format doesn't support the customer information field
BarcodeFragment.ErrorCorrection => Code.Substring(Code.Length - 15,
12), // the error correction bars are always immediately before the stop bars
_ => throw new ArgumentOutOfRangeException(nameof(barcodeFragment), barcodeFragment, null),
};
}
private static string Decode(string input, EncodingFormat format) {
// bool badData;
var chunkLength = format == EncodingFormat.N ? 2 : 3;
if (input.Length % 2 != 0) {
throw new ArgumentException($"Input length must be a multiple of {chunkLength}.", nameof(input));
}
var rx = new Regex("^[0123]+$");
if (!rx.IsMatch(input)) {
throw new ArgumentException("Input length must be a quaternary number.", nameof(input));
}
var sb = new StringBuilder();
for (var i = 0; i < input.Length; i += chunkLength) {
var chunk = input.Substring(i, chunkLength);
switch (format) {
case EncodingFormat.N:
// format N supports the digits 0 through 9, and nothing else.
// digits 0 through 8 are stored as their ternary representations, while 9 is stored as "30".
if (chunk == "30") {
sb.Append(9);
}
else if (chunk.Contains("3")) {
// not a ternary number
throw new ArgumentException($"{chunk} is not a valid identifier for format {format.ToString()}.");
}
else {
sb.Append(BaseConversion.FromBase(chunk, 3));
}
break;
case EncodingFormat.C:
break;
case EncodingFormat.BarToDecimal:
break;
default:
throw new ArgumentOutOfRangeException(nameof(format), format, null);
}
}
return sb.ToString();
}
}
}