/*
 * Copyright 2021 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::{fmt, sync::Arc};

use crate::{
    Exceptions,
    common::{CharacterSet, ECIInput, Eci, MinimalECIInput, Result},
};

use super::{SymbolShapeHint, high_level_encoder};

const ISO_8859_1_ENCODER: CharacterSet = CharacterSet::ISO8859_1;

/**
 * Encoder that encodes minimally
 *
 * Algorithm:
 *
 * Uses Dijkstra to produce mathematically minimal encodings that are in some cases smaller than the results produced
 * by the algorithm described in annex S in the specification ISO/IEC 16022:200(E). The biggest improvment of this
 * algorithm over that one is the case when the algorithm enters the most inefficient mode, the B256 Mode:: The
 * algorithm from the specification algorithm will exit this mode only if it encounters digits so that arbitrarily
 * inefficient results can be produced if the postfix contains no digits.
 *
 * Multi ECI support and ECI switching:
 *
 * For multi language content the algorithm selects the most compact representation using ECI modes. Note that unlike
 * the compaction algorithm used for QR-Codes, this implementation operates in two stages and therfore is not
 * mathematically optimal. In the first stage, the input string is encoded minimally as a stream of ECI character set
 * selectors and bytes encoded in the selected encoding. In this stage the algorithm might for example decide to
 * encode ocurrences of the characters "\u0150\u015C" (O-double-acute, S-circumflex) in UTF-8 by a single ECI or
 * alternatively by multiple ECIs that switch between IS0-8859-2 and ISO-8859-3 (e.g. in the case that the input
 * contains many * characters from ISO-8859-2 (Latin 2) and few from ISO-8859-3 (Latin 3)).
 * In a second stage this stream of ECIs and bytes is minimally encoded using the various Data Matrix encoding modes.
 * While both stages encode mathematically minimally it is not ensured that the result is mathematically minimal since
 * the size growth for inserting an ECI in the first stage can only be approximated as the first stage does not know
 * in which mode the ECI will occur in the second stage (may, or may not require an extra latch to ASCII depending on
 * the current mode). The reason for this shortcoming are difficulties in implementing it in a straightforward and
 * readable manner.
 *
 * GS1 support
 *
 * FNC1 delimiters can be encoded in the input string by using the FNC1 character specified in the encoding function.
 * When a FNC1 character is specified then a leading FNC1 will be encoded and all ocurrences of delimiter characters
 * while result in FNC1 codewords in the symbol.
 *
 * @author Alex Geller
 */

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Mode {
    Ascii,
    C40,
    Text,
    X12,
    Edf,
    B256,
}

impl Mode {
    pub fn ordinal(&self) -> usize {
        match self {
            Mode::Ascii => 0,
            Mode::C40 => 1,
            Mode::Text => 2,
            Mode::X12 => 3,
            Mode::Edf => 4,
            Mode::B256 => 5,
        }
    }
}

const C40_SHIFT2_CHARS: [char; 27] = [
    '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=',
    '>', '?', '@', '[', '\\', ']', '^', '_',
];

pub fn isExtendedASCII(ch: char, fnc1: Option<char>) -> bool {
    let is_fnc1 = if let Some(fnc1) = fnc1 {
        ch != fnc1
    } else {
        true
    };
    is_fnc1 && ch as u8 >= 128 //&& ch as u8 <= 255
    // return ch != fnc1 && ch as u8 >= 128 && ch as u8 <= 255;
}

#[inline]
fn isInC40Shift1Set(ch: char) -> bool {
    ch as u8 <= 31
}

fn isInC40Shift2Set(ch: char, fnc1: Option<char>) -> bool {
    for c40Shift2Char in C40_SHIFT2_CHARS {
        // for (char c40Shift2Char : C40_SHIFT2_CHARS) {
        if c40Shift2Char == ch {
            return true;
        }
    }
    if let Some(fnc1) = fnc1 {
        ch == fnc1
    } else {
        false
    }
    // return ch as u8 as i32 == fnc1;
}

fn isInTextShift1Set(ch: char) -> bool {
    isInC40Shift1Set(ch)
}

fn isInTextShift2Set(ch: char, fnc1: Option<char>) -> bool {
    isInC40Shift2Set(ch, fnc1)
}

/**
 * Performs message encoding of a DataMatrix message
 *
 * @param msg the message
 * @return the encoded message (the char values range from 0 to 255)
 */
pub fn encodeHighLevel(msg: &str) -> Result<String> {
    encodeHighLevelWithDetails(msg, None, None, SymbolShapeHint::FORCE_NONE)
}

/**
 * Performs message encoding of a DataMatrix message
 *
 * @param msg the message
 * @param priorityCharset The preferred {@link Charset}. When the value of the argument is null, the algorithm
 *   chooses charsets that leads to a minimal representation. Otherwise the algorithm will use the priority
 *   charset to encode any character in the input that can be encoded by it if the charset is among the
 *   supported charsets.
 * @param fnc1 denotes the character in the input that represents the FNC1 character or -1 if this is not a GS1
 *   bar code. If the value is not -1 then a FNC1 is also prepended.
 * @param shape requested shape.
 * @return the encoded message (the char values range from 0 to 255)
 */
pub fn encodeHighLevelWithDetails(
    msg: &str,
    priorityCharset: Option<CharacterSet>,
    fnc1: Option<char>,
    shape: SymbolShapeHint,
) -> Result<String> {
    let mut msg = msg;
    let mut macroId = 0;
    if msg.starts_with(high_level_encoder::MACRO_05_HEADER)
        && msg.ends_with(high_level_encoder::MACRO_TRAILER)
    {
        macroId = 5;
        // msg = msg.substring(high_level_encoder::MACRO_05_HEADER.len(), msg.len() - 2);
        msg = &msg[high_level_encoder::MACRO_05_HEADER.chars().count()..(msg.chars().count() - 2)];
    } else if msg.starts_with(high_level_encoder::MACRO_06_HEADER)
        && msg.ends_with(high_level_encoder::MACRO_TRAILER)
    {
        macroId = 6;
        // msg = msg.substring(high_level_encoder::MACRO_06_HEADER.len(), msg.len() - 2);
        msg = &msg[high_level_encoder::MACRO_06_HEADER.chars().count()..(msg.chars().count() - 2)];
    }
    Ok(ISO_8859_1_ENCODER
        .decode(&encode(msg, priorityCharset, fnc1, shape, macroId)?)
        .expect("should decode"))
    // return new String(encode(msg, priorityCharset, fnc1, shape, macroId), StandardCharsets.ISO_8859_1);
}

/**
 * Encodes input minimally and returns an array of the codewords
 *
 * @param input The string to encode
 * @param priorityCharset The preferred {@link Charset}. When the value of the argument is null, the algorithm
 *   chooses charsets that leads to a minimal representation. Otherwise the algorithm will use the priority
 *   charset to encode any character in the input that can be encoded by it if the charset is among the
 *   supported charsets.
 * @param fnc1 denotes the character in the input that represents the FNC1 character or -1 if this is not a GS1
 *   bar code. If the value is not -1 then a FNC1 is also prepended.
 * @param shape requested shape.
 * @param macroId Prepends the specified macro function in case that a value of 5 or 6 is specified.
 * @return An array of bytes representing the codewords of a minimal encoding.
 */
