package klyn.i18n

import klyn
import klyn.collections

/**
 * A Locale object represents a specific geographical, political, or cultural region.
 *
 * It is used to localize information such as date/time formatting,
 * number formatting, and string translations.
 */
public class Locale extends Object:

    private static _default as Locale = null

    private _language as String
    private _country as String

    # Data properties for localization
    private _dayNames as ArrayList<String>
    private _shortDayNames as ArrayList<String>
    private _monthNames as ArrayList<String>
    private _shortMonthNames as ArrayList<String>
    private _amPm as ArrayList<String>

    /** Predefined constant for English (United States). */
    public static readonly US as Locale = Locale("en", "US")

    /** Predefined constant for English (United Kingdom). */
    public static readonly UK as Locale = Locale("en", "GB")

    /** Predefined constant for French (France). */
    public static readonly FRANCE as Locale = Locale("fr", "FR")

    /** Predefined constant for German (Germany). */
    public static readonly GERMANY as Locale = Locale("de", "DE")

    /** Predefined constant for Spanish (Spain). */
    public static readonly SPAIN as Locale = Locale("es", "ES")

    /** Predefined constant for Italian (Italy). */
    public static readonly ITALY as Locale = Locale("it", "IT")

    /** Predefined constant for Portuguese (Portugal). */
    public static readonly PORTUGAL as Locale = Locale("pt", "PT")

    /** Predefined constant for Chinese (China). */
    public static readonly CHINA as Locale = Locale("zh", "CN")

    /** Predefined constant for Japanese (Japan). */
    public static readonly JAPAN as Locale = Locale("ja", "JP")

    /** Predefined constant for Arabic (Saudi Arabia). */
    public static readonly ARABIC as Locale = Locale("ar", "SA")

    /**
     * Predefined constant for Hebrew (Israel).
     *
     * ```klyn
     * import klyn.i18n
     * assert Locale.HEBREW.language == "he"
     * ```
     */
    public static readonly HEBREW as Locale = Locale("he", "IL")

    /** Predefined constant for Vietnamese (Vietnam). */
    public static readonly VIETNAM as Locale = Locale("vi", "VN")

    /** Predefined constant for Russian (Russia). */
    public static readonly RUSSIA as Locale = Locale("ru", "RU")

    /** Predefined constant for Greek (Greece). */
    public static readonly GREECE as Locale = Locale("el", "GR")

    /** Predefined constant for Hebrew (Israel). */
    public static readonly ISRAEL as Locale = Locale("he", "IL")

    /**
     * Constructs a new Locale from language and country codes.
     */
    public Locale(language as String, country as String = ""):
        this._language = language.toLower()
        this._country = country.toUpper()
        this._initializeData()

    /** The language code (lowercase ISO 639). */
    public property language as String:
        get:
            return this._language

    /** The country code (uppercase ISO 3166). */
    public property country as String:
        get:
            return this._country

    /** Returns the current default locale for the environment. */
    public static property default as Locale:
        get:
            if Locale._default is null:
                Locale._default = Locale._defaultFromEnvironment()
            return Locale._default
        set:
            Locale._default = value

    private static _defaultFromEnvironment() as Locale:
        localeText as String = Locale._firstEnvironmentValue(["LC_ALL", "LC_MESSAGES", "LANG", "LANGUAGE"])
        localeCode as String = Locale._normalizeLocaleCode(localeText)
        if localeCode == "":
            return Locale.US
        return Locale._fromLocaleCode(localeCode)

    private static _firstEnvironmentValue(names as IList<String>) as String:
        for name in names:
            if name in Application.environmentVariables:
                value as String = Application.environmentVariables[name]
                if value is not null and value.trim() != "":
                    return value.trim()
        return ""

    private static _normalizeLocaleCode(value as String) as String:
        if value is null:
            return ""
        code as String = value.trim()
        if code == "" or code == "C" or code == "POSIX":
            return ""
        colon as Int = code.indexOf(":")
        if colon >= 0:
            code = code.substring(0, colon)
        dot as Int = code.indexOf(".")
        if dot >= 0:
            code = code.substring(0, dot)
        return code.replace("-", "_")

    private static _fromLocaleCode(code as String) as Locale:
        normalized as String = code.replace("-", "_")
        sep as Int = normalized.indexOf("_")
        language as String = normalized
        country as String = ""
        if sep >= 0:
            language = normalized.substring(0, sep)
            country = normalized.substring(sep + 1)
        language = language.toLower()
        country = country.toUpper()
        if country == "":
            country = Locale._defaultCountryForLanguage(language)
        return Locale(language, country)

    private static _defaultCountryForLanguage(language as String) as String:
        if language == "en":
            return "US"
        if language == "fr":
            return "FR"
        if language == "de":
            return "DE"
        if language == "es":
            return "ES"
        if language == "it":
            return "IT"
        if language == "pt":
            return "PT"
        if language == "zh":
            return "CN"
        if language == "ja":
            return "JP"
        if language == "ar":
            return "SA"
        if language == "he" or language == "iw":
            return "IL"
        if language == "vi":
            return "VN"
        if language == "ru":
            return "RU"
        if language == "el":
            return "GR"
        return ""

    # --- Formatting Data Properties ---

    public property dayNames as ArrayList<String>:
        get:
            return this._dayNames
        set:
            this._dayNames = value

    public property shortDayNames as ArrayList<String>:
        get:
            return this._shortDayNames
        set:
            this._shortDayNames = value

    public property monthNames as ArrayList<String>:
        get:
            return this._monthNames
        set:
            this._monthNames = value

    public property shortMonthNames as ArrayList<String>:
        get:
            return this._shortMonthNames
        set:
            this._shortMonthNames = value

    public property amPm as ArrayList<String>:
        get:
            return this._amPm
        set:
            this._amPm = value

    private _initializeData() as Void:
        if this._language == "fr":
            this._dayNames = ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"]
            this._shortDayNames = ["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."]
            this._monthNames = ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"]
            this._shortMonthNames = ["janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.", "nov.", "déc."]
            this._amPm = ["AM", "PM"]
        elif this._language == "de":
            this._dayNames = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"]
            this._shortDayNames = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"]
            this._monthNames = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]
            this._shortMonthNames = ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"]
            this._amPm = ["AM", "PM"]
        elif this._language == "es":
            this._dayNames = ["domingo", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado"]
            this._shortDayNames = ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"]
            this._monthNames = ["enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"]
            this._shortMonthNames = ["ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sep", "oct", "nov", "dic"]
            this._amPm = ["AM", "PM"]
        elif this._language == "it":
            this._dayNames = ["domenica", "lunedì", "martedì", "mercoledì", "giovedì", "venerdì", "sabato"]
            this._shortDayNames = ["dom", "lun", "mar", "mer", "gio", "ven", "sab"]
            this._monthNames = ["gennaio", "febbraio", "marzo", "aprile", "maggio", "giugno", "luglio", "agosto", "settembre", "ottobre", "novembre", "dicembre"]
            this._shortMonthNames = ["gen", "feb", "mar", "apr", "mag", "giu", "lug", "ago", "set", "ott", "nov", "dic"]
            this._amPm = ["AM", "PM"]
        elif this._language == "pt":
            this._dayNames = ["domingo", "segunda-feira", "terça-feira", "quarta-feira", "quinta-feira", "sexta-feira", "sábado"]
            this._shortDayNames = ["dom", "seg", "ter", "qua", "qui", "sex", "sáb"]
            this._monthNames = ["janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro", "outubro", "novembro", "dezembro"]
            this._shortMonthNames = ["jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set", "out", "nov", "dez"]
            this._amPm = ["AM", "PM"]
        elif this._language == "zh":
            this._dayNames = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]
            this._shortDayNames = ["日", "一", "二", "三", "四", "五", "六"]
            this._monthNames = ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"]
            this._shortMonthNames = ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"]
            this._amPm = ["上午", "下午"]
        elif this._language == "ja":
            this._dayNames = ["日曜日", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日"]
            this._shortDayNames = ["日", "月", "火", "水", "木", "金", "土"]
            this._monthNames = ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"]
            this._shortMonthNames = ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"]
            this._amPm = ["午前", "午後"]
        elif this._language == "ar":
            this._dayNames = ["الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت"]
            this._shortDayNames = ["ح", "ن", "ث", "ر", "خ", "ج", "س"]
            this._monthNames = ["يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو", "يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر"]
            this._shortMonthNames = ["ينا", "فبر", "مار", "أبر", "ماي", "يون", "يول", "أغس", "سبت", "أكت", "نوف", "ديس"]
            this._amPm = ["ص", "م"]
        elif this._language == "vi":
            this._dayNames = ["chủ nhật", "thứ hai", "thứ ba", "thứ tư", "thứ năm", "thứ sáu", "thứ bảy"]
            this._shortDayNames = ["CN", "T2", "T3", "T4", "T5", "T6", "T7"]
            this._monthNames = ["tháng 1", "tháng 2", "tháng 3", "tháng 4", "tháng 5", "tháng 6", "tháng 7", "tháng 8", "tháng 9", "tháng 10", "tháng 11", "tháng 12"]
            this._shortMonthNames = ["thg 1", "thg 2", "thg 3", "thg 4", "thg 5", "thg 6", "thg 7", "thg 8", "thg 9", "thg 10", "thg 11", "thg 12"]
            this._amPm = ["SA", "CH"]
        elif this._language == "ru":
            this._dayNames = ["воскресенье", "понедельник", "вторник", "среда", "четверг", "пятница", "суббота"]
            this._shortDayNames = ["вс", "пн", "вт", "ср", "чт", "пт", "сб"]
            this._monthNames = ["январь", "февраль", "март", "апрель", "май", "июнь", "июль", "август", "сентябрь", "октябрь", "ноябрь", "декабрь"]
            this._shortMonthNames = ["янв.", "февр.", "март", "апр.", "май", "июнь", "июль", "авг.", "сент.", "окт.", "нояб.", "дек."]
            this._amPm = ["ДП", "ПП"]
        elif this._language == "el":
            this._dayNames = ["Κυριακή", "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο"]
            this._shortDayNames = ["Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ"]
            this._monthNames = ["Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"]
            this._shortMonthNames = ["Ιαν", "Φεβ", "Μάρ", "Απρ", "Μάι", "Ιούν", "Ιούλ", "Αύγ", "Σεπ", "Οκτ", "Νοέ", "Δεκ"]
            this._amPm = ["πμ", "μμ"]
        elif this._language == "he":
            this._dayNames = ["יום ראשון", "יום שני", "יום שלישי", "יום רביעי", "יום חמישי", "יום שישי", "שבת"]
            this._shortDayNames = ["א'", "ב'", "ג'", "ד'", "ה'", "ו'", "ש'"]
            this._monthNames = ["ינואר", "פברואר", "מרץ", "אפריל", "מאי", "יוני", "יולי", "אוגוסט", "ספטמבר", "אוקטובר", "נובמבר", "דצמבר"]
            this._shortMonthNames = ["ינו", "פבר", "מרץ", "אפר", "מאי", "יונ", "יול", "אוג", "ספט", "אוק", "נוב", "דצמ"]
            this._amPm = ["לפנה\"צ", "אחה\"צ"]
        else:
            # Default to English
            this._dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
            this._shortDayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
            this._monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
            this._shortMonthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
            this._amPm = ["AM", "PM"]

    public override toString() as String:
        if this._country == "":
            return this._language
        return this._language + "_" + this._country

    public equals(other as Object) as Boolean:
        if other is null or other is not Locale:
            return false
        loc = other as Locale
        return this._language == loc.language and this._country == loc.country
