#include "GTIN.h"
#include <algorithm>
#ifdef __cpp_lib_format
#include <format>
#endif
#include <iomanip>
#include <iterator>
#include <string>
namespace ZXing::GTIN {
struct CountryId
{
uint16_t first;
uint16_t last;
const char id[3];
};
bool operator<(const CountryId& lhs, const CountryId& rhs)
{
return lhs.last < rhs.last;
}
static const CountryId COUNTRIES[] = {
{1, 19, "US"},
{30, 39, "US"},
{60, 99, "US"}, {100, 139, "US"},
{300, 379, "FR"}, {380, 380, "BG"}, {383, 383, "SI"}, {385, 385, "HR"}, {387, 387, "BA"}, {389, 389, "ME"}, {400, 440, "DE"}, {450, 459, "JP"}, {460, 469, "RU"}, {470, 470, "KG"}, {471, 471, "TW"}, {474, 474, "EE"}, {475, 475, "LV"}, {476, 476, "AZ"}, {477, 477, "LT"}, {478, 478, "UZ"}, {479, 479, "LK"}, {480, 480, "PH"}, {481, 481, "BY"}, {482, 482, "UA"}, {483, 483, "TM"}, {484, 484, "MD"}, {485, 485, "AM"}, {486, 486, "GE"}, {487, 487, "KZ"}, {488, 488, "TJ"}, {489, 489, "HK"}, {490, 499, "JP"}, {500, 509, "GB"}, {520, 521, "GR"}, {528, 528, "LB"}, {529, 529, "CY"}, {530, 530, "AL"}, {531, 531, "MK"}, {535, 535, "MT"}, {539, 539, "IE"}, {540, 549, "BE"}, {560, 560, "PT"}, {569, 569, "IS"}, {570, 579, "DK"}, {590, 590, "PL"}, {594, 594, "RO"}, {599, 599, "HU"}, {600, 601, "ZA"}, {603, 603, "GH"}, {604, 604, "SN"}, {608, 608, "BH"}, {609, 609, "MU"}, {611, 611, "MA"}, {613, 613, "DZ"}, {615, 615, "NG"}, {616, 616, "KE"}, {617, 617, "CM"}, {618, 618, "CI"}, {619, 619, "TN"}, {620, 620, "TZ"}, {621, 621, "SY"}, {622, 622, "EG"}, {623, 623, "BN"}, {624, 624, "LY"}, {625, 625, "JO"}, {626, 626, "IR"}, {627, 627, "KW"}, {628, 628, "SA"}, {629, 629, "AE"}, {630, 630, "QA"}, {631, 631, "NA"}, {640, 649, "FI"}, {690, 699, "CN"}, {700, 709, "NO"}, {729, 729, "IL"}, {730, 739, "SE"}, {740, 740, "GT"}, {741, 741, "SV"}, {742, 742, "HN"}, {743, 743, "NI"}, {744, 744, "CR"}, {745, 745, "PA"}, {746, 746, "DO"}, {750, 750, "MX"}, {754, 755, "CA"}, {759, 759, "VE"}, {760, 769, "CH"}, {770, 771, "CO"}, {773, 773, "UY"}, {775, 775, "PE"}, {777, 777, "BO"}, {778, 779, "AR"}, {780, 780, "CL"}, {784, 784, "PY"}, {786, 786, "EC"}, {789, 790, "BR"}, {800, 839, "IT"}, {840, 849, "ES"}, {850, 850, "CU"}, {858, 858, "SK"}, {859, 859, "CZ"}, {860, 860, "RS"}, {865, 865, "MN"}, {867, 867, "KP"}, {868, 869, "TR"}, {870, 879, "NL"}, {880, 880, "KR"}, {883, 883, "MM"}, {884, 884, "KH"}, {885, 885, "TH"}, {888, 888, "SG"}, {890, 890, "IN"}, {893, 893, "VN"}, {896, 896, "PK"}, {899, 899, "ID"}, {900, 919, "AT"}, {930, 939, "AU"}, {940, 949, "NZ"}, {955, 955, "MY"}, {958, 958, "MO"}, };
std::string LookupCountryIdentifier(std::string_view GTIN, const BarcodeFormat format)
{
const auto space = GTIN.find(' ');
const std::string::size_type size = space != std::string::npos ? space : GTIN.size();
if (size != 14 && size != 13 && size != 12 && size != 8)
return {};
const int first = size == 14 ? 1 : 0;
const int implicitZero = size == 12 || (size == 8 && format != BarcodeFormat::EAN8) ? 1 : 0;
if (size != 8 || format != BarcodeFormat::EAN8) { int prefix = FromString<int>(GTIN.substr(first, 7 - implicitZero));
if (prefix >= 0 && prefix <= 99)
return {};
prefix = FromString<int>(GTIN.substr(first, 5 - implicitZero));
if (prefix >= 1 && prefix <= 9)
return "US";
prefix = FromString<int>(GTIN.substr(first, 4 - implicitZero));
if (prefix >= 1 && prefix <= 9)
return "US";
}
const int prefix = FromString<int>(GTIN.substr(first, 3 - implicitZero));
if (size == 8 && format == BarcodeFormat::EAN8 && prefix <= 99) return {};
const auto it = std::lower_bound(std::begin(COUNTRIES), std::end(COUNTRIES), CountryId{0, narrow_cast<uint16_t>(prefix), ""});
return it != std::end(COUNTRIES) && prefix >= it->first && prefix <= it->last ? it->id : std::string();
}
std::string EanAddOn(const Barcode& barcode)
{
if (barcode.symbologyIdentifier() != "]E3")
return {};
return barcode.text().substr(barcode.format() == BarcodeFormat::EAN8 ? 8 : 13);
}
std::string IssueNr(const std::string& ean2AddOn)
{
if (ean2AddOn.size() != 2)
return {};
return std::to_string(std::stoi(ean2AddOn));
}
std::string Price(const std::string& ean5AddOn)
{
if (ean5AddOn.size() != 5)
return {};
std::string currency;
switch (ean5AddOn.front()) {
case '0': [[fallthrough]];
case '1': currency = "GBP £"; break; case '3': currency = "AUD $"; break; case '4': currency = "NZD $"; break; case '5': currency = "USD $"; break; case '6': currency = "CAD $"; break; case '9':
if (ean5AddOn == "90000") return {};
if (ean5AddOn == "99991") return "0.00";
if (ean5AddOn == "99990")
return "Used";
currency = "";
break;
default: currency = ""; break;
}
int rawAmount = std::stoi(ean5AddOn.substr(1));
#if !defined(__cpp_lib_to_chars) || !defined(__cpp_lib_format)
return currency + std::to_string(rawAmount / 100) + '.' + std::to_string(rawAmount % 100);
#else
return std::format("{}{:.2f}", currency, float(rawAmount) / 100);
#endif
}
}