fn encode(
    input: &str,
    priorityCharset: Option<CharacterSet>,
    fnc1: Option<char>,
    shape: SymbolShapeHint,
    macroId: i32,
) -> Result<Vec<u8>> {
    Ok(encodeMinimally(Arc::new(Input::new(
        input,
        priorityCharset,
        fnc1,
        shape,
        macroId,
    )))?
    .getBytes()
    .to_vec())
}

fn addEdge(edges: &mut [Vec<Option<Arc<Edge>>>], edge: Arc<Edge>) -> Result<()> {
    let vertexIndex = (edge.fromPosition + edge.characterLength) as usize;
    if edges[vertexIndex][edge.getEndMode()?.ordinal()].is_none()
        || edges[vertexIndex][edge.getEndMode()?.ordinal()]
            .as_ref()
            .ok_or(Exceptions::ILLEGAL_STATE)?
            .cachedTotalSize
            > edge.cachedTotalSize
    {
        edges[vertexIndex][edge.getEndMode()?.ordinal()] = Some(edge.clone());
    }
    Ok(())
}

/** @return the number of words in which the string starting at from can be encoded in c40 or text Mode::
 *  The number of characters encoded is returned in characterLength.
 *  The number of characters encoded is also minimal in the sense that the algorithm stops as soon
 *  as a character encoding fills a C40 word competely (three C40 values). An exception is at the
 *  end of the string where two C40 values are allowed (according to the spec the third c40 value
 *  is filled  with 0 (Shift 1) in this case).
 */
fn getNumberOfC40Words(
    input: Arc<Input>,
    from: u32,
    c40: bool,
    characterLength: &mut [u32],
) -> Result<u32> {
    let mut thirdsCount = 0;
    for i in (from as usize)..input.length() {
        // for (int i = from; i < input.length(); i++) {
        if input.isECI(i as u32)? {
            characterLength[0] = 0;
            return Ok(0);
        }
        let ci = input.charAt(i)?;
        if c40 && high_level_encoder::isNativeC40(ci)
            || !c40 && high_level_encoder::isNativeText(ci)
        {
            thirdsCount += 1; //native
        } else if !isExtendedASCII(ci, input.getFNC1Character()) {
            thirdsCount += 2; //shift
        } else {
            let asciiValue = ci as u8;
            if asciiValue >= 128
                && (c40 && high_level_encoder::isNativeC40((asciiValue - 128) as char)
                    || !c40 && high_level_encoder::isNativeText((asciiValue - 128) as char))
            {
                thirdsCount += 3; // shift, Upper shift
            } else {
                thirdsCount += 4; // shift, Upper shift, shift
            }
        }

        if thirdsCount % 3 == 0 || ((thirdsCount - 2) % 3 == 0 && i + 1 == input.length()) {
            characterLength[0] = i as u32 - from + 1;
            // return (int) Math.ceil(((double) thirdsCount) / 3.0);
            return Ok(((thirdsCount as f64) / 3.0).ceil() as u32);
        }
    }
    characterLength[0] = 0;

    Ok(0)
}

fn addEdges(
    input: Arc<Input>,
    edges: &mut [Vec<Option<Arc<Edge>>>],
    from: u32,
    previous: Option<Arc<Edge>>,
) -> Result<()> {
    if input.isECI(from)? {
        addEdge(
            edges,
            Arc::new(Edge::new(input, Mode::Ascii, from, 1, previous)?),
        )?;
        return Ok(());
    }

    let ch = input.charAt(from as usize)?;
    if previous.is_none() || previous.as_ref().unwrap().getEndMode()? != Mode::Edf {
        //not possible to unlatch a full EDF edge to something
        //else
        if high_level_encoder::isDigit(ch)
            && input.haveNCharacters(from as usize, 2)?
            && high_level_encoder::isDigit(input.charAt(from as usize + 1)?)
        {
            // two digits ASCII encoded
            addEdge(
                edges,
                Arc::new(Edge::new(
                    input.clone(),
                    Mode::Ascii,
                    from,
                    2,
                    previous.clone(),
                )?),
            )?;
        } else {
            // one ASCII encoded character or an extended character via Upper Shift
            addEdge(
                edges,
                Arc::new(Edge::new(
                    input.clone(),
                    Mode::Ascii,
                    from,
                    1,
                    previous.clone(),
                )?),
            )?;
        }

        let modes = [Mode::C40, Mode::Text];
        for mode in modes {
            // for (Mode mode : modes) {
            let mut characterLength = [0u32; 1];
            if getNumberOfC40Words(input.clone(), from, mode == Mode::C40, &mut characterLength)?
                > 0
            {
                addEdge(
                    edges,
                    Arc::new(Edge::new(
                        input.clone(),
                        mode,
                        from,
                        characterLength[0],
                        previous.clone(),
                    )?),
                )?;
            }
        }

        if input.haveNCharacters(from as usize, 3)?
            && high_level_encoder::isNativeX12(input.charAt(from as usize)?)
            && high_level_encoder::isNativeX12(input.charAt(from as usize + 1)?)
            && high_level_encoder::isNativeX12(input.charAt(from as usize + 2)?)
        {
            addEdge(
                edges,
                Arc::new(Edge::new(
                    input.clone(),
                    Mode::X12,
                    from,
                    3,
                    previous.clone(),
                )?),
            )?;
        }

        addEdge(
            edges,
            Arc::new(Edge::new(
                input.clone(),
                Mode::B256,
                from,
                1,
                previous.clone(),
            )?),
        )?;
    }

    //We create 4 EDF edges,  with 1, 2 3 or 4 characters length. The fourth normally doesn't have a latch to ASCII
    //unless it is 2 characters away from the end of the input.
    let mut i: u32 = 0;
    while i < 3 {
        // for (i = 0; i < 3; i++) {
        let pos = from + i;
        if input.haveNCharacters(pos as usize, 1)?
            && high_level_encoder::isNativeEDIFACT(input.charAt(pos as usize)?)
        {
            addEdge(
                edges,
                Arc::new(Edge::new(
                    input.clone(),
                    Mode::Edf,
                    from,
                    i + 1,
                    previous.clone(),
                )?),
            )?;
        } else {
            break;
        }
        i += 1;
    }
    if i == 3
        && input.haveNCharacters(from as usize, 4)?
        && high_level_encoder::isNativeEDIFACT(input.charAt(from as usize + 3)?)
    {
        addEdge(
            edges,
            Arc::new(Edge::new(input, Mode::Edf, from, 4, previous)?),
        )?;
    }
    Ok(())
}

