// Copyright 2013-2014 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.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.

use std::char;
use test::TestFn;

pub fn collect_tests<F: FnMut(String, TestFn)>(add_test: &mut F) {
    // http://www.unicode.org/Public/idna/latest/IdnaTest.txt
    for (i, line) in include_str!("IdnaTest.txt").lines().enumerate() {
        if line == "" || line.starts_with("#") {
            continue;
        }
        // Remove comments
        let mut line = match line.find("#") {
            Some(index) => &line[0..index],
            None => line,
        };

        let mut expected_failure = false;
        if line.starts_with("XFAIL") {
            expected_failure = true;
            line = &line[5..line.len()];
        };

        let mut pieces = line.split(';').map(|x| x.trim()).collect::<Vec<&str>>();

        let test_type = pieces.remove(0);
        let original = pieces.remove(0);
        let source = unescape(original);
        let to_unicode = pieces.remove(0);
        let to_ascii = pieces.remove(0);
        let nv8 = if pieces.len() > 0 {
            pieces.remove(0)
        } else {
            ""
        };

        if expected_failure {
            continue;
        }

        let test_name = format!("UTS #46 line {}", i + 1);
        add_test(
            test_name,
            TestFn::dyn_test_fn(move || {
                let result = idna::Config::default()
                    .use_std3_ascii_rules(true)
                    .verify_dns_length(true)
                    .check_hyphens(true)
                    .transitional_processing(test_type == "T")
                    .to_ascii(&source);

                if to_ascii.starts_with("[") {
                    if to_ascii.starts_with("[C") {
                        // http://unicode.org/reports/tr46/#Deviations
                        // applications that perform IDNA2008 lookup are not required to check
                        // for these contexts
                        return;
                    }
                    if to_ascii == "[V2]" {
                        // Everybody ignores V2
                        // https://github.com/servo/rust-url/pull/240
                        // https://github.com/whatwg/url/issues/53#issuecomment-181528158
                        // http://www.unicode.org/review/pri317/
                        return;
                    }
                    let res = result.ok();
                    assert!(
                        res == None,
                        "Expected error. result: {} | original: {} | source: {}",
                        res.unwrap(),
                        original,
                        source
                    );
                    return;
                }

                let to_ascii = if to_ascii.len() > 0 {
                    to_ascii.to_string()
                } else {
                    if to_unicode.len() > 0 {
                        to_unicode.to_string()
                    } else {
                        source.clone()
                    }
                };

                if nv8 == "NV8" {
                    // This result isn't valid under IDNA2008. Skip it
                    return;
                }

                assert!(
                    result.is_ok(),
                    "Couldn't parse {} | original: {} | error: {:?}",
                    source,
                    original,
                    result.err()
                );
                let output = result.ok().unwrap();
                assert!(
                    output == to_ascii,
                    "result: {} | expected: {} | original: {} | source: {}",
                    output,
                    to_ascii,
                    original,
                    source
                );
            }),
        )
    }
}

fn unescape(input: &str) -> String {
    let mut output = String::new();
    let mut chars = input.chars();
    loop {
        match chars.next() {
            None => return output,
            Some(c) => {
                if c == '\\' {
                    match chars.next().unwrap() {
                        '\\' => output.push('\\'),
                        'u' => {
                            let c1 = chars.next().unwrap().to_digit(16).unwrap();
                            let c2 = chars.next().unwrap().to_digit(16).unwrap();
                            let c3 = chars.next().unwrap().to_digit(16).unwrap();
                            let c4 = chars.next().unwrap().to_digit(16).unwrap();
                            match char::from_u32(((c1 * 16 + c2) * 16 + c3) * 16 + c4) {
                                Some(c) => output.push(c),
                                None => {
                                    output
                                        .push_str(&format!("\\u{:X}{:X}{:X}{:X}", c1, c2, c3, c4));
                                }
                            };
                        }
                        _ => panic!("Invalid test data input"),
                    }
                } else {
                    output.push(c);
                }
            }
        }
    }
}
