import re
from enum import Enum
from typing import Union

LanguageType = Union['Language', str]


class Language(Enum):
    Abkhazian = "abk"
    Achinese = "ace"
    Acoli = "ach"
    Adangme = "ada"
    Adygei = "ady"
    Adyghe = "ady"
    Afar = "aar"
    Afrihili = "afh"
    Afrikaans = "afr"
    AfroAsiaticLanguages = "afa"
    Ainu = "ain"
    Akan = "aka"
    Akkadian = "akk"
    Albanian = "alb"
    Alemannic = "gsw"
    Aleut = "ale"
    AlgonquianLanguages = "alg"
    Alsatian = "gsw"
    AltaicLanguages = "tut"
    Amharic = "amh"
    AncientEgyptian = "egy"
    AncientGreek = "grc"
    Angika = "anp"
    ApacheLanguages = "apa"
    Arabic = "ara"
    Aragonese = "arg"
    Arapaho = "arp"
    Arawak = "arw"
    Armenian = "arm"
    Aromanian = "rup"
    ArtificialLanguages = "art"
    Arumanian = "rup"
    Assamese = "asm"
    Asturian = "ast"
    Asturleonese = "ast"
    AthapascanLanguages = "ath"
    AustralianLanguages = "aus"
    AustronesianLanguages = "map"
    Avaric = "ava"
    Avestan = "ave"
    Awadhi = "awa"
    Aymara = "aym"
    Azerbaijani = "aze"
    Bable = "ast"
    Balinese = "ban"
    BalticLanguages = "bat"
    Baluchi = "bal"
    Bambara = "bam"
    BamilekeLanguages = "bai"
    BandaLanguages = "bad"
    BantuLanguages = "bnt"
    Basa = "bas"
    Bashkir = "bak"
    Basque = "baq"
    BatakLanguages = "btk"
    Bedawiyet = "bej"
    Beja = "bej"
    Belarusian = "bel"
    Bemba = "bem"
    Bengali = "ben"
    BerberLanguages = "ber"
    Bhojpuri = "bho"
    BihariLanguages = "bih"
    Bikol = "bik"
    Bilin = "byn"
    Bini = "bin"
    Bislama = "bis"
    Blin = "byn"
    Bliss = "zbl"
    Blissymbolics = "zbl"
    Blissymbols = "zbl"
    Bosnian = "bos"
    Braj = "bra"
    Breton = "bre"
    Buginese = "bug"
    Bulgarian = "bul"
    Buriat = "bua"
    Burmese = "bur"
    Caddo = "cad"
    Castilian = "spa"
    Catalan = "cat"
    CaucasianLanguages = "cau"
    Cebuano = "ceb"
    CelticLanguages = "cel"
    CentralAmericanIndianLanguages = "cai"
    CentralKhmer = "khm"
    Chagatai = "chg"
    ChamicLanguages = "cmc"
    Chamorro = "cha"
    Chechen = "che"
    Cherokee = "chr"
    Chewa = "nya"
    Cheyenne = "chy"
    Chibcha = "chb"
    Chichewa = "nya"
    Chinese = "chi"
    ChinookJargon = "chn"
    Chipewyan = "chp"
    Choctaw = "cho"
    Chuang = "zha"
    ChurchSlavic = "chu"
    ChurchSlavonic = "chu"
    Chuukese = "chk"
    Chuvash = "chv"
    ClassicalNepalBhasa = "nwc"
    ClassicalNewari = "nwc"
    ClassicalSyriac = "syc"
    CookIslandsMaori = "rar"
    Coptic = "cop"
    Cornish = "cor"
    Corsican = "cos"
    Cree = "cre"
    Creek = "mus"
    CreolesAndPidgins = "crp"
    CrimeanTatar = "crh"
    CrimeanTurkish = "crh"
    Croatian = "hrv"
    CushiticLanguages = "cus"
    Czech = "cze"
    Dakota = "dak"
    Danish = "dan"
    Dargwa = "dar"
    Delaware = "del"
    DeneSuline = "chp"
    Dhivehi = "div"
    Dimili = "zza"
    Dimli = "zza"
    Dinka = "din"
    Divehi = "div"
    Dogri = "doi"
    Dogrib = "dgr"
    DravidianLanguages = "dra"
    Duala = "dua"
    Dutch = "dut"
    Dyula = "dyu"
    Dzongkha = "dzo"
    EasternFrisian = "frs"
    Edo = "bin"
    Efik = "efi"
    Ekajuk = "eka"
    Elamite = "elx"
    English = "eng"
    EnglishBasedCreolesAndPidgins = "cpe"
    Erzya = "myv"
    Esperanto = "epo"
    Estonian = "est"
    Ewe = "ewe"
    Ewondo = "ewo"
    Fang = "fan"
    Fanti = "fat"
    Faroese = "fao"
    Fijian = "fij"
    Filipino = "fil"
    Finnish = "fin"
    FinnoUgrianLanguages = "fiu"
    Flemish = "dut"
    Fon = "fon"
    French = "fre"
    FrenchBasedCreolesAndPidgins = "cpf"
    Friulian = "fur"
    Fulah = "ful"
    Ga = "gaa"
    Gaelic = "gla"
    GalibiCarib = "car"
    Galician = "glg"
    Ganda = "lug"
    Gayo = "gay"
    Gbaya = "gba"
    Geez = "gez"
    Georgian = "geo"
    German = "ger"
    GermanicLanguages = "gem"
    Gikuyu = "kik"
    Gilbertese = "gil"
    Gondi = "gon"
    Gorontalo = "gor"
    Gothic = "got"
    Grebo = "grb"
    Greenlandic = "kal"
    Guarani = "grn"
    Gujarati = "guj"
    GwichIn = "gwi"
    Haida = "hai"
    Haitian = "hat"
    HaitianCreole = "hat"
    Hausa = "hau"
    Hawaiian = "haw"
    Hebrew = "heb"
    Herero = "her"
    Hiligaynon = "hil"
    Himachalilanguages = "him"
    Hindi = "hin"
    HiriMotu = "hmo"
    Hittite = "hit"
    Hmong = "hmn"
    Hungarian = "hun"
    Hupa = "hup"
    Iban = "iba"
    Icelandic = "ice"
    Ido = "ido"
    Igbo = "ibo"
    IjoLanguages = "ijo"
    Iloko = "ilo"
    ImperialAramaic = "arc"
    InariSami = "smn"
    IndicLanguages = "inc"
    IndoEuropeanLanguages = "ine"
    Indonesian = "ind"
    Ingush = "inh"
    Interlingua = "ina"  # (International Auxiliary Language Association),
    Interlingue = "ile"
    Inuktitut = "iku"
    Inupiaq = "ipk"
    IranianLanguages = "ira"
    Irish = "gle"
    IroquoianLanguages = "iro"
    Italian = "ita"
    Japanese = "jpn"
    Javanese = "jav"
    Jingpho = "kac"
    JudeoArabic = "jrb"
    JudeoPersian = "jpr"
    Kabardian = "kbd"
    Kabyle = "kab"
    Kachin = "kac"
    Kalaallisut = "kal"
    Kalmyk = "xal"
    Kamba = "kam"
    Kannada = "kan"
    Kanuri = "kau"
    Kapampangan = "pam"
    KarachayBalkar = "krc"
    KaraKalpak = "kaa"
    Karelian = "krl"
    KarenLanguages = "kar"
    Kashmiri = "kas"
    Kashubian = "csb"
    Kawi = "kaw"
    Kazakh = "kaz"
    Khasi = "kha"
    KhoisanLanguages = "khi"
    Khotanese = "kho"
    Kikuyu = "kik"
    Kimbundu = "kmb"
    Kinyarwanda = "kin"
    Kirdki = "zza"
    Kirghiz = "kir"
    Kirmanjki = "zza"
    Klingon = "tlh"
    Komi = "kom"
    Kongo = "kon"
    Konkani = "kok"
    Korean = "kor"
    Kosraean = "kos"
    Kpelle = "kpe"
    KruLanguages = "kro"
    Kuanyama = "kua"
    Kumyk = "kum"
    Kurdish = "kur"
    Kurukh = "kru"
    Kutenai = "kut"
    Kwanyama = "kua"
    Kyrgyz = "kir"
    Ladino = "lad"
    Lahnda = "lah"
    Lamba = "lam"
    LandDayakLanguages = "day"
    Lao = "lao"
    Latin = "lat"
    Latvian = "lav"
    Leonese = "ast"
    Letzeburgesch = "ltz"
    Lezghian = "lez"
    Limburgan = "lim"
    Limburger = "lim"
    Limburgish = "lim"
    Lingala = "lin"
    Lithuanian = "lit"
    Lojban = "jbo"
    LowerSorbian = "dsb"
    LowGerman = "nds"
    LowSaxon = "nds"
    Lozi = "loz"
    LubaKatanga = "lub"
    LubaLulua = "lua"
    Luiseno = "lui"
    LuleSami = "smj"
    Lunda = "lun"
    Luo = "luo"  # (Kenya and Tanzania),
    Lushai = "lus"
    Luxembourgish = "ltz"
    Macedonian = "mac"
    MacedoRomanian = "rup"
    Madurese = "mad"
    Magahi = "mag"
    Maithili = "mai"
    Makasar = "mak"
    Malagasy = "mlg"
    Malay = "may"
    Malayalam = "mal"
    Maldivian = "div"
    Maltese = "mlt"
    Manchu = "mnc"
    Mandar = "mdr"
    Mandingo = "man"
    Manipuri = "mni"
    ManoboLanguages = "mno"
    Manx = "glv"
    Maori = "mao"
    Mapuche = "arn"
    Mapudungun = "arn"
    Marathi = "mar"
    Mari = "chm"
    Marshallese = "mah"
    Marwari = "mwr"
    Masai = "mas"
    MayanLanguages = "myn"
    Mende = "men"
    Micmac = "mic"
    MiddleDutch = "dum"
    MiddleEnglish = "enm"
    MiddleFrench = "frm"
    MiddleHighGerman = "gmh"
    MiddleIrish = "mga"
    MiKmaq = "mic"
    Minangkabau = "min"
    Mirandese = "mwl"
    ModernGreek = "gre"
    Mohawk = "moh"
    Moksha = "mdf"
    Moldavian = "rum"
    Mong = "hmn"
    Mongo = "lol"
    Mongolian = "mon"
    MonKhmerLanguages = "mkh"
    Montenegrin = "cnr"
    Mossi = "mos"
    MultipleLanguages = "mul"
    MundaLanguages = "mun"
    NahuatlLanguages = "nah"
    Nauru = "nau"
    Navaho = "nav"
    Navajo = "nav"
    Ndonga = "ndo"
    Neapolitan = "nap"
    NepalBhasa = "new"
    Nepali = "nep"
    Newari = "new"
    Nias = "nia"
    NigerKordofanianLanguages = "nic"
    NiloSaharanLanguages = "ssa"
    Niuean = "niu"
    NKo = "nqo"
    Nogai = "nog"
    NoLinguisticContent = "zxx"
    NorthAmericanIndianLanguages = "nai"
    NorthernFrisian = "frr"
    NorthernSami = "sme"
    NorthernSotho = "nso"
    NorthNdebele = "nde"
    Norwegian = "nor"
    NorwegianBokmal = "nob"
    NorwegianNynorsk = "nno"
    Notapplicable = "zxx"
    NubianLanguages = "nub"
    Nuosu = "iii"
    Nyamwezi = "nym"
    Nyanja = "nya"
    Nyankole = "nyn"
    Nyoro = "nyo"
    Nzima = "nzi"
    Occidental = "ile"
    Occitan = "oci"
    OfficialAramaic = "arc"
    Oirat = "xal"
    Ojibwa = "oji"
    OldBulgarian = "chu"
    OldChurchSlavonic = "chu"
    OldEnglish = "ang"
    OldFrench = "fro"
    OldHighGerman = "goh"
    OldIrish = "sga"
    OldNewari = "nwc"
    OldNorse = "non"
    OldOccitan = "pro"
    OldPersian = "peo"
    OldProvencal = "pro"
    OldSlavonic = "chu"
    Oriya = "ori"
    Oromo = "orm"
    Osage = "osa"
    Ossetian = "oss"
    Ossetic = "oss"
    OtomianLanguages = "oto"
    OttomanTurkish = "ota"
    Pahlavi = "pal"
    Palauan = "pau"
    Pali = "pli"
    Pampanga = "pam"
    Pangasinan = "pag"
    Panjabi = "pan"
    Papiamento = "pap"
    PapuanLanguages = "paa"
    Pashto = "pus"
    Pedi = "nso"
    Persian = "per"
    PhilippineLanguages = "phi"
    Phoenician = "phn"
    Pilipino = "fil"
    Pohnpeian = "pon"
    Polish = "pol"
    Portuguese = "por"
    PortugueseBasedCreolesAndPidgins = "cpp"
    PrakritLanguages = "pra"
    Punjabi = "pan"
    Pushto = "pus"
    Quechua = "que"
    Rajasthani = "raj"
    Rapanui = "rap"
    Rarotongan = "rar"
    ReservedForLocalUse = "qaa-qtz"
    RomanceLanguages = "roa"
    Romanian = "rum"
    Romansh = "roh"
    Romany = "rom"
    Rundi = "run"
    Russian = "rus"
    Sakan = "kho"
    SalishanLanguages = "sal"
    SamaritanAramaic = "sam"
    SamiLanguages = "smi"
    Samoan = "smo"
    Sandawe = "sad"
    Sango = "sag"
    Sanskrit = "san"
    Santali = "sat"
    Sardinian = "srd"
    Sasak = "sas"
    Scots = "sco"
    ScottishGaelic = "gla"
    Selkup = "sel"
    SemiticLanguages = "sem"
    Sepedi = "nso"
    Serbian = "srp"
    Serer = "srr"
    Shan = "shn"
    Shona = "sna"
    SichuanYi = "iii"
    Sicilian = "scn"
    Sidamo = "sid"
    SignLanguages = "sgn"
    Siksika = "bla"
    Sindhi = "snd"
    Sinhala = "sin"
    Sinhalese = "sin"
    SinoTibetanLanguages = "sit"
    SiouanLanguages = "sio"
    SkoltSami = "sms"
    Slave = "den"  # (Athapascan)
    SlavicLanguages = "sla"
    Slovak = "slo"
    Slovenian = "slv"
    Sogdian = "sog"
    Somali = "som"
    SonghaiLanguages = "son"
    Soninke = "snk"
    SorbianLanguages = "wen"
    SouthAmericanIndianLanguages = "sai"
    SouthernAltai = "alt"
    SouthernSami = "sma"
    SouthernSotho = "sot"
    SouthNdebele = "nbl"
    Spanish = "spa"
    SrananTongo = "srn"
    StandardMoroccanTamazight = "zgh"
    Sukuma = "suk"
    Sumerian = "sux"
    Sundanese = "sun"
    Susu = "sus"
    Swahili = "swa"
    Swati = "ssw"
    Swedish = "swe"
    SwissGerman = "gsw"
    Syriac = "syr"
    Tagalog = "tgl"
    Tahitian = "tah"
    TaiLanguages = "tai"
    Tajik = "tgk"
    Tamashek = "tmh"
    Tamil = "tam"
    Tatar = "tat"
    Telugu = "tel"
    Tereno = "ter"
    Tetum = "tet"
    Thai = "tha"
    Tibetan = "tib"
    Tigre = "tig"
    Tigrinya = "tir"
    Timne = "tem"
    Tiv = "tiv"
    TlhInganHol = "tlh"
    Tlingit = "tli"
    Tokelau = "tkl"
    TokPisin = "tpi"
    Tonga_Nyasa = "tog"
    Tonga_TongaIslands = "ton"
    Tsimshian = "tsi"
    Tsonga = "tso"
    Tswana = "tsn"
    Tumbuka = "tum"
    TupiLanguages = "tup"
    Turkish = "tur"
    Turkmen = "tuk"
    Tuvalu = "tvl"
    Tuvinian = "tyv"
    Twi = "twi"
    Udmurt = "udm"
    Ugaritic = "uga"
    Uighur = "uig"
    Ukrainian = "ukr"
    Umbundu = "umb"
    UncodedLanguages = "mis"
    Undetermined = "und"
    UpperSorbian = "hsb"
    Urdu = "urd"
    Uyghur = "uig"
    Uzbek = "uzb"
    Vai = "vai"
    Valencian = "cat"
    Venda = "ven"
    Vietnamese = "vie"
    Volapuk = "vol"
    Votic = "vot"
    WakashanLanguages = "wak"
    Walloon = "wln"
    Waray = "war"
    Washo = "was"
    Welsh = "wel"
    WesternFrisian = "fry"
    WesternPahariLanguages = "him"
    Wolaitta = "wal"
    Wolaytta = "wal"
    Wolof = "wol"
    Xhosa = "xho"
    Yakut = "sah"
    Yao = "yao"
    Yapese = "yap"
    Yiddish = "yid"
    Yoruba = "yor"
    YupikLanguages = "ypk"
    ZandeLanguages = "znd"
    Zapotec = "zap"
    Zaza = "zza"
    Zazaki = "zza"
    Zenaga = "zen"
    Zhuang = "zha"
    Zulu = "zul"
    Zuni = "zun"

    def __str__(self) -> str:
        return self.value

    def __format__(self, format_spec):
        if format_spec is None or format_spec == "" or format_spec == "r":
            return self.__repr__()
        elif format_spec == "s":
            return self.__str__()
        else:
            raise ValueError(f"Unknown format code '{format_spec}' for object of type 'Language'")

    @staticmethod
    def value_of(val: LanguageType) -> 'Language':
        if isinstance(val, Language):
            return val
        elif isinstance(val, str):
            val = val.strip(" ")
            if len(val) == 3:
                return Language(val.lower())
            elif re.match("[- ]", val) is not None:
                return Language.value_of(val.replace(" ", "").replace("-", ""))
            else:
                for lang in Language:
                    if lang.name.lower() == val.lower():
                        return lang
        else:
            raise NotImplementedError()