fn encodeMinimally(input: Arc<Input>) -> Result<RXingResult> {
    // @SuppressWarnings("checkstyle:lineLength")
    /* The minimal encoding is computed by Dijkstra. The acyclic graph is modeled as follows:
     * A vertex represents a combination of a position in the input and an encoding mode where position 0
     * denotes the position left of the first character, 1 the position left of the second character and so on.
     * Likewise the end vertices are located after the last character at position input.length().
     * For any position there might be up to six vertices, one for each of the encoding types ASCII, C40, TEXT, X12,
     * EDF and B256.
     *
     * As an example consider the input string "ABC123" then at position 0 there is only one vertex with the default
     * ASCII encodation. At position 3 there might be vertices for the types ASCII, C40, X12, EDF and B256.
     *
     * An edge leading to such a vertex encodes one or more of the characters left of the position that the vertex
     * represents. It encodes the characters in the encoding mode of the vertex that it ends on. In other words,
     * all edges leading to a particular vertex encode the same characters (the length of the suffix can vary) using the same
     * encoding Mode::
     * As an example consider the input string "ABC123" and the vertex (4,EDF). Possible edges leading to this vertex
     * are:
     *   (0,ASCII)  --EDF(ABC1)--> (4,EDF)
     *   (1,ASCII)  --EDF(BC1)-->  (4,EDF)
     *   (1,B256)   --EDF(BC1)-->  (4,EDF)
     *   (1,EDF)    --EDF(BC1)-->  (4,EDF)
     *   (2,ASCII)  --EDF(C1)-->   (4,EDF)
     *   (2,B256)   --EDF(C1)-->   (4,EDF)
     *   (2,EDF)    --EDF(C1)-->   (4,EDF)
     *   (3,ASCII)  --EDF(1)-->    (4,EDF)
     *   (3,B256)   --EDF(1)-->    (4,EDF)
     *   (3,EDF)    --EDF(1)-->    (4,EDF)
     *   (3,C40)    --EDF(1)-->    (4,EDF)
     *   (3,X12)    --EDF(1)-->    (4,EDF)
     *
     * The edges leading to a vertex are stored in such a way that there is a fast way to enumerate the edges ending
     * on a particular vertex.
     *
     * The algorithm processes the vertices in order of their position thereby performing the following:
     *
     * For every vertex at position i the algorithm enumerates the edges ending on the vertex and removes all but the
     * shortest from that list.
     * Then it processes the vertices for the position i+1. If i+1 == input.length() then the algorithm ends
     * and chooses the the edge with the smallest size from any of the edges leading to vertices at this position.
     * Otherwise the algorithm computes all possible outgoing edges for the vertices at the position i+1
     *
     * Examples:
     * The process is illustrated by showing the graph (edges) after each iteration from left to right over the input:
     * An edge is drawn as follows "(" + fromVertex + ") -- " + encodingMode + "(" + encodedInput + ") (" +
     * accumulatedSize + ") --> (" + toVertex + ")"
     *
     * Example 1 encoding the string "ABCDEFG":
     *
     *
     * Situation after adding edges to the start vertex (0,ASCII)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII)
     * (0,ASCII) B256(A) (3) --> (1,B256)
     * (0,ASCII) EDF(AB) (4) --> (2,EDF)
     * (0,ASCII) C40(ABC) (3) --> (3,C40)
     * (0,ASCII) TEXT(ABC) (5) --> (3,TEXT)
     * (0,ASCII) X12(ABC) (3) --> (3,X12)
     * (0,ASCII) EDF(ABC) (4) --> (3,EDF)
     * (0,ASCII) EDF(ABCD) (4) --> (4,EDF)
     *
     * Situation after adding edges to vertices at position 1
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII)
     * (0,ASCII) B256(A) (3) --> (1,B256)
     * (0,ASCII) EDF(AB) (4) --> (2,EDF)
     * (0,ASCII) C40(ABC) (3) --> (3,C40)
     * (0,ASCII) TEXT(ABC) (5) --> (3,TEXT)
     * (0,ASCII) X12(ABC) (3) --> (3,X12)
     * (0,ASCII) EDF(ABC) (4) --> (3,EDF)
     * (0,ASCII) EDF(ABCD) (4) --> (4,EDF)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) B256(B) (4) --> (2,B256)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) EDF(BC) (5) --> (3,EDF)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) C40(BCD) (4) --> (4,C40)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) TEXT(BCD) (6) --> (4,TEXT)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) X12(BCD) (4) --> (4,X12)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) EDF(BCD) (5) --> (4,EDF)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) EDF(BCDE) (5) --> (5,EDF)
     * (0,ASCII) B256(A) (3) --> (1,B256) ASCII(B) (4) --> (2,ASCII)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256)
     * (0,ASCII) B256(A) (3) --> (1,B256) EDF(BC) (6) --> (3,EDF)
     * (0,ASCII) B256(A) (3) --> (1,B256) C40(BCD) (5) --> (4,C40)
     * (0,ASCII) B256(A) (3) --> (1,B256) TEXT(BCD) (7) --> (4,TEXT)
     * (0,ASCII) B256(A) (3) --> (1,B256) X12(BCD) (5) --> (4,X12)
     * (0,ASCII) B256(A) (3) --> (1,B256) EDF(BCD) (6) --> (4,EDF)
     * (0,ASCII) B256(A) (3) --> (1,B256) EDF(BCDE) (6) --> (5,EDF)
     *
     * Edge "(1,ASCII) ASCII(B) (2) --> (2,ASCII)" is minimal for the vertex (2,ASCII) so that edge "(1,B256) ASCII(B) (4) --> (2,ASCII)" is removed.
     * Edge "(1,B256) B256(B) (3) --> (2,B256)" is minimal for the vertext (2,B256) so that the edge "(1,ASCII) B256(B) (4) --> (2,B256)" is removed.
     *
     * Situation after adding edges to vertices at position 2
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII)
     * (0,ASCII) B256(A) (3) --> (1,B256)
     * (0,ASCII) EDF(AB) (4) --> (2,EDF)
     * (0,ASCII) C40(ABC) (3) --> (3,C40)
     * (0,ASCII) TEXT(ABC) (5) --> (3,TEXT)
     * (0,ASCII) X12(ABC) (3) --> (3,X12)
     * (0,ASCII) EDF(ABC) (4) --> (3,EDF)
     * (0,ASCII) EDF(ABCD) (4) --> (4,EDF)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) EDF(BC) (5) --> (3,EDF)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) C40(BCD) (4) --> (4,C40)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) TEXT(BCD) (6) --> (4,TEXT)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) X12(BCD) (4) --> (4,X12)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) EDF(BCD) (5) --> (4,EDF)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) EDF(BCDE) (5) --> (5,EDF)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256)
     * (0,ASCII) B256(A) (3) --> (1,B256) EDF(BC) (6) --> (3,EDF)
     * (0,ASCII) B256(A) (3) --> (1,B256) C40(BCD) (5) --> (4,C40)
     * (0,ASCII) B256(A) (3) --> (1,B256) TEXT(BCD) (7) --> (4,TEXT)
     * (0,ASCII) B256(A) (3) --> (1,B256) X12(BCD) (5) --> (4,X12)
     * (0,ASCII) B256(A) (3) --> (1,B256) EDF(BCD) (6) --> (4,EDF)
     * (0,ASCII) B256(A) (3) --> (1,B256) EDF(BCDE) (6) --> (5,EDF)
     * (0,ASCII) EDF(AB) (4) --> (2,EDF) ASCII(C) (5) --> (3,ASCII)
     * (0,ASCII) EDF(AB) (4) --> (2,EDF) B256(C) (6) --> (3,B256)
     * (0,ASCII) EDF(AB) (4) --> (2,EDF) EDF(CD) (7) --> (4,EDF)
     * (0,ASCII) EDF(AB) (4) --> (2,EDF) C40(CDE) (6) --> (5,C40)
     * (0,ASCII) EDF(AB) (4) --> (2,EDF) TEXT(CDE) (8) --> (5,TEXT)
     * (0,ASCII) EDF(AB) (4) --> (2,EDF) X12(CDE) (6) --> (5,X12)
     * (0,ASCII) EDF(AB) (4) --> (2,EDF) EDF(CDE) (7) --> (5,EDF)
     * (0,ASCII) EDF(AB) (4) --> (2,EDF) EDF(CDEF) (7) --> (6,EDF)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) ASCII(C) (3) --> (3,ASCII)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) B256(C) (5) --> (3,B256)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) EDF(CD) (6) --> (4,EDF)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) C40(CDE) (5) --> (5,C40)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) TEXT(CDE) (7) --> (5,TEXT)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) X12(CDE) (5) --> (5,X12)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) EDF(CDE) (6) --> (5,EDF)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) EDF(CDEF) (6) --> (6,EDF)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) ASCII(C) (4) --> (3,ASCII)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) B256(C) (4) --> (3,B256)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) EDF(CD) (6) --> (4,EDF)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) C40(CDE) (5) --> (5,C40)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) TEXT(CDE) (7) --> (5,TEXT)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) X12(CDE) (5) --> (5,X12)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) EDF(CDE) (6) --> (5,EDF)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) EDF(CDEF) (6) --> (6,EDF)
     *
     * Edge "(2,ASCII) ASCII(C) (3) --> (3,ASCII)" is minimal for the vertex (3,ASCII) so that edges "(2,EDF) ASCII(C) (5) --> (3,ASCII)"
     * and "(2,B256) ASCII(C) (4) --> (3,ASCII)" can be removed.
     * Edge "(0,ASCII) EDF(ABC) (4) --> (3,EDF)" is minimal for the vertex (3,EDF) so that edges "(1,ASCII) EDF(BC) (5) --> (3,EDF)"
     * and "(1,B256) EDF(BC) (6) --> (3,EDF)" can be removed.
     * Edge "(2,B256) B256(C) (4) --> (3,B256)" is minimal for the vertex (3,B256) so that edges "(2,ASCII) B256(C) (5) --> (3,B256)"
     * and "(2,EDF) B256(C) (6) --> (3,B256)" can be removed.
     *
     * This continues for vertices 3 thru 7
     *
     * Situation after adding edges to vertices at position 7
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII)
     * (0,ASCII) B256(A) (3) --> (1,B256)
     * (0,ASCII) EDF(AB) (4) --> (2,EDF)
     * (0,ASCII) C40(ABC) (3) --> (3,C40)
     * (0,ASCII) TEXT(ABC) (5) --> (3,TEXT)
     * (0,ASCII) X12(ABC) (3) --> (3,X12)
     * (0,ASCII) EDF(ABC) (4) --> (3,EDF)
     * (0,ASCII) EDF(ABCD) (4) --> (4,EDF)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) C40(BCD) (4) --> (4,C40)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) TEXT(BCD) (6) --> (4,TEXT)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) X12(BCD) (4) --> (4,X12)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) EDF(BCDE) (5) --> (5,EDF)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256)
     * (0,ASCII) C40(ABC) (3) --> (3,C40) C40(DEF) (5) --> (6,C40)
     * (0,ASCII) X12(ABC) (3) --> (3,X12) X12(DEF) (5) --> (6,X12)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) ASCII(C) (3) --> (3,ASCII)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) C40(CDE) (5) --> (5,C40)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) TEXT(CDE) (7) --> (5,TEXT)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) X12(CDE) (5) --> (5,X12)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) EDF(CDEF) (6) --> (6,EDF)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) C40(BCD) (4) --> (4,C40) C40(EFG) (6) --> (7,C40)    //Solution 1
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) X12(BCD) (4) --> (4,X12) X12(EFG) (6) --> (7,X12)    //Solution 2
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) B256(C) (4) --> (3,B256)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) ASCII(C) (3) --> (3,ASCII) ASCII(D) (4) --> (4,ASCII)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) ASCII(C) (3) --> (3,ASCII) TEXT(DEF) (8) --> (6,TEXT)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) ASCII(C) (3) --> (3,ASCII) EDF(DEFG) (7) --> (7,EDF)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) B256(C) (4) --> (3,B256) B256(D) (5) --> (4,B256)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) ASCII(C) (3) --> (3,ASCII) ASCII(D) (4) --> (4,ASCII) ASCII(E) (5) --> (5,ASCII)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) ASCII(C) (3) --> (3,ASCII) ASCII(D) (4) --> (4,ASCII) TEXT(EFG) (9) --> (7,TEXT)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) B256(C) (4) --> (3,B256) B256(D) (5) --> (4,B256) B256(E) (6) --> (5,B256)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) ASCII(C) (3) --> (3,ASCII) ASCII(D) (4) --> (4,ASCII) ASCII(E) (5) --> (5,ASCII) ASCII(F) (6) --> (6,ASCII)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) B256(C) (4) --> (3,B256) B256(D) (5) --> (4,B256) B256(E) (6) --> (5,B256) B256(F) (7) --> (6,B256)
     * (0,ASCII) ASCII(A) (1) --> (1,ASCII) ASCII(B) (2) --> (2,ASCII) ASCII(C) (3) --> (3,ASCII) ASCII(D) (4) --> (4,ASCII) ASCII(E) (5) --> (5,ASCII) ASCII(F) (6) --> (6,ASCII) ASCII(G) (7) --> (7,ASCII)
     * (0,ASCII) B256(A) (3) --> (1,B256) B256(B) (3) --> (2,B256) B256(C) (4) --> (3,B256) B256(D) (5) --> (4,B256) B256(E) (6) --> (5,B256) B256(F) (7) --> (6,B256) B256(G) (8) --> (7,B256)
     *
     * Hence a minimal encoding of "ABCDEFG" is either ASCII(A),C40(BCDEFG) or ASCII(A), X12(BCDEFG) with a size of 5 bytes.
     */

    let inputLength = input.length();

    // Array that represents vertices. There is a vertex for every character and Mode::
    // The last dimension in the array below encodes the 6 modes ASCII, C40, TEXT, X12, EDF and B256
    // let edges = new Edge[inputLength + 1][6];
    let mut edges = vec![vec![None; 6]; inputLength + 1];
    addEdges(input.clone(), &mut edges, 0, None)?;

    for i in 1..=inputLength {
        for j in 0..6 {
            if edges[i][j].is_some() && i < inputLength {
                let edge = edges[i][j].clone();
                addEdges(input.clone(), &mut edges, i as u32, edge)?;
            }
        }
        //optimize memory by removing edges that have been passed.
        edges[i - 1][..6].fill(None);
    }

    let mut minimalJ: i32 = -1;
    let mut minimalSize = i32::MAX;
    for j in 0..6 {
        if let Some(edge) = &edges[inputLength][j] {
            // if edges[inputLength][j].is_some() {
            // let edge = edges[inputLength][j].as_ref().unwrap();
            let size = if (1..=3).contains(&j) {
                edge.cachedTotalSize + 1
            } else {
                edge.cachedTotalSize
            }; //C40, TEXT and X12 need an
            // extra unlatch at the end
            if (size as i32) < minimalSize {
                minimalSize = size as i32;
                minimalJ = j as i32;
            }
        }
    }

    if minimalJ < 0 {
        return Err(Exceptions::illegal_state_with(format!(
            "Internal error: failed to encode \"{input}\""
        )));
    }
    RXingResult::new(edges[inputLength][minimalJ as usize].clone())
}

