#![feature(const_generics)]

mod login;
mod character;
mod dataaccess;
mod entities;


use std::net::{SocketAddr, Ipv4Addr};
//use std::net;
use std::thread;
use std::env;

use mio::tcp::TcpListener;
use mio::{Events, Poll, Token, Ready, PollOpt};
//use dotenv::dotenv;

use bcrypt;
//use diesel::r2d2;
//use diesel::prelude::*;
//use diesel::pg::PgConnection;

use libpso::character::settings;

//use models::{NewUser, UserAccount, UserSettings, NewUserSettings};
//use dataaccess::DBAccess;
use entities::{UserAccount, UserSettings};
use dataaccess::DataAccess;

use std::time::SystemTime;

#[derive(Clone)]
struct LoginStubData {
}

impl DataAccess for LoginStubData {
    fn get_user_by_name(&self, username: String) -> Option<UserAccount> {
        if username.as_str() == "hi" {
            Some(UserAccount {
                id: 1,
                username: "hi".to_owned(),
                password: bcrypt::hash("qwer", 5).unwrap(),
                guildcard: None,
                team_id: None,
                banned: false,
                muted_until: SystemTime::now(),
                created_at: SystemTime::now(),
            })
        }
        else {
            None
        }
    }

    fn get_user_settings_by_user(&self, user: &UserAccount) -> Option<UserSettings> {
        Some(UserSettings {
            id: 0,
            user_id: user.id,
            settings: settings::UserSettings::default()
        })
    }
}

fn main() {
    //dotenv().ok();
    //let database_url = env::var("DATABASE_URL").unwrap();

    /*if let Some(arg) = env::args().nth(1) {
        if arg == "dbstuff" {
            let conn = PgConnection::establish(&database_url).unwrap();

            use elseware::schema::user_accounts::dsl::*;
            use elseware::schema::user_settings::dsl::*;

            let u = NewUser::new("hi".to_string(), "qwer".to_string());
            diesel::insert_into(user_accounts).values(&u).execute(&conn).unwrap();
            let u = NewUser::new("hi2".to_string(), "qwer".to_string());
            let user: UserAccount = diesel::insert_into(user_accounts).values(&u).get_result(&conn).unwrap();

            let mut s = models::EUserSettings(libpso::character::settings::UserSettings::default());
            s.0.blocked_users[5] = 99;
            s.0.blocked_users[6] = 123;

            diesel::insert_into(user_settings).values(& NewUserSettings {
                user_id: user.id,
                settings: s,
            }).execute(&conn).unwrap();

            let us = user_settings.load::<UserSettings>(&conn).unwrap();
            for u in us {
                println!("{:?}", u.settings.0.blocked_users[4]);
                println!("{:?}", u.settings.0.blocked_users[5]);
                println!("{:?}", u.settings.0.blocked_users[6]);
            }
        }
    }*/

    //let database_url = env::var("DATABASE_URL").unwrap();

    println!("[login+character] starting server");

    /*let connection_manager = r2d2::ConnectionManager::<PgConnection>::new(database_url);
    let connection_pool = r2d2::Pool::builder()
        .build(connection_manager).unwrap();*/

    let login_listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), login::LOGIN_PORT))).unwrap();
    let character_listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), character::CHARACTER_PORT))).unwrap();

    //let login_shared_state = login::SharedLoginState::new(DBAccess::new(connection_pool));
    let login_shared_state = login::SharedLoginState::new(LoginStubData {});

    let poll = Poll::new().unwrap();
    poll.register(&login_listener, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
    poll.register(&character_listener, Token(1), Ready::readable(), PollOpt::edge()).unwrap();

    let mut events = Events::with_capacity(1024);

    loop {
        poll.poll(&mut events, None).unwrap();

        for event in &events {
            match event.token() {
                Token(0) => {
                    login_listener.accept().map(|(socket, addr)| {
                        let shared_state_clone = login_shared_state.clone();
                        thread::spawn(move || {
                            println!("[login] accepted connection: {}", addr);
                            login::new_client(socket, shared_state_clone);
                        });
                    });
                },
                Token(1) => {
                    character_listener.accept().map(|(socket, addr)| {
                        let shared_state_clone = login_shared_state.clone();
                        thread::spawn(move || {
                            println!("[character] accepted connection: {}", addr);
                            character::new_client(socket, shared_state_clone);
                        });
                    });
                },
                _ => {}
            }
        }
    }
}