use std::error::Error;
use std::io::Read;
use std::net::TcpStream;
use std::str::FromStr;
use std::time::Duration;
use log::{debug, error, info};
use url::Url;
use hornbeam::frame::Message;
use hornbeam::handshake_client::ClientConnectionInfo;
use hornbeam::stream::{MessageReadable, MessageReadError, MessageWritable, MessageWriteError};

fn get_case_count() -> u32 {
    // ws://localhost:9001/getCaseCount
    let mut socket = TcpStream::connect("localhost:9001").unwrap();
    let info = ClientConnectionInfo::from(Url::from_str("ws://localhost:9001/getCaseCount").unwrap());
    info.send_handshake(&mut socket).unwrap();
    let mut ws = info.read_handshake(socket).unwrap();
    let msg = ws.read_message().unwrap();
    ws.close(None, None).unwrap();
    match msg {
        hornbeam::frame::Message::Text(s) => s.parse().unwrap(),
        _ => panic!("unexpected getCaseCount response")
    }
}

fn update_reports() {
    // ws://localhost:9001/updateReports?agent=Hornbeam
    let mut socket = TcpStream::connect("localhost:9001").unwrap();
    let info = ClientConnectionInfo::from(Url::from_str("ws://localhost:9001/updateReports?agent=Hornbeam").unwrap());
    info.send_handshake(&mut socket).unwrap();
    let mut ws = info.read_handshake(socket).unwrap();
    ws.close(None, None).unwrap();
}

fn run_test(test_id: u32) -> Result<(), MessageReadError> {
    info!("Running test case {}", test_id);

    let mut socket = TcpStream::connect("localhost:9001")?;
    socket.set_read_timeout(Some(Duration::from_secs(1))).unwrap();
    let info = ClientConnectionInfo::from(Url::from_str(&format!("ws://localhost:9001/runCase?case={}&agent=Hornbeam", test_id)).unwrap());
    info.send_handshake(&mut socket).unwrap();
/*
    loop {
        let mut buf: [u8; 1024] = [0u8; 1024];
        let bytes = socket.read(&mut buf).unwrap();
        if bytes == 0 { break; }
        let bytes = &buf[0..bytes];
        debug!("{:x?}", bytes);
    }

    Ok(())

 */

    let mut ws = info.read_handshake(socket).unwrap();

    loop {
        let msg = ws.read_message()?;
        match msg {
            Message::Text(_) => ws.write_message(msg).unwrap(),
            Message::Binary(_) => ws.write_message(msg).unwrap(),
            Message::Ping(_) => {}
            Message::Pong(_) => {}
        }
    }
}

fn main() {
    simple_logger::init_with_env().unwrap();

    info!("-=- Hornbeam Websockets -=-");
    info!("Autobahn Testsuite - Client");
    info!("Fetching case count...");

    info!("Starting tests...");

    let mut args = std::env::args();

    let run;

    if args.len() == 1 {
        let case_count = get_case_count();
        run = 1..=case_count;
    } else {
        let test_number = args.nth(1).unwrap().parse().unwrap();
        run = test_number..=test_number;
    }

    info!("Running tests ({:?}) ...", run);

    for test in run {
        match run_test(test) {
            Ok(_) => {}
            Err(e) => match e {
                MessageReadError::IoError(e) => {
                    error!("io error on test {}: {}", test, e);
                },
                MessageReadError::FrameReadError(e) => {
                    error!("frame read error on test {}: {}", test, e);
                },
                MessageReadError::ContinuationOutsideFragment => {
                    error!("continuation outside fragment on test {}", test);
                },
                MessageReadError::FrameWriteError(e) => {
                    error!("frame write error on test {}: {}", test, e);
                },
                _ => {}
            }
        }
    }

    info!("Tests complete, generating reports...");
    update_reports();
    info!("All done!");
}