const ALL_CODEWORD_CAPACITIES: [u32; 28] = [
    3, 5, 8, 10, 12, 16, 18, 22, 30, 32, 36, 44, 49, 62, 86, 114, 144, 174, 204, 280, 368, 456,
    576, 696, 816, 1050, 1304, 1558,
];
const SQUARE_CODEWORD_CAPACITIES: [u32; 24] = [
    3, 5, 8, 12, 18, 22, 30, 36, 44, 62, 86, 114, 144, 174, 204, 280, 368, 456, 576, 696, 816,
    1050, 1304, 1558,
];
const RECTANGULAR_CODEWORD_CAPACITIES: [u32; 6] = [5, 10, 16, 33, 32, 49];

struct Edge {
    input: Arc<Input>,
    mode: Mode, //the mode at the start of this edge.
    fromPosition: u32,
    characterLength: u32,
    previous: Option<Arc<Edge>>,
    cachedTotalSize: u32,
}
impl Edge {
    fn new(
        input: Arc<Input>,
        mode: Mode,
        fromPosition: u32,
        characterLength: u32,
        previous: Option<Arc<Edge>>,
    ) -> Result<Self> {
        if fromPosition + characterLength > input.length() as u32 {
            return Err(Exceptions::FORMAT);
        }

        let mut size = if let Some(previous) = previous.clone() {
            previous.cachedTotalSize
        } else {
            0
        };

        let previousMode = Self::getPreviousMode(previous.clone())?;

        /*
         * Switching modes
         * ASCII -> C40: latch 230
         * ASCII -> TEXT: latch 239
         * ASCII -> X12: latch 238
         * ASCII -> EDF: latch 240
         * ASCII -> B256: latch 231
         * C40 -> ASCII: word(c1,c2,c3), 254
         * TEXT -> ASCII: word(c1,c2,c3), 254
         * X12 -> ASCII: word(c1,c2,c3), 254
         * EDIFACT -> ASCII: Unlatch character,0,0,0 or c1,Unlatch character,0,0 or c1,c2,Unlatch character,0 or
         * c1,c2,c3,Unlatch character
         * B256 -> ASCII: without latch after n bytes
         */
        match mode {
            Mode::Ascii => {
                size += 1;
                if input.isECI(fromPosition)?
                    || isExtendedASCII(
                        input.charAt(fromPosition as usize)?,
                        input.getFNC1Character(),
                    )
                {
                    size += 1;
                }
                if previousMode == Mode::C40
                    || previousMode == Mode::Text
                    || previousMode == Mode::X12
                {
                    size += 1; // unlatch 254 to ASCII
                }
            }
            Mode::B256 => {
                size += 1;
                if previousMode != Mode::B256 || Self::getB256Size(mode, previous.clone()) == 250 {
                    size += 1; //byte count
                }
                // } else if Self::getB256Size(mode, previous.clone()) == 250 {
                //     size += 1; //extra byte count
                // }
                if previousMode == Mode::Ascii {
                    size += 1; //latch to B256
                } else if previousMode == Mode::C40
                    || previousMode == Mode::Text
                    || previousMode == Mode::X12
                {
                    size += 2; //unlatch to ASCII, latch to B256
                }
            }
            Mode::C40 | Mode::Text | Mode::X12 => {
                if mode == Mode::X12 {
                    size += 2;
                } else {
                    let mut charLen = [0u32; 1];
                    size += getNumberOfC40Words(
                        input.clone(),
                        fromPosition,
                        mode == Mode::C40,
                        &mut charLen,
                    )? * 2;
                }

                if previousMode == Mode::Ascii || previousMode == Mode::B256 {
                    size += 1; //additional byte for latch from ASCII to this mode
                } else if previousMode != mode
                    && (previousMode == Mode::C40
                        || previousMode == Mode::Text
                        || previousMode == Mode::X12)
                {
                    size += 2; //unlatch 254 to ASCII followed by latch to this mode
                }
            }
            Mode::Edf => {
                size += 3;
                if previousMode == Mode::Ascii || previousMode == Mode::B256 {
                    size += 1; //additional byte for latch from ASCII to this mode
                } else if previousMode == Mode::C40
                    || previousMode == Mode::Text
                    || previousMode == Mode::X12
                {
                    size += 2; //unlatch 254 to ASCII followed by latch to this mode
                }
            }
        }
        Ok(Self {
            input,
            mode,
            fromPosition,
            characterLength,
            previous,
            cachedTotalSize: size,
        })
        // cachedTotalSize = size;
    }

