use std::io::prelude::*;

#[derive(Debug, Default, PartialEq)]
struct Marker {
    length: u32,
    multiplier: u32,
}

fn main() 


fn decoding_nested(line: &str) -> u64 {
    let (ret, _) = decoding_nested_rec(&mut line.chars(), std::u64::MAX);
    ret
}

fn decoding_nested_rec(it: &mut std::str::Chars, length: u64) -> (u64, u64) {
    let mut total_length: u64 = 0;
    let mut char_read: u64 = 0;

    while char_read < length {
        let letter = match it.next() {
            Some(letter) => letter,
            None => break,
        };

        if letter == ' ' {
            continue;
        }

        char_read += 1;

        if is_marker_start(letter) {
            let (marker, marker_read) = decode_marker(it);
            char_read += marker_read as u64;

            let (nested_length, nested_read) = decoding_nested_rec(it, marker.length as u64);
            total_length += nested_length * (marker.multiplier as u64);
            char_read += nested_read as u64;

        } else {
            total_length += 1;
        }
    }

    (total_length, char_read)
}

#[test]
fn decoding_nested_test() {
    let input = "X(7x2)A(1x2)AY"; //A(1x2)AA(1x2)A ; AAAAAA
    assert_eq!(decoding_nested(input), 8);

    let input = "(11x2)ABCDEF(6x1)(1x1)A";
    assert_eq!(decoding_nested(input), 14);

    let input = "(3x3)XYZ";
    assert_eq!(decoding_nested(input), 9);

    let input = "(1x1)B";
    assert_eq!(decoding_nested(input), 1);

    let input = "X(8x2)(3x3)ABCY";
    assert_eq!(decoding_nested(input), 20);

    let input = "(27x12)(20x12)(13x14)(7x10)(1x12)A";
    assert_eq!(decoding_nested(input), 241920);

    let input = "(25x3)(3x3)ABC(2x3)XY(5x2)PQRSTX(18x9)(3x2)TWO(5x7)SEVEN";
    assert_eq!(decoding_nested(input), 445);
}

fn decoding_light(line: String) -> u32 {
    let mut total_length: u32 = 0;
    let mut it = line.chars();

    loop {
        let letter = match it.next() {
            Some(letter) => letter,
            None => break,
        };

        if letter == ' ' {
            continue;
        }

        if is_marker_start(letter) {
            let (marker, _) = decode_marker(&mut it);
            total_length += marker.length * marker.multiplier;
            skip_letter(&mut it, marker.length);
        } else {
            total_length += 1;
        }
    }

    total_length
}

#[test]
fn decoding_light_test() {
    assert_eq!(decoding_light(String::from("ADVENT")), 6);
    assert_eq!(decoding_light(String::from("A(1x5)BC")), 7);
    assert_eq!(decoding_light(String::from("(3x3)XYZ")), 9);
    assert_eq!(decoding_light(String::from("A(2x2)BCD(2x2)EFG")), 11);
    assert_eq!(decoding_light(String::from("(6x1)(1x3)A")), 6);
    assert_eq!(decoding_light(String::from("X(8x2)(3x3)ABCY")), 18);
}

fn is_marker_start(letter: char) -> bool {
    letter == '('
}

fn skip_letter<IteratorChars>(it: &mut IteratorChars, n: u32)
    where IteratorChars: Iterator<Item = char>
{
    for _ in 0..n {
        it.next();
    }
}

fn decode_marker(it: &mut std::str::Chars) -> (Marker, u32) {
    let (length, length_length) = read_number(it);
    let (multiplier, multiplier_length) = read_number(it);

    (Marker {
        length: length,
        multiplier: multiplier,
    },
     length_length + multiplier_length + 2)
}

#[test]
fn decode_marker_test() {
    assert_eq!(decode_marker(&mut "1x2)".chars()),
               (Marker {
                   length: 1,
                   multiplier: 2,
               },
                4));

    assert_eq!(decode_marker(&mut "123w456r".chars()),
               (Marker {
                   length: 123,
                   multiplier: 456,
               },
                8));
}

fn read_number(it: &mut std::str::Chars) -> (u32, u32) {
    let mut number = String::new();

    loop {
        let letter = match it.next() {
            Some(letter) => letter,
            _ => break,
        };

        if letter.is_numeric() {
            number.push(letter);
        } else {
            break;
        }
    }

    if number.is_empty() {
        panic!("No integer to parse at beginnig of string");
    } else {
        (number.parse::<u32>().expect("Cannot parse u32"), number.len() as u32)
    }
}

#[test]
fn read_number_test() {
    assert_eq!(read_number(&mut "123".chars()), (123, 3));
    assert_eq!(read_number(&mut "1".chars()), (1, 1));
    assert_eq!(read_number(&mut "42)".chars()), (42, 2));
    assert_eq!(read_number(&mut "2x".chars()), (2, 1));
}

{
    let mut inputs = std::fs::File::open("day9_inputs.txt").expect("Cannot open input file");
    let mut line = String::new();
    inputs.read_to_string(&mut line).expect("Cannot read line");
    line.pop();  // Last char is a new line.

    let length = decoding_light(line.clone());
    println!("Length: {}", length);
    assert_eq!(length, 98135);

    let length = decoding_nested(&line);
    println!("Nested length: {}", length);
    assert_eq!(length, 10964557606);
}