186 lines
5.8 KiB
C#
186 lines
5.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace AusPostCode {
|
|
public class Barcode {
|
|
private readonly struct BarcodeFragment {
|
|
public EncodingFormat TypicalFormat { get; }
|
|
public int Length { get;}
|
|
public int Offset { get; }
|
|
|
|
public BarcodeFragment(EncodingFormat typicalFormat, int length, int offset) {
|
|
TypicalFormat = typicalFormat;
|
|
Length = length;
|
|
Offset = offset;
|
|
}
|
|
}
|
|
|
|
private readonly Dictionary<string, BarcodeFragment> _fragments = new Dictionary<string, BarcodeFragment> {
|
|
["FormatControlCode"] = new BarcodeFragment(EncodingFormat.N, 4, 2),
|
|
["SortingCode"] = new BarcodeFragment(EncodingFormat.N, 16, 6),
|
|
["CustomerInformation2"] = new BarcodeFragment(EncodingFormat.C, 16, 22),
|
|
["CustomerInformation3"] = new BarcodeFragment(EncodingFormat.C, 31, 22),
|
|
["ErrorCorrection"] = new BarcodeFragment(EncodingFormat.BarToDecimal, 12, - 16),
|
|
};
|
|
|
|
private enum EncodingFormat {
|
|
N,
|
|
C,
|
|
BarToDecimal,
|
|
}
|
|
|
|
private int _formatCode;
|
|
|
|
private readonly 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(GetFragment(_fragments["FormatControlCode"]));
|
|
Console.WriteLine(Format);
|
|
|
|
// process Sorting Code Field
|
|
SortingCode = int.Parse(GetFragment(_fragments["SortingCode"]));
|
|
Console.WriteLine($"Sorting code: {SortingCode}");
|
|
|
|
// process Customer Information, if necessary
|
|
// TODO: actually do this, and get a code to test it on. also, ensure that customer codes are the right length
|
|
if (_formatCode == 59) {
|
|
Console.WriteLine("This code has a Customer Information field, but I don't know how to process that yet 0uo");
|
|
} else if (_formatCode == 62) {
|
|
Console.WriteLine("This code has a Customer Information field, but I don't know how to process that yet 0uo");
|
|
}
|
|
|
|
// process Reed-Solomon Error Correction Bars
|
|
// TODO: actually validate the barcode
|
|
Console.WriteLine(BaseConversion.FromBase(GetFragment(_fragments["ErrorCorrection"]), 64));
|
|
|
|
}
|
|
|
|
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; } = new List<string>();
|
|
private string Code { get; }
|
|
|
|
private string GetFragment(BarcodeFragment fragment, bool decode = true) {
|
|
var data = Code.Substring(fragment.Offset < 0 ? Code.Length + fragment.Offset : fragment.Offset, fragment.Length);
|
|
|
|
if (!decode) {
|
|
return data;
|
|
}
|
|
|
|
// bool badData;
|
|
var chunkLength = fragment.TypicalFormat == EncodingFormat.N ? 2 : 3;
|
|
var rx = new Regex("^[0123]+$");
|
|
var sb = new StringBuilder();
|
|
if (!rx.IsMatch(data)) {
|
|
throw new ArgumentException("Input length must be a quaternary number.", nameof(data));
|
|
}
|
|
|
|
for (var i = 0; i < data.Length; i += chunkLength) {
|
|
var chunk = data.Substring(i, chunkLength);
|
|
|
|
switch (fragment.TypicalFormat) {
|
|
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 {data.ToString()}.");
|
|
}
|
|
else {
|
|
sb.Append(BaseConversion.FromBase(chunk, 3));
|
|
}
|
|
break;
|
|
|
|
case EncodingFormat.C:
|
|
break;
|
|
|
|
case EncodingFormat.BarToDecimal:
|
|
var digitInt = BaseConversion.FromBase(chunk, 4);
|
|
sb.Insert(0, BaseConversion.ToBase(digitInt, 64));
|
|
break;
|
|
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(fragment.TypicalFormat), fragment.TypicalFormat, null);
|
|
}
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|