    // does not count beyond 250
    pub fn getB256Size(mode: Mode, previous: Option<Arc<Edge>>) -> u32 {
        if mode != Mode::B256 {
            return 0;
        }
        let mut cnt = 1;
        let mut current = previous;
        while current.is_some() && current.as_ref().unwrap().mode == Mode::B256 && cnt <= 250 {
            cnt += 1;
            current.clone_from(&current.clone().as_ref().unwrap().previous);
        }
        // let cnt = 0;
        // Edge current = this;
        // while (current != null && current.mode == Mode::B256 && cnt <= 250) {
        //   cnt+=1;
        //   current = current.previous;
        // }
        cnt
    }

    pub fn getPreviousStartMode(previous: Option<Arc<Edge>>) -> Mode {
        if let Some(prev) = previous {
            prev.mode
        } else {
            Mode::Ascii
        }
        // if previous.is_none() { Mode::ASCII} else {previous.as_ref().unwrap().mode}
    }

    pub fn getPreviousMode(previous: Option<Arc<Edge>>) -> Result<Mode> {
        if let Some(prev) = previous {
            prev.getEndMode()
        } else {
            Ok(Mode::Ascii)
        }
        // return  previous == null ? Mode::ASCII : previous.getEndMode();
    }

    /** Returns Mode::ASCII in case that:
     *  - Mode is EDIFACT and characterLength is less than 4 or the remaining characters can be encoded in at most 2
     *    ASCII bytes.
     *  - Mode is C40, TEXT or X12 and the remaining characters can be encoded in at most 1 ASCII byte.
     *
     *  Returns mode in all other cases.
     * */
    pub fn getEndMode(&self) -> Result<Mode> {
        let mode = self.mode;
        if mode == Mode::Edf {
            if self.characterLength < 4 {
                return Ok(Mode::Ascii);
            }
            let lastASCII = Self::getLastASCII(self)?; // see 5.2.8.2 EDIFACT encodation Rules
            if lastASCII > 0
                && self.getCodewordsRemaining(self.cachedTotalSize + lastASCII) <= 2 - lastASCII
            {
                return Ok(Mode::Ascii);
            }
        }
        if mode == Mode::C40 || mode == Mode::Text || mode == Mode::X12 {
            // see 5.2.5.2 C40 encodation rules and 5.2.7.2 ANSI X12 encodation rules
            if self.fromPosition + self.characterLength >= self.input.length() as u32
                && self.getCodewordsRemaining(self.cachedTotalSize) == 0
            {
                return Ok(Mode::Ascii);
            }
            let lastASCII = Self::getLastASCII(self)?;
            if lastASCII == 1 && self.getCodewordsRemaining(self.cachedTotalSize + 1) == 0 {
                return Ok(Mode::Ascii);
            }
        }

        Ok(mode)
    }

