use crate::shape::*;
use alloc::vec::Vec;
use core::cmp::max;
fn change_lam_alef(ch: &u16) -> u16 {
match ch {
0x0622 => 0x065C,
0x0623 => 0x065D,
0x0625 => 0x065E,
0x0627 => 0x065F,
_ => 0,
}
}
fn is_tashkeel_char(ch: &u16) -> bool {
*ch >= 0x064B && *ch <= 0x0652
}
fn is_tashkeel_char_fe(ch: &u16) -> bool {
*ch >= 0xFE70 && *ch <= 0xFE7F
}
fn is_alef_char(ch: &u16) -> bool {
*ch == 0x0622 || *ch == 0x0623 || *ch == 0x0625 || *ch == 0x0627
}
fn is_lam_alef_char(ch: &u16) -> bool {
*ch >= 0xFEF5 && *ch <= 0xFEFC
}
fn get_link(ch: &u16) -> u16 {
if *ch >= 0x0622 && *ch <= 0x06D3 {
let ind: usize = (*ch - 0x0622).into();
return ARA_LINK[ind];
} else if *ch == 0x200D {
return 3;
} else if *ch >= 0x206D && *ch <= 0x206F {
return 4;
} else if *ch >= 0xFB50 && *ch <= 0xFC62 {
let ind: usize = (*ch - 0xFB50).into();
return PRES_ALINK[ind].into();
} else if *ch >= 0xFE70 && *ch <= 0xFEFC {
let ind: usize = (*ch - 0xFE70).into();
return PRES_BLINK[ind].into();
}
0
}
fn is_tashkeel_on_tatweel_char(ch: &u16) -> i32 {
if *ch >= 0xfe70
&& *ch <= 0xfe7f
&& *ch != NEW_TAIL_CHAR
&& *ch != 0xFE75
&& *ch != SHADDA_TATWEEL_CHAR
{
let ind: usize = (*ch - 0xFE70).into();
return TASHKEEL_MEDIAL[ind].into();
} else if (*ch >= 0xfcf2 && *ch <= 0xfcf4) || *ch == SHADDA_TATWEEL_CHAR {
return 2;
}
0
}
fn is_isolated_tashkeel_char(ch: &u16) -> i32 {
if *ch >= 0xfe70 && *ch <= 0xfe7f && *ch != NEW_TAIL_CHAR && *ch != 0xFE75 {
let ind: usize = (*ch - 0xFE70).into();
return (1 - TASHKEEL_MEDIAL[ind]).into();
} else if *ch >= 0xfc5e && *ch <= 0xfc63 {
return 1;
}
0
}
fn handle_tashkeel_with_tatweel(dest: &mut [u16]) {
let mut i: usize = 0;
while i < dest.len() {
if is_tashkeel_on_tatweel_char(&dest[i]) == 1 {
dest[i] = TATWEEL_CHAR;
} else if is_tashkeel_on_tatweel_char(&dest[i]) == 2 {
dest[i] = SHADDA_TATWEEL_CHAR;
} else if is_isolated_tashkeel_char(&dest[i]) != 0 && dest[i] != SHADDA_CHAR {
dest[i] = SPACE_CHAR;
}
i += 1
}
}
fn count_spaces(dest: &[u16], spaces_countl: &mut usize, spaces_countr: &mut usize) {
let mut s: usize = dest.len();
let mut i: usize = 0;
let mut countl: usize = 0;
let mut countr: usize = 0;
while dest[i] == SPACE_CHAR && countl < s {
countl += 1;
i += 1;
}
if countl < s {
while dest[s - 1] == SPACE_CHAR {
countr += 1;
s -= 1;
}
}
*spaces_countl = countl;
*spaces_countr = countr;
}
fn invert_buffer(buffer: &mut [u16], lowlimit: usize, highlimit: usize) {
let mut i: usize = lowlimit;
let mut j: usize = buffer.len() - highlimit - 1;
while i < j {
buffer.swap(i, j);
i += 1;
j -= 1;
}
}
fn calculate_size(source: &[u16], options: &u32) -> usize {
let mut dest_size: usize = source.len();
let mut i: usize;
let mut lam_alef_option: bool = false;
let mut tashkeel_option: bool = false;
if ((options & U_SHAPE_LETTERS_MASK) == U_SHAPE_LETTERS_SHAPE
|| ((options & U_SHAPE_LETTERS_MASK) == U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED))
&& ((options & U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_RESIZE)
{
lam_alef_option = true;
}
if (options & U_SHAPE_LETTERS_MASK) == U_SHAPE_LETTERS_SHAPE
&& ((options & U_SHAPE_TASHKEEL_MASK) == U_SHAPE_TASHKEEL_RESIZE)
{
tashkeel_option = true;
}
if lam_alef_option || tashkeel_option {
if (options & U_SHAPE_TEXT_DIRECTION_MASK) == U_SHAPE_TEXT_DIRECTION_VISUAL_LTR {
i = 0;
while i < source.len() {
if ((is_alef_char(&source[i])
&& i < (source.len() - 1)
&& source[i + 1] == LAM_CHAR)
|| is_tashkeel_char_fe(&source[i]))
&& dest_size > 0
{
dest_size -= 1;
}
i += 1
}
} else if (options & U_SHAPE_TEXT_DIRECTION_MASK) == U_SHAPE_TEXT_DIRECTION_LOGICAL {
i = 0;
while i < source.len() {
if (((source[i] == LAM_CHAR)
&& (i < (source.len() - 1))
&& (is_alef_char(&source[i + 1])))
|| (is_tashkeel_char_fe(&source[i])))
&& (dest_size > 0)
{
dest_size -= 1;
}
i += 1
}
}
}
if ((options & U_SHAPE_LETTERS_MASK) == U_SHAPE_LETTERS_UNSHAPE)
&& ((options & U_SHAPE_LAMALEF_MASK) == U_SHAPE_LAMALEF_RESIZE)
{
i = 0;
while i < source.len() {
if is_lam_alef_char(&source[i]) && dest_size > 0 {
dest_size += 1;
}
i += 1
}
}
dest_size
}
fn _shape_arabic(
dest: &mut [u16],
tashkeel_flag: i8,
) {
let mut shape: u16;
let mut i: usize;
let mut ii: isize;
const I_END: isize = -1;
let mut last_pos: usize;
let mut nx: isize = -2;
let mut nw: isize;
let mut prev_link: u16 = 0;
let mut last_link: u16 = 0;
let mut curr_link: u16;
let mut next_link: u16 = 0;
let mut w_lamalef: u16;
i = dest.len() - 1;
last_pos = i;
curr_link = get_link(&dest[i]);
loop {
if (curr_link & 0xFF00) > 0 || (get_link(&dest[i]) & IRRELEVANT) != 0 {
nw = (i as isize) - 1;
while nx < 0 {
if nw == I_END {
next_link = 0;
nx = 3000;
} else {
next_link = get_link(&dest[nw as usize]);
if (next_link & IRRELEVANT) == 0 {
nx = nw;
} else {
nw -= 1;
}
}
}
if (curr_link & ALEFTYPE) > 0 && (last_link & LAMTYPE) > 0 {
w_lamalef = change_lam_alef(&dest[i]); if w_lamalef != 0 {
dest[i] = LAMALEF_SPACE_SUB; dest[last_pos] = w_lamalef; i = last_pos; } last_link = prev_link; curr_link = get_link(&w_lamalef); }
let si: usize = (next_link & (LINKR + LINKL)).into();
let sj: usize = (last_link & (LINKR + LINKL)).into();
let sk: usize = (curr_link & (LINKR + LINKL)).into();
shape = SHAPE_TABLE[si][sj][sk].into();
if (curr_link & (LINKR + LINKL)) == 1 {
shape &= 1;
} else if is_tashkeel_char(&dest[i]) {
if ((last_link & LINKL) > 0)
&& ((next_link & LINKR) > 0)
&& (tashkeel_flag == 1)
&& dest[i] != 0x064C
&& dest[i] != 0x064D
{
shape = 1;
if (next_link & ALEFTYPE) == ALEFTYPE && (last_link & LAMTYPE) == LAMTYPE {
shape = 0;
}
} else if tashkeel_flag == 2 && dest[i] == SHADDA06_CHAR {
shape = 1;
} else {
shape = 0;
}
}
if (dest[i] ^ 0x0600) < 0x100 {
if is_tashkeel_char(&dest[i]) {
if tashkeel_flag == 2 && dest[i] != SHADDA06_CHAR {
dest[i] = TASHKEEL_SPACE_SUB;
} else {
let ind: usize = (dest[i] - 0x064B).into();
if dest[i] < 0x064B || ind >= IRRELEVANT_POS.len() {
unreachable!();
}
dest[i] = 0xFE70 + (IRRELEVANT_POS[ind] as u16) + shape;
}
} else if (curr_link & APRESENT) > 0 {
dest[i] = 0xFB50 + (curr_link >> 8) + shape;
} else if (curr_link >> 8) > 0 && (curr_link & IRRELEVANT) == 0 {
dest[i] = 0xFE70 + (curr_link >> 8) + shape;
}
}
}
if (curr_link & IRRELEVANT) == 0 {
prev_link = last_link;
last_link = curr_link;
last_pos = i;
}
ii = (i as isize) - 1;
if ii >= 0 {
i -= 1;
}
if ii == nx {
curr_link = next_link;
nx = -2;
} else if ii != I_END {
curr_link = get_link(&dest[i]);
}
if ii == I_END {
break;
}
}
}
pub fn shape_arabic(input: &[u16], options: &u32) -> Vec<u16> {
let mut source_ptr = input;
let mut tempsource = Vec::<u16>::new();
if (options & U_SHAPE_AGGREGATE_TASHKEEL_MASK) != 0 {
tempsource.resize(input.len() * 2, 0);
let logical_order: bool =
(options & U_SHAPE_TEXT_DIRECTION_MASK) == U_SHAPE_TEXT_DIRECTION_LOGICAL;
let aggregate_tashkeel: bool = (options
& (U_SHAPE_AGGREGATE_TASHKEEL_MASK + U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED))
== (U_SHAPE_AGGREGATE_TASHKEEL + U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED);
let mut j: usize = 2 * input.len();
if logical_order {
j = 0;
}
let mut i: usize = input.len();
if logical_order {
i = 0;
}
let mut end: usize = input.len() - 1;
if logical_order {
end = input.len();
}
let mut aggregation_possible: bool = true;
let mut prev: u16 = 0;
let mut prev_link: u16;
let mut curr_link: u16 = 0;
let mut new_source_length: usize = 0;
while i != end {
prev_link = curr_link;
curr_link = get_link(&input[i]);
if aggregate_tashkeel
&& ((prev_link | curr_link) & COMBINE) == COMBINE
&& aggregation_possible
{
aggregation_possible = false;
if prev < input[i] {
tempsource[j] = prev - 0x064C + 0xFC5E;
} else {
tempsource[j] = input[i] - 0x064C + 0xFC5E;
}
curr_link = get_link(&tempsource[j]);
} else {
new_source_length += 1;
aggregation_possible = true;
tempsource[j] = input[i];
if logical_order {
j += 1;
} else {
j -= 1;
}
prev = input[i];
}
if logical_order {
i += 1;
} else {
i -= 1;
}
}
if logical_order {
source_ptr = &tempsource[0..new_source_length];
} else {
source_ptr = &tempsource[j..new_source_length];
}
}
let output_size = calculate_size(source_ptr, options);
let mut output = Vec::<u16>::with_capacity(max(output_size, source_ptr.len()));
output.extend_from_slice(source_ptr);
let mut spaces_countl: usize = 0;
let mut spaces_countr: usize = 0;
if (options & U_SHAPE_TEXT_DIRECTION_MASK) == U_SHAPE_TEXT_DIRECTION_LOGICAL {
count_spaces(&output, &mut spaces_countl, &mut spaces_countr);
invert_buffer(&mut output, spaces_countl, spaces_countr);
}
match options & U_SHAPE_LETTERS_MASK {
U_SHAPE_LETTERS_SHAPE => {
if ((options & U_SHAPE_TASHKEEL_MASK) > 0)
&& ((options & U_SHAPE_TASHKEEL_MASK) != U_SHAPE_TASHKEEL_REPLACE_BY_TATWEEL)
{
_shape_arabic(&mut output, 2);
} else {
_shape_arabic(&mut output, 1);
if (options & U_SHAPE_TASHKEEL_MASK) == U_SHAPE_TASHKEEL_REPLACE_BY_TATWEEL {
handle_tashkeel_with_tatweel(&mut output);
}
}
}
U_SHAPE_LETTERS_SHAPE_TASHKEEL_ISOLATED => {
_shape_arabic(&mut output, 0);
}
_ => {
}
}
if (options & U_SHAPE_TEXT_DIRECTION_MASK) == U_SHAPE_TEXT_DIRECTION_LOGICAL {
count_spaces(&output, &mut spaces_countl, &mut spaces_countr);
invert_buffer(&mut output, spaces_countl, spaces_countr);
}
let mut arabic_output = Vec::<u16>::with_capacity(output_size);
for ch in output {
if ch != LAMALEF_SPACE_SUB && ch != TASHKEEL_SPACE_SUB {
arabic_output.push(ch);
}
}
arabic_output
}