use std::net::{TcpListener, SocketAddr, Ipv4Addr};
use std::net;
use std::thread;

use mio::{Events, Poll, Token, Ready, PollOpt};
use rand::{Rng, RngCore};

use libpso::{PacketParseError, PSOPacket};
use libpso::packet::login::*;
use libpso::crypto::{CipherError, PSOCipher, NullCipher};
use libpso::crypto::bb::PSOBBCipher;

use elseware::common::{send_packet, recv_packet, PacketNetworkError};
use elseware::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};

const LOGIN_PORT: u16 = 12000;
const CHARACTER_PORT: u16 = 12001;


struct Client {
    running: bool,
    socket: mio::tcp::TcpStream,
    cipher_in: Box<dyn PSOCipher>,
    cipher_out: Box<dyn PSOCipher>,
}

impl Client {
    fn new(socket: net::TcpStream) -> Client {
        Client {
            running: true,
            socket: mio::tcp::TcpStream::from_stream(socket).expect("could not convert socket to nonblocking"),
            cipher_in: Box::new(NullCipher {}),
            cipher_out: Box::new(NullCipher {}),
        }
    }

    fn send(&mut self, pkt: &dyn PSOPacket) {
        match send_packet(&mut self.socket, &mut *self.cipher_out, pkt) {
            Ok(_) => {
                println!("[login] send ({:?}): {:?}", self.socket, pkt);
            },
            Err(err) => {
                println!("[login] error sending packet to {:?}: {:?}", self.socket, err);
                self.running = false;
            }
        }
    }
}

fn client_loop(mut client: Client) {
    let poll = mio::Poll::new().unwrap();
    poll.register(&client.socket, Token(0), Ready::readable(), PollOpt::edge()).unwrap();

    let mut events = Events::with_capacity(1024);
    loop {
        poll.poll(&mut events, None).unwrap();

        for event in &events{
            println!("event! {:?}", event);
            if event.token() == Token(0) {
                loop {
                    let pkt = recv_packet(&mut client.socket, &mut *client.cipher_in);
                    println!("{:?}", pkt);

                    match pkt {
                        Ok(pkt) => {
                            //handle_packet(&mut client, pkt).expect("could not handle packet");
                            println!("[login] pkt: {:?}", pkt);
                        },
                        Err(err) => {
                            println!("[login] error recv-ing packet with {:?}: {:?}", client.socket, err);
                            break;
                        }
                    }
                }
            }
        }
    }
}

fn new_client(socket: net::TcpStream) {
    let mut client = Client::new(socket);
    let mut rng = rand::thread_rng();

    let mut server_key = [0u8; 48];
    let mut client_key = [0u8; 48];
    rng.fill(&mut server_key[..]);
    rng.fill(&mut client_key[..]);
    let welcome = LoginWelcome::new(server_key, client_key);
    client.send(&welcome);
    client.cipher_in = Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, client_key));
    client.cipher_out = Box::new(PSOBBCipher::new(ELSEWHERE_PARRAY, ELSEWHERE_PRIVATE_KEY, server_key));
    client_loop(client);
}

fn main() {
    println!("[login] starting server");

    let listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), LOGIN_PORT))).unwrap();

    while let Ok((socket, addr)) = listener.accept() {
        thread::spawn(move || {
            println!("[login] accepted connection: {}", addr);
            new_client(socket);
        });
    }


    println!("[login] exiting...");
}