    pub fn getMode(&self) -> Mode {
        self.mode
    }

    /** Peeks ahead and returns 1 if the postfix consists of exactly two digits, 2 if the postfix consists of exactly
     *  two consecutive digits and a non extended character or of 4 digits.
     *  Returns 0 in any other case
     **/
    pub fn getLastASCII(&self) -> Result<u32> {
        let length = self.input.length() as u32;
        let from = self.fromPosition + self.characterLength;
        if length - from > 4 || from >= length {
            return Ok(0);
        }
        if length - from == 1 {
            if isExtendedASCII(
                self.input.charAt(from as usize)?,
                self.input.getFNC1Character(),
            ) {
                return Ok(0);
            }
            return Ok(1);
        }
        if length - from == 2 {
            if isExtendedASCII(
                self.input.charAt(from as usize)?,
                self.input.getFNC1Character(),
            ) || isExtendedASCII(
                self.input.charAt(from as usize + 1)?,
                self.input.getFNC1Character(),
            ) {
                return Ok(0);
            }
            if high_level_encoder::isDigit(self.input.charAt(from as usize)?)
                && high_level_encoder::isDigit(self.input.charAt(from as usize + 1)?)
            {
                return Ok(1);
            }
            return Ok(2);
        }
        if length - from == 3 {
            if high_level_encoder::isDigit(self.input.charAt(from as usize)?)
                && high_level_encoder::isDigit(self.input.charAt(from as usize + 1)?)
                && !isExtendedASCII(
                    self.input.charAt(from as usize + 2)?,
                    self.input.getFNC1Character(),
                )
            {
                return Ok(2);
            }
            if high_level_encoder::isDigit(self.input.charAt(from as usize + 1)?)
                && high_level_encoder::isDigit(self.input.charAt(from as usize + 2)?)
                && !isExtendedASCII(
                    self.input.charAt(from as usize)?,
                    self.input.getFNC1Character(),
                )
            {
                return Ok(2);
            }
            return Ok(0);
        }
        if high_level_encoder::isDigit(self.input.charAt(from as usize)?)
            && high_level_encoder::isDigit(self.input.charAt(from as usize + 1)?)
            && high_level_encoder::isDigit(self.input.charAt(from as usize + 2)?)
            && high_level_encoder::isDigit(self.input.charAt(from as usize + 3)?)
        {
            return Ok(2);
        }

        Ok(0)
    }

    /** Returns the capacity in codewords of the smallest symbol that has enough capacity to fit the given minimal
     * number of codewords.
     **/
    pub fn getMinSymbolSize(&self, minimum: u32) -> u32 {
        match self.input.getShapeHint() {
            SymbolShapeHint::FORCE_SQUARE => {
                for capacity in SQUARE_CODEWORD_CAPACITIES {
                    // for (int capacity : squareCodewordCapacities) {
                    if capacity >= minimum {
                        return capacity;
                    }
                }
            }
            SymbolShapeHint::FORCE_RECTANGLE => {
                for capacity in RECTANGULAR_CODEWORD_CAPACITIES {
                    // for (int capacity : rectangularCodewordCapacities) {
                    if capacity >= minimum {
                        return capacity;
                    }
                }
            }
            _ => {}
        }
        for capacity in ALL_CODEWORD_CAPACITIES {
            // for (int capacity : allCodewordCapacities) {
            if capacity >= minimum {
                return capacity;
            }
        }

        ALL_CODEWORD_CAPACITIES[ALL_CODEWORD_CAPACITIES.len() - 1]
    }

    /** Returns the remaining capacity in codewords of the smallest symbol that has enough capacity to fit the given
     * minimal number of codewords.
     **/
    pub fn getCodewordsRemaining(&self, minimum: u32) -> u32 {
        Self::getMinSymbolSize(self, minimum) - minimum
    }

    #[inline]
    pub fn getBytes1(c: u32) -> Vec<u8> {
        // let result = vec![0u8;1];
        // result[0] =  c as u8;
        // result
        vec![c as u8]
    }

    #[inline]
    pub fn getBytes2(c1: u32, c2: u32) -> Vec<u8> {
        // byte[] result = new byte[2];
        // result[0] = (byte) c1;
        // result[1] = (byte) c2;
        // return result;
        vec![c1 as u8, c2 as u8]
    }

    pub fn setC40Word(bytes: &mut [u8], offset: u32, c1: u32, c2: u32, c3: u32) {
        let val16 = (1600 * (c1 & 0xff)) + (40 * (c2 & 0xff)) + (c3 & 0xff) + 1;
        bytes[offset as usize] = (val16 / 256) as u8;
        bytes[offset as usize + 1] = (val16 % 256) as u8;
    }

    fn getX12Value(c: char) -> u32 {
        let c = c as u32;
        if c == 13 {
            0
        } else if c == 42 {
            1
        } else if c == 62 {
            2
        } else if c == 32 {
            3
        } else if (48..=57).contains(&c) {
            c - 44
        } else if (65..=90).contains(&c) {
            c - 51
        } else {
            c
        }
    }

    pub fn getX12Words(&self) -> Result<Vec<u8>> {
        assert!(self.characterLength % 3 == 0);
        let mut result = vec![0u8; self.characterLength as usize / 3 * 2];
        let mut i = 0;
        while i < result.len() {
            // for (int i = 0; i < result.length; i += 2) {
            Self::setC40Word(
                &mut result,
                i as u32,
                Self::getX12Value(self.input.charAt(self.fromPosition as usize + i / 2 * 3)?),
                Self::getX12Value(
                    self.input
                        .charAt(self.fromPosition as usize + i / 2 * 3 + 1)?,
                ),
                Self::getX12Value(
                    self.input
                        .charAt(self.fromPosition as usize + i / 2 * 3 + 2)?,
                ),
            );
            i += 2;
        }
        Ok(result)
    }

    pub fn getShiftValue(c: char, c40: bool, fnc1: Option<char>) -> u32 {
        if c40 && isInC40Shift1Set(c) || !c40 && isInTextShift1Set(c) {
            0
        } else if c40 && isInC40Shift2Set(c, fnc1) || !c40 && isInTextShift2Set(c, fnc1) {
            1
        } else {
            2
        }
    }

