// Copyright 2016 John D. Hume
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

const A_OFFSET: u8 = 65;
const CODE_POS_1_MULTIPLIER: u16 = 26 * 26;
const CODE_POS_2_MULTIPLIER: u16 = 26;

pub fn code_to_base26(code: &str) -> u16 {
    assert_eq!(3, code.len(), "Code must be three upper-case letters");
    let bytes = code.as_bytes();
    for b in bytes {
        assert!(A_OFFSET <= *b && *b <= (A_OFFSET + 26));
    }
    (((bytes[0] - A_OFFSET) as u16) * CODE_POS_1_MULTIPLIER) +
    (((bytes[1] - A_OFFSET) as u16) * CODE_POS_2_MULTIPLIER) + (bytes[2] - A_OFFSET) as u16
}

pub fn base26_to_code(base26: u16) -> String {
    let mut base26 = base26;
    let byte3 = (base26 % 26) as u8 + A_OFFSET;
    base26 /= 26;
    let byte2 = (base26 % 26) as u8 + A_OFFSET;
    base26 /= 26;
    let byte1 = (base26 % 26) as u8 + A_OFFSET;
    String::from_utf8(vec![byte1, byte2, byte3]).expect("Bad byte in currency code")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn base26_code_reflexivity() {
        check_base26_code_reflexivity(0, "AAA");
        check_base26_code_reflexivity(1, "AAB");
        check_base26_code_reflexivity(26, "ABA");
        check_base26_code_reflexivity((25 * 26u16.pow(2)) + (24 * 26) + 23, "ZYX");
    }

    fn check_base26_code_reflexivity(base26: u16, code: &str) {
        assert_eq!(base26, code_to_base26(code));
        assert_eq!(code, base26_to_code(base26).as_str());
    }

    #[test]
    #[should_panic]
    fn panics_on_too_short_code() {
        code_to_base26("US");
    }

    #[test]
    #[should_panic]
    fn panics_on_too_long_code() {
        code_to_base26("USDA");
    }

    #[test]
    #[should_panic]
    fn panics_on_lower_case_code() {
        code_to_base26("usd");
    }
}
