// Package script: Unicode Script & Script_Extensions classification and analysis

///|
pub(all) enum Script {
  Common
  Inherited
  Latin
  Cyrillic
  Greek
  Arabic
  Hebrew
  Han
  Hiragana
  Katakana
  Hangul
  Unknown
} derive(Eq, Debug)

///|
pub impl Show for Script with fn output(self, logger) {
  match self {
    Common => logger.write_string("Common")
    Inherited => logger.write_string("Inherited")
    Latin => logger.write_string("Latin")
    Cyrillic => logger.write_string("Cyrillic")
    Greek => logger.write_string("Greek")
    Arabic => logger.write_string("Arabic")
    Hebrew => logger.write_string("Hebrew")
    Han => logger.write_string("Han")
    Hiragana => logger.write_string("Hiragana")
    Katakana => logger.write_string("Katakana")
    Hangul => logger.write_string("Hangul")
    Unknown => logger.write_string("Unknown")
  }
}

///|
pub(all) enum RestrictionLevel {
  SingleScript
  HighlyRestrictive
  ModeratelyRestrictive
  MinimallyRestrictive
  Unrestricted
} derive(Eq, Debug)

///|
pub impl Show for RestrictionLevel with fn output(self, logger) {
  match self {
    SingleScript => logger.write_string("SingleScript")
    HighlyRestrictive => logger.write_string("HighlyRestrictive")
    ModeratelyRestrictive => logger.write_string("ModeratelyRestrictive")
    MinimallyRestrictive => logger.write_string("MinimallyRestrictive")
    Unrestricted => logger.write_string("Unrestricted")
  }
}

///|
pub(all) struct ScriptAnalysis {
  primary_script : Script
  scripts : Array[Script]
  is_mixed : Bool
  level : RestrictionLevel
} derive(Debug)

///|
pub fn get_script(cp : Int32) -> Script {
  binary_search_script(cp)
}

///|
pub fn contains_script(arr : Array[Script], s : Script) -> Bool {
  for item in arr {
    if item == s {
      return true
    }
  }
  false
}

///|

///|
pub fn has_mixed_triad_scripts(scripts : Array[Script]) -> Bool {
  let has_lat = contains_script(scripts, Latin)
  let has_cyr = contains_script(scripts, Cyrillic)
  let has_grk = contains_script(scripts, Greek)
  (has_lat && has_cyr) || (has_lat && has_grk) || (has_cyr && has_grk)
}

///|
pub fn is_recommended_script(s : Script) -> Bool {
  match s {
    Latin
    | Cyrillic
    | Greek
    | Arabic
    | Hebrew
    | Han
    | Hiragana
    | Katakana
    | Hangul => true
    Common | Inherited | Unknown => false
  }
}

///|
pub fn analyze_scripts(input : String) -> ScriptAnalysis {
  let found : Array[Script] = []
  let mut primary = Script::Common
  let mut has_disallowed = false

  for ch in input {
    let cp = ch.to_int()
    let s = get_script(cp)
    if s == Unknown {
      has_disallowed = true
    }
    if s != Common && s != Inherited && s != Unknown {
      if !contains_script(found, s) {
        found.push(s)
      }
      if primary == Common {
        primary = s
      }
    }
  }
  let count = found.length()
  let is_mixed = count > 1
  let level = if has_disallowed {
    RestrictionLevel::Unrestricted
  } else if count <= 1 {
    RestrictionLevel::SingleScript
  } else if is_cjk_script_set(found) {
    RestrictionLevel::HighlyRestrictive
  } else if count == 2 &&
    (
      (contains_script(found, Latin) && contains_script(found, Arabic)) ||
      (contains_script(found, Latin) && contains_script(found, Hebrew))
    ) {
    RestrictionLevel::ModeratelyRestrictive
  } else {
    let mut all_rec = true
    for s in found {
      if !is_recommended_script(s) {
        all_rec = false
      }
    }
    if all_rec && count > 0 {
      RestrictionLevel::MinimallyRestrictive
    } else {
      RestrictionLevel::Unrestricted
    }
  }
  { primary_script: primary, scripts: found, is_mixed, level, }
}

///|
fn is_cjk_script_set(scripts : Array[Script]) -> Bool {
  let mut japanese = true
  let mut korean = true
  for script in scripts {
    if script != Latin &&
      script != Han &&
      script != Hiragana &&
      script != Katakana {
      japanese = false
    }
    if script != Latin && script != Han && script != Hangul {
      korean = false
    }
  }
  japanese || korean
}

///|
pub fn is_single_script(input : String) -> Bool {
  let res = analyze_scripts(input)
  res.level == RestrictionLevel::SingleScript
}

///|
pub fn is_mixed_script(input : String) -> Bool {
  let res = analyze_scripts(input)
  res.is_mixed
}

///|
pub fn get_restriction_level(input : String) -> RestrictionLevel {
  let res = analyze_scripts(input)
  res.level
}