    fn getC40Value(c40: bool, setIndex: u32, c: char, fnc1: Option<char>) -> u32 {
        if let Some(fnc1_char) = fnc1 {
            if c == fnc1_char {
                assert!(setIndex == 2);
                return 27;
            }
        }
        if c40 {
            let c = c as u32;
            if c <= 31 {
                c
            } else if c == 32 {
                3
            } else if c <= 47 {
                c - 33
            } else if c <= 57 {
                c - 44
            } else if c <= 64 {
                c - 43
            } else if c <= 90 {
                c - 51
            } else if c <= 95 {
                c - 69
            } else if c <= 127 {
                c - 96
            } else {
                c
            }
        } else {
            let c = c as u32;
            if c == 0 {
                0
            } else if setIndex == 0 && c <= 3 {
                c - 1
            } else if
            //is this a bug in the spec?
            setIndex == 1 && c <= 31 {
                c
            } else if c == 32 {
                3
            } else if (33..=47).contains(&c) {
                c - 33
            } else if (48..=57).contains(&c) {
                c - 44
            } else if (58..=64).contains(&c) {
                c - 43
            } else if (65..=90).contains(&c) {
                c - 64
            } else if (91..=95).contains(&c) {
                c - 69
            } else if c == 96 {
                0
            } else if (97..=122).contains(&c) {
                c - 83
            } else if (123..=127).contains(&c) {
                c - 96
            } else {
                c
            }
        }
    }

    pub fn getC40Words(&self, c40: bool, fnc1: Option<char>) -> Result<Vec<u8>> {
        let mut c40Values: Vec<u8> = Vec::new();
        let fromPosition = self.fromPosition as usize;
        for i in 0..self.characterLength as usize {
            // for (int i = 0; i < characterLength; i++) {
            let ci = self.input.charAt(fromPosition + i)?;
            if c40 && high_level_encoder::isNativeC40(ci)
                || !c40 && high_level_encoder::isNativeText(ci)
            {
                c40Values.push(Self::getC40Value(c40, 0, ci, fnc1) as u8);
            } else if !isExtendedASCII(ci, fnc1) {
                let shiftValue = Self::getShiftValue(ci, c40, fnc1);
                c40Values.push(shiftValue as u8); //Shift[123]
                c40Values.push(Self::getC40Value(c40, shiftValue, ci, fnc1) as u8);
            } else {
                let asciiValue = (ci as u8 - 128) as char;
                if c40 && high_level_encoder::isNativeC40(asciiValue)
                    || !c40 && high_level_encoder::isNativeText(asciiValue)
                {
                    c40Values.push(1); //Shift 2
                    c40Values.push(30); //Upper Shift
                    c40Values.push(Self::getC40Value(c40, 0, asciiValue, fnc1) as u8);
                } else {
                    c40Values.push(1); //Shift 2
                    c40Values.push(30); //Upper Shift
                    let shiftValue = Self::getShiftValue(asciiValue, c40, fnc1);
                    c40Values.push(shiftValue as u8); // Shift[123]
                    c40Values.push(Self::getC40Value(c40, shiftValue, asciiValue, fnc1) as u8);
                }
            }
        }

        if (c40Values.len() % 3) != 0 {
            assert!(
                (c40Values.len() - 2) % 3 == 0
                    && fromPosition + self.characterLength as usize == self.input.length()
            );
            c40Values.push(0); // pad with 0 (Shift 1)
        }

        let mut result = vec![0u8; c40Values.len() / 3 * 2];
        let mut byteIndex = 0;
        let mut i = 0;
        while i < c40Values.len() {
            // for (int i = 0; i < c40Values.size(); i += 3) {
            Self::setC40Word(
                &mut result,
                byteIndex,
                c40Values[i] as u32,
                c40Values[i + 1] as u32,
                c40Values[i + 2] as u32,
            );
            byteIndex += 2;

            i += 3;
        }

        Ok(result)
    }

    pub fn getEDFBytes(&self) -> Result<Vec<u8>> {
        let numberOfThirds = (self.characterLength as f32 / 4.0).ceil() as usize;
        let mut result = vec![0u8; numberOfThirds * 3];
        let mut pos = self.fromPosition as usize;
        let endPos = (self.fromPosition as usize + self.characterLength as usize - 1)
            .min(self.input.length() - 1);
        let mut i = 0;
        while i < numberOfThirds {
            // for (int i = 0; i < numberOfThirds; i += 3) {
            let mut edfValues = [0u32; 4];
            for edfValue in &mut edfValues {
                // for j in 0..4 {
                // for (int j = 0; j < 4; j++) {
                if pos <= endPos {
                    *edfValue = self.input.charAt(pos)? as u32 & 0x3f;
                    pos += 1;
                } else {
                    *edfValue = if pos == endPos + 1 { 0x1f } else { 0 };
                }
            }
            let mut val24 = edfValues[0] << 18;
            val24 |= edfValues[1] << 12;
            val24 |= edfValues[2] << 6;
            val24 |= edfValues[3];
            result[i] = ((val24 >> 16) & 0xff) as u8;
            result[i + 1] = ((val24 >> 8) & 0xff) as u8;
            result[i + 2] = (val24 & 0xff) as u8;

            i += 3;
        }

        Ok(result)
    }

    pub fn getLatchBytes(&self) -> Result<Vec<u8>> {
        match Self::getPreviousMode(self.previous.clone())? {
            Mode::Ascii | Mode::B256 =>
            //after B256 ends (via length) we are back to ASCII
            {
                match self.mode {
                    Mode::B256 => return Ok(Self::getBytes1(231)),
                    Mode::C40 => return Ok(Self::getBytes1(230)),
                    Mode::Text => return Ok(Self::getBytes1(239)),
                    Mode::X12 => return Ok(Self::getBytes1(238)),
                    Mode::Edf => return Ok(Self::getBytes1(240)),
                    _ => {}
                }
            }
            Mode::C40 | Mode::Text | Mode::X12
                if self.mode != Self::getPreviousMode(self.previous.clone())? =>
            {
                match self.mode {
                    Mode::Ascii => return Ok(Self::getBytes1(254)),
                    Mode::B256 => return Ok(Self::getBytes2(254, 231)),
                    Mode::C40 => return Ok(Self::getBytes2(254, 230)),
                    Mode::Text => return Ok(Self::getBytes2(254, 239)),
                    Mode::X12 => return Ok(Self::getBytes2(254, 238)),
                    Mode::Edf => return Ok(Self::getBytes2(254, 240)),
                }
            }
            Mode::C40 | Mode::Text | Mode::X12 => {}
            Mode::Edf => assert!(self.mode == Mode::Edf), //The rightmost EDIFACT edge always contains an unlatch character
        }

        Ok(Vec::new())
    }

    // Important: The function does not return the length bytes (one or two) in case of B256 encoding
    pub fn getDataBytes(&self) -> Result<Vec<u8>> {
        match self.mode {
            Mode::Ascii => {
                if self.input.isECI(self.fromPosition)? {
                    Ok(Self::getBytes2(
                        241,
                        self.input.getECIValue(self.fromPosition as usize)? as u32 + 1,
                    ))
                } else if isExtendedASCII(
                    self.input.charAt(self.fromPosition as usize)?,
                    self.input.getFNC1Character(),
                ) {
                    Ok(Self::getBytes2(
                        235,
                        self.input.charAt(self.fromPosition as usize)? as u32 - 127,
                    ))
                } else if self.characterLength == 2 {
                    Ok(Self::getBytes1(
                        (self.input.charAt(self.fromPosition as usize)? as u32 - b'0' as u32) * 10
                            + self.input.charAt(self.fromPosition as usize + 1)? as u32
                            - b'0' as u32
                            + 130,
                    ))
                } else if self.input.isFNC1(self.fromPosition as usize)? {
                    Ok(Self::getBytes1(232))
                } else {
                    Ok(Self::getBytes1(
                        self.input.charAt(self.fromPosition as usize)? as u32 + 1,
                    ))
                }
            }
            Mode::B256 => Ok(Self::getBytes1(
                self.input.charAt(self.fromPosition as usize)? as u32,
            )),
            Mode::C40 => self.getC40Words(true, self.input.getFNC1Character()),
            Mode::Text => self.getC40Words(false, self.input.getFNC1Character()),
            Mode::X12 => self.getX12Words(),
            Mode::Edf => self.getEDFBytes(),
        }
        // assert!( false);
        // Ok(vec![0])
    }
}

struct RXingResult {
    bytes: Vec<u8>,
}
impl RXingResult {
    pub fn new(solution: Option<Arc<Edge>>) -> Result<Self> {
        let solution = if let Some(edge) = solution {
            edge
        } else {
            return Err(Exceptions::ILLEGAL_ARGUMENT);
        };
        let input = solution.input.clone();
        let mut size = 0;
        let mut bytesAL = Vec::new(); //new ArrayList<>();
        let mut randomizePostfixLength = Vec::new(); //new ArrayList<>();
        let mut randomizeLengths = Vec::new(); //new ArrayList<>();
        if (solution.mode == Mode::C40 || solution.mode == Mode::Text || solution.mode == Mode::X12)
            && solution.getEndMode()? != Mode::Ascii
        {
            size += Self::prepend(&Edge::getBytes1(254), &mut bytesAL);
        }
        let mut hold_current = Some(solution.clone());
        while let Some(current) = hold_current {
            // Fails on i = 77 should be 151 is 144
            size += Self::prepend(&current.getDataBytes()?, &mut bytesAL);

            if current.previous.is_none()
                || Edge::getPreviousStartMode(current.previous.clone()) != current.getMode()
            {
                if current.getMode() == Mode::B256 {
                    if size <= 249 {
                        bytesAL.insert(0, size as u8);
                        size += 1;
                    } else {
                        bytesAL.insert(0, (size % 250) as u8);
                        bytesAL.insert(0, (size / 250 + 249) as u8);
                        size += 2;
                    }
                    randomizePostfixLength.push(bytesAL.len());
                    randomizeLengths.push(size);
                }
                Self::prepend(&current.getLatchBytes()?, &mut bytesAL);
                size = 0;
            }

            hold_current = current.previous.clone();
        }
        if input.getMacroId() == 5 {
            _ = Self::prepend(&Edge::getBytes1(236), &mut bytesAL);
        } else if input.getMacroId() == 6 {
            _ = Self::prepend(&Edge::getBytes1(237), &mut bytesAL);
        }

        if input.getFNC1Character().is_some() {
            _ = Self::prepend(&Edge::getBytes1(232), &mut bytesAL);
        }
        for i in 0..randomizePostfixLength.len() {
            // for (int i = 0; i < randomizePostfixLength.size(); i++) {
            let bytes_al_len = bytesAL.len() as u32;
            Self::applyRandomPattern(
                &mut bytesAL,
                bytes_al_len - *randomizePostfixLength.get(i).unwrap() as u32,
                *randomizeLengths.get(i).unwrap() as u32,
            );
        }
        //add padding
        let capacity = solution.getMinSymbolSize(bytesAL.len() as u32);
        if bytesAL.len() < capacity as usize {
            bytesAL.push(129);
        }
        while bytesAL.len() < capacity as usize {
            bytesAL.push(Self::randomize253State(bytesAL.len() as u32 + 1) as u8);
        }

        let mut bytes = vec![0u8; bytesAL.len()];
        // for (i, byte) in bytes.iter_mut().enumerate() {
        //     // for (int i = 0; i < bytes.length; i++) {
        //     *byte = *bytesAL.get(i).unwrap();
        // }
        bytes[..].copy_from_slice(&bytesAL[..]);

        Ok(Self { bytes })
    }

    pub fn prepend(bytes: &[u8], into: &mut Vec<u8>) -> usize {
        for byte in bytes.iter().rev() {
            into.insert(0, *byte);
        }
        bytes.len()
    }

    #[inline]
    fn randomize253State(codewordPosition: u32) -> u32 {
        let pseudoRandom = ((149 * codewordPosition) % 253) + 1;
        let tempVariable = 129 + pseudoRandom;
        if tempVariable <= 254 {
            tempVariable
        } else {
            tempVariable - 254
        }
    }

    pub fn applyRandomPattern(bytesAL: &mut [u8], startPosition: u32, length: u32) {
        for i in 0..length as usize {
            //See "B.1 253-state algorithm
            let Pad_codeword_position = startPosition as usize + i;
            let Pad_codeword_value = bytesAL.get(Pad_codeword_position).unwrap_or(&0);
            let pseudo_random_number = ((149 * (Pad_codeword_position + 1)) % 255) + 1;
            let temp_variable: u16 = *Pad_codeword_value as u16 + pseudo_random_number as u16;
            bytesAL[Pad_codeword_position] = if temp_variable <= 255 {
                temp_variable as u8
            } else {
                (temp_variable - 256) as u8
            };
        }
    }

    pub fn getBytes(&self) -> &[u8] {
        &self.bytes
    }
}

struct Input {
    shape: SymbolShapeHint,
    macroId: i32,
    internal: MinimalECIInput,
}

impl Input {
    pub fn new(
        stringToEncode: &str,
        priorityCharset: Option<CharacterSet>,
        fnc1: Option<char>,
        shape: SymbolShapeHint,
        macroId: i32,
    ) -> Self {
        let z = fnc1.unwrap_or_default().to_string();
        let v = if fnc1.is_some() {
            Some(z.as_str())
        } else {
            None
        };
        Self {
            shape,
            macroId,
            internal: MinimalECIInput::new(stringToEncode, priorityCharset, v),
        }
    }

    pub fn getMacroId(&self) -> i32 {
        self.macroId
    }

    pub fn getShapeHint(&self) -> SymbolShapeHint {
        self.shape
    }

    pub fn length(&self) -> usize {
        self.internal.length()
    }
    pub fn isECI(&self, index: u32) -> Result<bool> {
        self.internal.isECI(index)
    }
    pub fn charAt(&self, index: usize) -> Result<char> {
        self.internal.charAt(index)
    }
    pub fn getFNC1Character(&self) -> Option<char> {
        if self.internal.getFNC1Character() == 1000 {
            None
        } else {
            Some(self.internal.getFNC1Character() as u8 as char)
        }
    }
    fn haveNCharacters(&self, index: usize, n: usize) -> Result<bool> {
        self.internal.haveNCharacters(index, n)
    }
    fn isFNC1(&self, index: usize) -> Result<bool> {
        self.internal.isFNC1(index)
    }
    fn getECIValue(&self, index: usize) -> Result<Eci> {
        self.internal.getECIValue(index)
    }
}

impl fmt::Display for Input {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.internal.fmt(f)
    }
}
