<commit_before>use std::io::File;
use std::io::fs::PathExtensions;
use std::collections::HashMap;
use util::{color, V2};
use backend::{Canvas, CanvasUtil, Event, Key, Fonter};
use world;
use world::action;
use world::action::Input::{Step, Melee};
use world::action::ControlState::*;
use world::{Msg, FovStatus};
use world::Dir6;
use world::Dir6::*;
use world::{Entity};
use world::item::{Slot};
use worldview;
use sprite::{WorldSprites, GibSprite};
use tilecache;
use tilecache::icon;
use msg_queue::MsgQueue;

pub struct GameState {
    /// Transient effect sprites drawn in game world view.
    world_spr: WorldSprites,
    /// Counters for entities with flashing damage animation.
    damage_timers: HashMap<Entity, u32>,

    /// Flag for autoexploration.
    // TODO: Probably going to need a general "ongoing activity" system at
    // some point.
    exploring: bool,

    msg: MsgQueue,
    ui_state: UiState,

    // Hacky thing to wait for next time Canvas reference is available.
    screenshot_requested: bool,
}

enum UiState {
    Gameplay,
    Inventory
}

impl GameState {
    pub fn new(seed: Option<u32>) -> GameState {
        world::init_world(seed);
        GameState {
            world_spr: WorldSprites::new(),
            damage_timers: HashMap::new(),
            exploring: false,
            msg: MsgQueue::new(),
            ui_state: UiState::Gameplay,
            screenshot_requested: false,
        }
    }

    fn draw_player_ui(&mut self, ctx: &mut Canvas, player: Entity) {
        let hp = player.hp();
        let max_hp = player.max_hp();

        // Draw heart containers.
        for i in 0..((max_hp + 1) / 2) {
            let pos = V2(i as f32 * 8.0, 8.0);
            let idx = if hp >= (i + 1) * 2 { icon::HEART }
                else if hp == i * 2 + 1 { icon::HALF_HEART }
                else { icon::NO_HEART };
            ctx.draw_image(tilecache::get(idx), pos, 0.0, &color::FIREBRICK, &color::BLACK);
        }
    }

    fn base_paint(&mut self, ctx: &mut Canvas) {
        let camera = world::camera();
        worldview::draw_world(&camera, ctx, &self.damage_timers);

        self.world_spr.draw(|x| (camera + x).fov_status() == Some(FovStatus::Seen), &camera, ctx);
        self.world_spr.update();

        let location_name = camera.name();
        write!(&mut ctx.text_writer(V2(640 - location_name.len() as i32 * 8, 8), 0.1, color::LIGHTGRAY)
                       .set_border(color::BLACK),
                       "{}", location_name).unwrap();

        self.msg.draw(ctx);
        if let Some(player) = action::player() {
            self.draw_player_ui(ctx, player);
        }

        let fps = 1.0 / ctx.render_duration;
        let _ = write!(&mut ctx.text_writer(V2(0, 16), 0.1, color::LIGHTGREEN)
                       .set_border(color::BLACK),
                       "FPS {:.0}", fps);
        let _ = write!(&mut ctx.text_writer(V2(0, 360), 0.1, color::LIGHTGRAY)
                      .set_border(color::BLACK),
                      "MAGOG v{}{}",
                      ::version(),
                      if !cfg!(ndebug) { " ***DEBUG build***" } else { "" });
    }

    fn base_update(&mut self, ctx: &mut Canvas) {
        // Process events
        loop {
            match world::pop_msg() {
                Some(Msg::Gib(loc)) => {
                    self.world_spr.add(Box::new(GibSprite::new(loc)));
                }
                Some(Msg::Damage(entity)) => {
                    self.damage_timers.insert(entity, 2);
                }
                Some(Msg::Text(txt)) => {
                    self.msg.msg(txt)
                }
                Some(Msg::Caption(txt)) => {
                    self.msg.caption(txt)
                }
                Some(x) => {
                    println!("Unhandled Msg type {:?}", x);
                }
                None => break
            }
        }

        self.base_paint(ctx);

        if action::control_state() == ReadyToUpdate {
            action::update();
        }

        if self.exploring {
            if action::control_state() == AwaitingInput {
                self.exploring = self.autoexplore();
            }
        }

        self.damage_timers = self.damage_timers.clone().into_iter()
            .filter(|&(_, t)| t > 0)
            .map(|(e, t)| (e, t - 1))
            .collect();

        self.msg.update();
    }

    fn inventory_update(&mut self, ctx: &mut Canvas) {
        let player = action::player().unwrap();
        {
            let mut cursor = ctx.text_writer(V2(0, 8), 0.1, color::GAINSBORO);
            for slot_data in SLOT_DATA.iter() {
                let name = match player.equipped(slot_data.slot) {
                    Some(item) => item.name(),
                    None => "".to_string()
                };
                write!(&mut cursor, "{}] {}: {}\n",
                    slot_data.key, slot_data.name, name).unwrap();
            }
        }

        {
            let mut cursor = ctx.text_writer(V2(0, 360), 0.1, color::GAINSBORO);
            write!(&mut cursor, "Press letter to equip/unequip item. Press shift+letter to drop item.").unwrap();
        }
    }

    pub fn inventory_process(&mut self, event: Event) -> bool {
        let player = action::player().unwrap();
        match event {
            Event::Render(ctx) => { self.update(ctx); }
            Event::KeyPressed(Key::Escape) | Event::KeyPressed(Key::Tab) => {
                self.ui_state = UiState::Gameplay
            }
            Event::KeyPressed(Key::F12) => { self.screenshot_requested = true; }
            Event::KeyPressed(_) => {}

            Event::Char(ch) => {
                for slot_data in SLOT_DATA.iter() {
                    if ch == slot_data.key {
                        if slot_data.slot.is_gear_slot() {
                            // Unequip gear
                            match player.free_bag_slot() {
                                None => {
                                    // No room in bag, can't unequip until
                                    // drop something.
                                    // TODO: Message about full bag.
                                }
                                Some(swap_slot) => {
                                    player.swap_equipped(slot_data.slot, swap_slot);
                                }
                            }
                        }
                        if slot_data.slot.is_bag_slot() {
                            // Bag items get equipped if they have are gear
                            // with a preferred slot.
                            if let Some(item) = player.equipped(slot_data.slot) {
                                let equip_slots = item.equip_slots();
                                for &swap_slot in equip_slots.iter() {
                                    if player.equipped(swap_slot).is_none() {
                                        player.swap_equipped(slot_data.slot, swap_slot);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    if ch == slot_data.key.to_uppercase() {
                        // Drop item in slot.
                        if let Some(item) = player.equipped(slot_data.slot) {
                            item.place(player.location().unwrap());
                        }
                        break;
                    }
                }
            }

            _ => ()
        }
        true
    }

    /// Repaint view, update game world if needed.
    pub fn update(&mut self, ctx: &mut Canvas) {
        if self.screenshot_requested {
            use std::io::File;
            use image;
            let shot = ctx.screenshot();
            let file = File::create(&Path::new("/tmp/shot.png")).unwrap();
            let _ = image::ImageRgb8(shot).save(file, image::PNG);

            self.screenshot_requested = false;
        }

        ctx.clear();

        match self.ui_state {
            UiState::Gameplay => self.base_update(ctx),
            UiState::Inventory => self.inventory_update(ctx),
        }
    }

    pub fn save_game(&self) {
        let save_data = world::save();
        let mut file = File::create(&Path::new("/tmp/magog_save.json"));
        file.write_str(save_data.as_slice()).unwrap();
    }

    pub fn load_game(&mut self) {
        let path = Path::new("/tmp/magog_save.json");
        if !path.exists() { return; }
        let save_data = File::open(&path).read_to_string().unwrap();
        // TODO: Handle failed load nicely.
        world::load(save_data.as_slice()).unwrap();
    }

    fn smart_move(&mut self, dir: Dir6) {
        let player = action::player().unwrap();
        let loc = player.location().unwrap();
        for &d in vec![dir, dir + 1, dir - 1].iter() {
            let target_loc = loc + d.to_v2();
            if target_loc.has_mobs() {
                action::input(Melee(d));
                return;
            } else if player.can_step(d) {
                action::input(Step(d));
                return;
            }
        }
    }

    fn autoexplore(&mut self) -> bool {
        let player = action::player().unwrap();
        if player.is_threatened() {
            return false;
        }
        if let Some(pathing) = action::autoexplore_map(32) {
            let loc = player.location().unwrap();
            let steps = pathing.sorted_neighbors(&loc);
            if steps.len() == 0 {
                return false;
            }

            action::input(Step(loc.dir6_towards(steps[0]).unwrap()));
            return true;
        }

        false
    }

    /// Context-specific interaction with the current cell.
    fn interact(&mut self) {
        let player = action::player().unwrap();
        let loc = player.location().unwrap();
        if let Some(item) = loc.top_item() {
            player.pick_up(item);
            return;
        }
    }

    /// Process a player control keypress.
    pub fn gameplay_process_key(&mut self, key: Key) -> bool {
        if action::control_state() != AwaitingInput {
            return false;
        }

        if self.exploring {
            self.exploring = false;
        }

        match key {
            Key::Q | Key::Pad7 => { self.smart_move(NorthWest); }
            Key::W | Key::Pad8 | Key::Up => { self.smart_move(North); }
            Key::E | Key::Pad9 => { self.smart_move(NorthEast); }
            Key::A | Key::Pad1 => { self.smart_move(SouthWest); }
            Key::S | Key::Pad2 | Key::Down => { self.smart_move(South); }
            Key::D | Key::Pad3 => { self.smart_move(SouthEast); }

            Key::Enter => { self.interact(); }
            Key::X => { self.exploring = true; }

            // Open inventory
            Key::Tab => { self.ui_state = UiState::Inventory; }

            Key::F5 => { self.save_game(); }
            Key::F9 => { self.load_game(); }
            Key::F12 => { self.screenshot_requested = true; }
            _ => { return false; }
        }
        return true;
    }

    pub fn gameplay_process(&mut self, event: Event) -> bool {
        match event {
            Event::Render(ctx) => {
                self.update(ctx);
            }
            // TODO: Better quit confirmation than just pressing esc.
            Event::KeyPressed(Key::Escape) => {
                return false;
            }
            Event::KeyPressed(k) => {
                self.gameplay_process_key(k);
            }

            Event::Char(ch) => {
                // TODO: Chars and keypresses in same lookup (use variants?)
                match ch {
                    // Debug
                    '>' => { action::next_level(); }
                    _ => ()
                }
            }

            _ => ()
        }
        true
    }

    pub fn process(&mut self, event: Event) -> bool {
        match self.ui_state {
            UiState::Gameplay => self.gameplay_process(event),
            UiState::Inventory => self.inventory_process(event),
        }
    }
}

struct SlotData {
    key: char,
    slot: Slot,
    name: &'static str,
}

static SLOT_DATA: [SlotData; 34] = [
    SlotData { key: '1', slot: Slot::Spell1,     name: "Ability" },
    SlotData { key: '2', slot: Slot::Spell2,     name: "Ability" },
    SlotData { key: '3', slot: Slot::Spell3,     name: "Ability" },
    SlotData { key: '4', slot: Slot::Spell4,     name: "Ability" },
    SlotData { key: '5', slot: Slot::Spell5,     name: "Ability" },
    SlotData { key: '6', slot: Slot::Spell6,     name: "Ability" },
    SlotData { key: '7', slot: Slot::Spell7,     name: "Ability" },
    SlotData { key: '8', slot: Slot::Spell8,     name: "Ability" },
    SlotData { key: 'a', slot: Slot::Melee,      name: "Weapon " },
    SlotData { key: 'b', slot: Slot::Ranged,     name: "Ranged " },
    SlotData { key: 'c', slot: Slot::Head,       name: "Head   " },
    SlotData { key: 'd', slot: Slot::Body,       name: "Body   " },
    SlotData { key: 'e', slot: Slot::Feet,       name: "Feet   " },
    SlotData { key: 'f', slot: Slot::TrinketF,   name: "Trinket" },
    SlotData { key: 'g', slot: Slot::TrinketG,   name: "Trinket" },
    SlotData { key: 'h', slot: Slot::TrinketH,   name: "Trinket" },
    SlotData { key: 'i', slot: Slot::TrinketI,   name: "Trinket" },
    SlotData { key: 'j', slot: Slot::InventoryJ, name: "       " },
    SlotData { key: 'k', slot: Slot::InventoryK, name: "       " },
    SlotData { key: 'l', slot: Slot::InventoryL, name: "       " },
    SlotData { key: 'm', slot: Slot::InventoryM, name: "       " },
    SlotData { key: 'n', slot: Slot::InventoryN, name: "       " },
    SlotData { key: 'o', slot: Slot::InventoryO, name: "       " },
    SlotData { key: 'p', slot: Slot::InventoryP, name: "       " },
    SlotData { key: 'q', slot: Slot::InventoryQ, name: "       " },
    SlotData { key: 'r', slot: Slot::InventoryR, name: "       " },
    SlotData { key: 's', slot: Slot::InventoryS, name: "       " },
    SlotData { key: 't', slot: Slot::InventoryT, name: "       " },
    SlotData { key: 'u', slot: Slot::InventoryU, name: "       " },
    SlotData { key: 'v', slot: Slot::InventoryV, name: "       " },
    SlotData { key: 'w', slot: Slot::InventoryW, name: "       " },
    SlotData { key: 'x', slot: Slot::InventoryX, name: "       " },
    SlotData { key: 'y', slot: Slot::InventoryY, name: "       " },
    SlotData { key: 'z', slot: Slot::InventoryZ, name: "       " },
];
<commit_msg>Sequential screenshot numbering<commit_after>use time;
use std::io::File;
use std::io::fs::PathExtensions;
use std::collections::HashMap;
use util::{color, V2};
use backend::{Canvas, CanvasUtil, Event, Key, Fonter};
use world;
use world::action;
use world::action::Input::{Step, Melee};
use world::action::ControlState::*;
use world::{Msg, FovStatus};
use world::Dir6;
use world::Dir6::*;
use world::{Entity};
use world::item::{Slot};
use worldview;
use sprite::{WorldSprites, GibSprite};
use tilecache;
use tilecache::icon;
use msg_queue::MsgQueue;

pub struct GameState {
    /// Transient effect sprites drawn in game world view.
    world_spr: WorldSprites,
    /// Counters for entities with flashing damage animation.
    damage_timers: HashMap<Entity, u32>,

    /// Flag for autoexploration.
    // TODO: Probably going to need a general "ongoing activity" system at
    // some point.
    exploring: bool,

    msg: MsgQueue,
    ui_state: UiState,

    // Hacky thing to wait for next time Canvas reference is available.
    screenshot_requested: bool,
}

enum UiState {
    Gameplay,
    Inventory
}

impl GameState {
    pub fn new(seed: Option<u32>) -> GameState {
        world::init_world(seed);
        GameState {
            world_spr: WorldSprites::new(),
            damage_timers: HashMap::new(),
            exploring: false,
            msg: MsgQueue::new(),
            ui_state: UiState::Gameplay,
            screenshot_requested: false,
        }
    }

    fn draw_player_ui(&mut self, ctx: &mut Canvas, player: Entity) {
        let hp = player.hp();
        let max_hp = player.max_hp();

        // Draw heart containers.
        for i in 0..((max_hp + 1) / 2) {
            let pos = V2(i as f32 * 8.0, 8.0);
            let idx = if hp >= (i + 1) * 2 { icon::HEART }
                else if hp == i * 2 + 1 { icon::HALF_HEART }
                else { icon::NO_HEART };
            ctx.draw_image(tilecache::get(idx), pos, 0.0, &color::FIREBRICK, &color::BLACK);
        }
    }

    fn base_paint(&mut self, ctx: &mut Canvas) {
        let camera = world::camera();
        worldview::draw_world(&camera, ctx, &self.damage_timers);

        self.world_spr.draw(|x| (camera + x).fov_status() == Some(FovStatus::Seen), &camera, ctx);
        self.world_spr.update();

        let location_name = camera.name();
        write!(&mut ctx.text_writer(V2(640 - location_name.len() as i32 * 8, 8), 0.1, color::LIGHTGRAY)
                       .set_border(color::BLACK),
                       "{}", location_name).unwrap();

        self.msg.draw(ctx);
        if let Some(player) = action::player() {
            self.draw_player_ui(ctx, player);
        }

        let fps = 1.0 / ctx.render_duration;
        let _ = write!(&mut ctx.text_writer(V2(0, 16), 0.1, color::LIGHTGREEN)
                       .set_border(color::BLACK),
                       "FPS {:.0}", fps);
        let _ = write!(&mut ctx.text_writer(V2(0, 360), 0.1, color::LIGHTGRAY)
                      .set_border(color::BLACK),
                      "MAGOG v{}{}",
                      ::version(),
                      if !cfg!(ndebug) { " ***DEBUG build***" } else { "" });
    }

    fn base_update(&mut self, ctx: &mut Canvas) {
        // Process events
        loop {
            match world::pop_msg() {
                Some(Msg::Gib(loc)) => {
                    self.world_spr.add(Box::new(GibSprite::new(loc)));
                }
                Some(Msg::Damage(entity)) => {
                    self.damage_timers.insert(entity, 2);
                }
                Some(Msg::Text(txt)) => {
                    self.msg.msg(txt)
                }
                Some(Msg::Caption(txt)) => {
                    self.msg.caption(txt)
                }
                Some(x) => {
                    println!("Unhandled Msg type {:?}", x);
                }
                None => break
            }
        }

        self.base_paint(ctx);

        if action::control_state() == ReadyToUpdate {
            action::update();
        }

        if self.exploring {
            if action::control_state() == AwaitingInput {
                self.exploring = self.autoexplore();
            }
        }

        self.damage_timers = self.damage_timers.clone().into_iter()
            .filter(|&(_, t)| t > 0)
            .map(|(e, t)| (e, t - 1))
            .collect();

        self.msg.update();
    }

    fn inventory_update(&mut self, ctx: &mut Canvas) {
        let player = action::player().unwrap();
        {
            let mut cursor = ctx.text_writer(V2(0, 8), 0.1, color::GAINSBORO);
            for slot_data in SLOT_DATA.iter() {
                let name = match player.equipped(slot_data.slot) {
                    Some(item) => item.name(),
                    None => "".to_string()
                };
                write!(&mut cursor, "{}] {}: {}\n",
                    slot_data.key, slot_data.name, name).unwrap();
            }
        }

        {
            let mut cursor = ctx.text_writer(V2(0, 360), 0.1, color::GAINSBORO);
            write!(&mut cursor, "Press letter to equip/unequip item. Press shift+letter to drop item.").unwrap();
        }
    }

    pub fn inventory_process(&mut self, event: Event) -> bool {
        let player = action::player().unwrap();
        match event {
            Event::Render(ctx) => { self.update(ctx); }
            Event::KeyPressed(Key::Escape) | Event::KeyPressed(Key::Tab) => {
                self.ui_state = UiState::Gameplay
            }
            Event::KeyPressed(Key::F12) => { self.screenshot_requested = true; }
            Event::KeyPressed(_) => {}

            Event::Char(ch) => {
                for slot_data in SLOT_DATA.iter() {
                    if ch == slot_data.key {
                        if slot_data.slot.is_gear_slot() {
                            // Unequip gear
                            match player.free_bag_slot() {
                                None => {
                                    // No room in bag, can't unequip until
                                    // drop something.
                                    // TODO: Message about full bag.
                                }
                                Some(swap_slot) => {
                                    player.swap_equipped(slot_data.slot, swap_slot);
                                }
                            }
                        }
                        if slot_data.slot.is_bag_slot() {
                            // Bag items get equipped if they have are gear
                            // with a preferred slot.
                            if let Some(item) = player.equipped(slot_data.slot) {
                                let equip_slots = item.equip_slots();
                                for &swap_slot in equip_slots.iter() {
                                    if player.equipped(swap_slot).is_none() {
                                        player.swap_equipped(slot_data.slot, swap_slot);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    if ch == slot_data.key.to_uppercase() {
                        // Drop item in slot.
                        if let Some(item) = player.equipped(slot_data.slot) {
                            item.place(player.location().unwrap());
                        }
                        break;
                    }
                }
            }

            _ => ()
        }
        true
    }

    /// Repaint view, update game world if needed.
    pub fn update(&mut self, ctx: &mut Canvas) {
        if self.screenshot_requested {
            use std::io::File;
            use image;
            let shot = ctx.screenshot();
            let file = File::create(&Path::new(
                    format!("/tmp/shot-{}.png", time::precise_time_s() as u64)))
                    .unwrap();
            let _ = image::ImageRgb8(shot).save(file, image::PNG);

            self.screenshot_requested = false;
        }

        ctx.clear();

        match self.ui_state {
            UiState::Gameplay => self.base_update(ctx),
            UiState::Inventory => self.inventory_update(ctx),
        }
    }

    pub fn save_game(&self) {
        let save_data = world::save();
        let mut file = File::create(&Path::new("/tmp/magog_save.json"));
        file.write_str(save_data.as_slice()).unwrap();
    }

    pub fn load_game(&mut self) {
        let path = Path::new("/tmp/magog_save.json");
        if !path.exists() { return; }
        let save_data = File::open(&path).read_to_string().unwrap();
        // TODO: Handle failed load nicely.
        world::load(save_data.as_slice()).unwrap();
    }

    fn smart_move(&mut self, dir: Dir6) {
        let player = action::player().unwrap();
        let loc = player.location().unwrap();
        for &d in vec![dir, dir + 1, dir - 1].iter() {
            let target_loc = loc + d.to_v2();
            if target_loc.has_mobs() {
                action::input(Melee(d));
                return;
            } else if player.can_step(d) {
                action::input(Step(d));
                return;
            }
        }
    }

    fn autoexplore(&mut self) -> bool {
        let player = action::player().unwrap();
        if player.is_threatened() {
            return false;
        }
        if let Some(pathing) = action::autoexplore_map(32) {
            let loc = player.location().unwrap();
            let steps = pathing.sorted_neighbors(&loc);
            if steps.len() == 0 {
                return false;
            }

            action::input(Step(loc.dir6_towards(steps[0]).unwrap()));
            return true;
        }

        false
    }

    /// Context-specific interaction with the current cell.
    fn interact(&mut self) {
        let player = action::player().unwrap();
        let loc = player.location().unwrap();
        if let Some(item) = loc.top_item() {
            player.pick_up(item);
            return;
        }
    }

    /// Process a player control keypress.
    pub fn gameplay_process_key(&mut self, key: Key) -> bool {
        if action::control_state() != AwaitingInput {
            return false;
        }

        if self.exploring {
            self.exploring = false;
        }

        match key {
            Key::Q | Key::Pad7 => { self.smart_move(NorthWest); }
            Key::W | Key::Pad8 | Key::Up => { self.smart_move(North); }
            Key::E | Key::Pad9 => { self.smart_move(NorthEast); }
            Key::A | Key::Pad1 => { self.smart_move(SouthWest); }
            Key::S | Key::Pad2 | Key::Down => { self.smart_move(South); }
            Key::D | Key::Pad3 => { self.smart_move(SouthEast); }

            Key::Enter => { self.interact(); }
            Key::X => { self.exploring = true; }

            // Open inventory
            Key::Tab => { self.ui_state = UiState::Inventory; }

            Key::F5 => { self.save_game(); }
            Key::F9 => { self.load_game(); }
            Key::F12 => { self.screenshot_requested = true; }
            _ => { return false; }
        }
        return true;
    }

    pub fn gameplay_process(&mut self, event: Event) -> bool {
        match event {
            Event::Render(ctx) => {
                self.update(ctx);
            }
            // TODO: Better quit confirmation than just pressing esc.
            Event::KeyPressed(Key::Escape) => {
                return false;
            }
            Event::KeyPressed(k) => {
                self.gameplay_process_key(k);
            }

            Event::Char(ch) => {
                // TODO: Chars and keypresses in same lookup (use variants?)
                match ch {
                    // Debug
                    '>' => { action::next_level(); }
                    _ => ()
                }
            }

            _ => ()
        }
        true
    }

    pub fn process(&mut self, event: Event) -> bool {
        match self.ui_state {
            UiState::Gameplay => self.gameplay_process(event),
            UiState::Inventory => self.inventory_process(event),
        }
    }
}

struct SlotData {
    key: char,
    slot: Slot,
    name: &'static str,
}

static SLOT_DATA: [SlotData; 34] = [
    SlotData { key: '1', slot: Slot::Spell1,     name: "Ability" },
    SlotData { key: '2', slot: Slot::Spell2,     name: "Ability" },
    SlotData { key: '3', slot: Slot::Spell3,     name: "Ability" },
    SlotData { key: '4', slot: Slot::Spell4,     name: "Ability" },
    SlotData { key: '5', slot: Slot::Spell5,     name: "Ability" },
    SlotData { key: '6', slot: Slot::Spell6,     name: "Ability" },
    SlotData { key: '7', slot: Slot::Spell7,     name: "Ability" },
    SlotData { key: '8', slot: Slot::Spell8,     name: "Ability" },
    SlotData { key: 'a', slot: Slot::Melee,      name: "Weapon " },
    SlotData { key: 'b', slot: Slot::Ranged,     name: "Ranged " },
    SlotData { key: 'c', slot: Slot::Head,       name: "Head   " },
    SlotData { key: 'd', slot: Slot::Body,       name: "Body   " },
    SlotData { key: 'e', slot: Slot::Feet,       name: "Feet   " },
    SlotData { key: 'f', slot: Slot::TrinketF,   name: "Trinket" },
    SlotData { key: 'g', slot: Slot::TrinketG,   name: "Trinket" },
    SlotData { key: 'h', slot: Slot::TrinketH,   name: "Trinket" },
    SlotData { key: 'i', slot: Slot::TrinketI,   name: "Trinket" },
    SlotData { key: 'j', slot: Slot::InventoryJ, name: "       " },
    SlotData { key: 'k', slot: Slot::InventoryK, name: "       " },
    SlotData { key: 'l', slot: Slot::InventoryL, name: "       " },
    SlotData { key: 'm', slot: Slot::InventoryM, name: "       " },
    SlotData { key: 'n', slot: Slot::InventoryN, name: "       " },
    SlotData { key: 'o', slot: Slot::InventoryO, name: "       " },
    SlotData { key: 'p', slot: Slot::InventoryP, name: "       " },
    SlotData { key: 'q', slot: Slot::InventoryQ, name: "       " },
    SlotData { key: 'r', slot: Slot::InventoryR, name: "       " },
    SlotData { key: 's', slot: Slot::InventoryS, name: "       " },
    SlotData { key: 't', slot: Slot::InventoryT, name: "       " },
    SlotData { key: 'u', slot: Slot::InventoryU, name: "       " },
    SlotData { key: 'v', slot: Slot::InventoryV, name: "       " },
    SlotData { key: 'w', slot: Slot::InventoryW, name: "       " },
    SlotData { key: 'x', slot: Slot::InventoryX, name: "       " },
    SlotData { key: 'y', slot: Slot::InventoryY, name: "       " },
    SlotData { key: 'z', slot: Slot::InventoryZ, name: "       " },
];
<|endoftext|>


<commit_before>// (C)opyleft 2013,2014 Frank Denis

/*!
 * Bindings for the GeoIP library
 */
#![desc = "Bindings for the GeoIP library."]
#![license = "BSD"]
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]

extern crate libc;
extern crate serialize;

use libc::{c_void, c_char, c_int, c_ulong, c_float};
use std::c_str::CString;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

type RawGeoIp = *const c_void;
type In6Addr = [u8, ..16];

#[repr(C)]
struct GeoIpLookup {
    netmask: c_int
}

impl GeoIpLookup {
    fn new() -> GeoIpLookup {
        GeoIpLookup {
            netmask: 0
        }
    }
}

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: *const c_char, flags: c_int) -> RawGeoIp;
    fn GeoIP_delete(db: RawGeoIp);
    fn GeoIP_name_by_ipnum_gl(db: RawGeoIp, ipnum: c_ulong, gl: *mut GeoIpLookup) -> *const c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: RawGeoIp, ipnum: In6Addr, gl: *mut GeoIpLookup) -> *const c_char;
    fn GeoIP_record_by_ipnum(db: RawGeoIp, ipnum: c_ulong) -> *const GeoIpRecord;
    fn GeoIP_record_by_ipnum_v6(db: RawGeoIp, ipnum: In6Addr) -> *const GeoIpRecord;
    fn GeoIPRecord_delete(gir: *const GeoIpRecord);
    fn GeoIP_set_charset(db: RawGeoIp, charset: c_int) -> c_int;
}

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

pub struct GeoIp {
    db: RawGeoIp
}

#[repr(C)]
pub struct GeoIpRecord {
    country_code: *const c_char,
    country_code3: *const c_char,
    country_name: *const c_char,
    region: *const c_char,
    city: *const c_char,
    postal_code: *const c_char,
    latitude: c_float,
    longitude: c_float,
    dma_code: c_int,
    area_code: c_int,
    charset: c_int,
    continent_code: *const c_char,
    netmask: c_int
}

#[deriving(Decodable, Encodable)]
pub struct ASInfo {
    pub asn: uint,
    pub name: String,
    pub netmask: uint
}

#[deriving(Decodable, Encodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: uint,
    pub area_code: uint,
    pub charset: uint,
    pub continent_code: Option<String>,
    pub netmask: uint
}

unsafe fn maybe_string(c_str: *const c_char) -> Option<String> {
    c_str.as_ref().and_then(|opt| {
        CString::new(opt, false).as_str().map(|s| s.to_string())
    })
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as uint,
            area_code: res.area_code as uint,
            charset: res.charset as uint,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as uint
        }
    }
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            Ipv4Addr(a, b, c, d) => {
                CNetworkIp::V4((a as c_ulong << 24) | (b as c_ulong << 16) |
                               (c as c_ulong << 8)  | (d as c_ulong))
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                CNetworkIp::V6([(a >> 8) as u8, a as u8,
                                (b >> 8) as u8, b as u8,
                                (c >> 8) as u8, c as u8,
                                (d >> 8) as u8, d as u8,
                                (e >> 8) as u8, e as u8,
                                (f >> 8) as u8, f as u8,
                                (g >> 8) as u8, g as u8,
                                (h >> 8) as u8, h as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            GeoIP_open(file.to_c_str().unwrap(), options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { GeoIP_set_charset(db, Charset::UTF8 as c_int) } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe { GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe { GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe { GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe { GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    from_str::<uint>(asn.slice_from(2)).unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as uint
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&from_str("/opt/geoip/GeoLiteCity.dat").unwrap(), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap().as_slice() == "Mountain View");
}
<commit_msg>unwrap() -> into_inner()<commit_after>// (C)opyleft 2013,2014 Frank Denis

/*!
 * Bindings for the GeoIP library
 */
#![desc = "Bindings for the GeoIP library."]
#![license = "BSD"]
#![crate_name = "geoip"]
#![crate_type = "rlib"]

#![warn(non_camel_case_types,
        non_upper_case_globals,
        unused_qualifications)]

extern crate libc;
extern crate serialize;

use libc::{c_void, c_char, c_int, c_ulong, c_float};
use std::c_str::CString;
use std::fmt;
use std::io::net::ip::{IpAddr,Ipv4Addr,Ipv6Addr};

type RawGeoIp = *const c_void;
type In6Addr = [u8, ..16];

#[repr(C)]
struct GeoIpLookup {
    netmask: c_int
}

impl GeoIpLookup {
    fn new() -> GeoIpLookup {
        GeoIpLookup {
            netmask: 0
        }
    }
}

#[link(name = "GeoIP")]
extern {
    fn GeoIP_open(dbtype: *const c_char, flags: c_int) -> RawGeoIp;
    fn GeoIP_delete(db: RawGeoIp);
    fn GeoIP_name_by_ipnum_gl(db: RawGeoIp, ipnum: c_ulong, gl: *mut GeoIpLookup) -> *const c_char;
    fn GeoIP_name_by_ipnum_v6_gl(db: RawGeoIp, ipnum: In6Addr, gl: *mut GeoIpLookup) -> *const c_char;
    fn GeoIP_record_by_ipnum(db: RawGeoIp, ipnum: c_ulong) -> *const GeoIpRecord;
    fn GeoIP_record_by_ipnum_v6(db: RawGeoIp, ipnum: In6Addr) -> *const GeoIpRecord;
    fn GeoIPRecord_delete(gir: *const GeoIpRecord);
    fn GeoIP_set_charset(db: RawGeoIp, charset: c_int) -> c_int;
}

enum Charset {
    UTF8 = 1
}

pub enum Options {
    Standard = 0,
    MemoryCache = 1,
    CheckCache = 2,
    IndexCache = 4,
    MmapCache = 8
}

pub enum DBType {
    CountryEdition = 1,
    RegionEditionRev0 = 7,
    CityEditionRev0 = 6,
    ORGEdition = 5,
    ISPEdition = 4,
    CityEditionRev1 = 2,
    RegionEditionRev1 = 3,
    ProxyEdition = 8,
    ASNUMEdition = 9,
    NetSpeedEdition = 10,
    DomainEdition = 11,
    CountryEditionV6 = 12,
    LocationAEdition = 13,
    AccuracyRadiusEdition = 14,
    LargeCountryEdition = 17,
    LargeCountryEditionV6 = 18,
    ASNumEditionV6 = 21,
    ISPEditionV6 = 22,
    ORGEditionV6 = 23,
    DomainEditionV6 = 24,
    LoctionAEditionV6 = 25,
    RegistrarEdition = 26,
    RegistrarEditionV6 = 27,
    UserTypeEdition = 28,
    UserTypeEditionV6 = 29,
    CityEditionRev1V6 = 30,
    CityEditionRev0V6 = 31,
    NetSpeedEditionRev1 = 32,
    NetSpeedEditionRev1V6 = 33,
    CountryConfEdition = 34,
    CityConfEdition = 35,
    RegionConfEdition = 36,
    PostalConfEdition = 37,
    AccuracyRadiusEditionV6 = 38
}

pub struct GeoIp {
    db: RawGeoIp
}

#[repr(C)]
pub struct GeoIpRecord {
    country_code: *const c_char,
    country_code3: *const c_char,
    country_name: *const c_char,
    region: *const c_char,
    city: *const c_char,
    postal_code: *const c_char,
    latitude: c_float,
    longitude: c_float,
    dma_code: c_int,
    area_code: c_int,
    charset: c_int,
    continent_code: *const c_char,
    netmask: c_int
}

#[deriving(Decodable, Encodable)]
pub struct ASInfo {
    pub asn: uint,
    pub name: String,
    pub netmask: uint
}

#[deriving(Decodable, Encodable)]
pub struct CityInfo {
    pub country_code: Option<String>,
    pub country_code3: Option<String>,
    pub country_name: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
    pub postal_code: Option<String>,
    pub latitude: f32,
    pub longitude: f32,
    pub dma_code: uint,
    pub area_code: uint,
    pub charset: uint,
    pub continent_code: Option<String>,
    pub netmask: uint
}

unsafe fn maybe_string(c_str: *const c_char) -> Option<String> {
    c_str.as_ref().and_then(|opt| {
        CString::new(opt, false).as_str().map(|s| s.to_string())
    })
}

impl CityInfo {
    unsafe fn from_geoiprecord(res: &GeoIpRecord) -> CityInfo {
        CityInfo {
            country_code: maybe_string(res.country_code),
            country_code3: maybe_string(res.country_code3),
            country_name: maybe_string(res.country_name),
            region: maybe_string(res.region),
            city: maybe_string(res.city),
            postal_code: maybe_string(res.postal_code),
            latitude: res.latitude as f32,
            longitude: res.longitude as f32,
            dma_code: res.dma_code as uint,
            area_code: res.area_code as uint,
            charset: res.charset as uint,
            continent_code: maybe_string(res.continent_code),
            netmask: res.netmask as uint
        }
    }
}

impl fmt::Show for ASInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}\t{}", self.asn, self.name)
    }
}

enum CNetworkIp {
    V4(c_ulong),
    V6(In6Addr)
}

impl CNetworkIp {
    fn new(ip: IpAddr) -> CNetworkIp {
        match ip {
            Ipv4Addr(a, b, c, d) => {
                CNetworkIp::V4((a as c_ulong << 24) | (b as c_ulong << 16) |
                               (c as c_ulong << 8)  | (d as c_ulong))
            },
            Ipv6Addr(a, b, c, d, e, f, g, h) => {
                CNetworkIp::V6([(a >> 8) as u8, a as u8,
                                (b >> 8) as u8, b as u8,
                                (c >> 8) as u8, c as u8,
                                (d >> 8) as u8, d as u8,
                                (e >> 8) as u8, e as u8,
                                (f >> 8) as u8, f as u8,
                                (g >> 8) as u8, g as u8,
                                (h >> 8) as u8, h as u8])
            }
        }
    }
}

impl GeoIp {
    pub fn open(path: &Path, options: Options) -> Result<GeoIp, String> {
        let file = match path.as_str() {
            None => return Err(format!("Invalid path {}", path.display())),
            Some(file) => file
        };
        let db = unsafe {
            GeoIP_open(file.to_c_str().into_inner(), options as c_int)
        };
        if db.is_null() {
            return Err(format!("Can't open {}", file));
        }
        if unsafe { GeoIP_set_charset(db, Charset::UTF8 as c_int) } != 0 {
            return Err("Can't set charset to UTF8".to_string());
        }
        Ok(GeoIp { db: db })
    }

    pub fn city_info_by_ip(&self, ip: IpAddr) -> Option<CityInfo> {
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe { GeoIP_record_by_ipnum(self.db, ip) },
            CNetworkIp::V6(ip) => unsafe { GeoIP_record_by_ipnum_v6(self.db, ip) }
        };

        if cres.is_null() { return None; }

        unsafe {
            let city_info = CityInfo::from_geoiprecord(&*cres);
            GeoIPRecord_delete(cres);
            std::mem::forget(cres);
            Some(city_info)
        }
    }

    pub fn as_info_by_ip(&self, ip: IpAddr) -> Option<ASInfo> {
        let mut gl = GeoIpLookup::new();
        let cres = match CNetworkIp::new(ip) {
            CNetworkIp::V4(ip) => unsafe { GeoIP_name_by_ipnum_gl(self.db, ip, &mut gl) },
            CNetworkIp::V6(ip) => unsafe { GeoIP_name_by_ipnum_v6_gl(self.db, ip, &mut gl) }
        };

        if cres.is_null() {
            return None;
        }
        let description_cstr = unsafe { CString::new(cres, true) };
        let description = match description_cstr.as_str() {
            None => return None,
            Some(description) => description
        };
        let mut di = description.splitn(1, ' ');
        let asn = match di.next() {
            None => return None,
            Some(asn) => {
                if ! asn.starts_with("AS") {
                    return None
                } else {
                    from_str::<uint>(asn.slice_from(2)).unwrap()
                }
            }
        };
        let name = di.next().unwrap_or("(none)");
        let as_info = ASInfo {
            asn: asn,
            name: name.to_string(),
            netmask: gl.netmask as uint
        };
        Some(as_info)
    }
}

impl Drop for GeoIp {
    fn drop(&mut self) {
        unsafe {
            GeoIP_delete(self.db);
        }
    }
}

#[test]
fn geoip_test_basic() {
    let geoip = match GeoIp::open(&from_str("/opt/geoip/GeoIPASNum.dat").unwrap(), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("91.203.184.192").unwrap();
    let res = geoip.as_info_by_ip(ip).unwrap();
    assert!(res.asn == 41064);
    assert!(res.name.as_slice().contains("Telefun"));
    assert!(res.netmask == 22);
}

#[test]
fn geoip_test_city() {
    let geoip = match GeoIp::open(&from_str("/opt/geoip/GeoLiteCity.dat").unwrap(), Options::MemoryCache) {
        Err(err) => panic!(err),
        Ok(geoip) => geoip
    };
    let ip = from_str("8.8.8.8").unwrap();
    let res = geoip.city_info_by_ip(ip).unwrap();
    assert!(res.city.unwrap().as_slice() == "Mountain View");
}
<|endoftext|>


<commit_before>use super::point::Point;
use super::number::NumberOps;
use super::ring::Ring;

#[inline]
pub fn h_down_intersect(y: i64, p1: Point, p2: Point, points: &mut Ring<Point>) {
   if p2.y > y {
      if p1.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y > y && p2.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(p2);
   }
}

#[inline]
pub fn h_up_intersect(y: i64, p1: Point, p2: Point, points: &mut Ring<Point>) {
   if p2.y < y {
      if p1.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y < y && p2.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(p2);
   }
}

#[inline]
pub fn v_left_intersect(x: i64, p1: Point, p2: Point, points: &mut Ring<Point>) {
   if p2.x > x {
      if p1.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x > x && p2.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(p2);
   }
}

#[inline]
pub fn v_right_intersect(x: i64, p1: Point, p2: Point, points: &mut Ring<Point>) {
   if p2.x < x {
      if p1.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x < x && p2.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(p2);
   }
}

#[inline]
pub fn h_intersect(y: i64, p1: Point, p2: Point) -> i64 {
   p1.x + ((p2.x - p1.x) * (y - p1.y)).rounding_div(p2.y - p1.y)
}

#[inline]
pub fn v_intersect(x: i64, p1: Point, p2: Point) -> i64 {
   p1.y + ((p2.y - p1.y) * (x - p1.x)).rounding_div(p2.x - p1.x)
}

pub fn hv_clip<F>(intersect: F, at: i64, points: &mut Ring<Point>) where
   F: Fn(i64, Point, Point, &mut Ring<Point>),
{
   let start = points.start();
   let end = points.end();

   points.rewind(2 * (end - start));

   let rewinded_end = points.end();

   let mut p1 = points[end-1];

   for i in start..end {
      let p2 = points[i];

      intersect(at, p1, p2, points);

      p1 = p2;
   }

   points.consume_at(rewinded_end);
}

pub fn hv_split<F>(split: F, at: i64, write: &mut Ring<Point>, read_write: &mut Ring<Point>) where
   F: Fn(i64, Point, Point, &mut Ring<Point>, &mut Ring<Point>),
{
   let start = read_write.start();
   let end = read_write.end();

   if end - start <= 2 {
      read_write.consume();
      return;
   }

   let double = 2 * (end - start);
   read_write.rewind(double);
   write.rewind(double);

   let rewinded_end = read_write.end();

   let mut p1 = read_write[end-1];

   for i in start..end {
      let p2 = read_write[i];

      split(at, p1, p2, write, read_write);

      p1 = p2;
   }

   read_write.consume_at(rewinded_end);
}

#[inline]
pub fn h_split_edge(y: i64, p1: Point, p2: Point, down: &mut Ring<Point>, up: &mut Ring<Point>) {
   if p2.y > y {
      if p1.y < y {
         let intersection = Point::new(h_intersect(y, p1, p2), y);
         down.push(intersection);
         up.push(intersection);
      }
      up.push(p2);
   } else if p2.y < y {
      if p1.y > y {
         let intersection = Point::new(h_intersect(y, p1, p2), y);
         down.push(intersection);
         up.push(intersection);
      }
      down.push(p2);
   } else {
      down.push(p2);
      up.push(p2);
   }
}

#[inline]
pub fn v_split_edge(x: i64, p1: Point, p2: Point, left: &mut Ring<Point>, right: &mut Ring<Point>) {
   if p2.x > x {
      if p1.x < x {
         let intersection = Point::new(x, v_intersect(x, p1, p2));
         left.push(intersection);
         right.push(intersection);
      }
      right.push(p2);
   } else if p2.x < x {
      if p1.x > x {
         let intersection = Point::new(x, v_intersect(x, p1, p2));
         left.push(intersection);
         right.push(intersection);
      }
      left.push(p2);
   } else {
      left.push(p2);
      right.push(p2);
   }
}

#[cfg(test)]
mod tests {
   use test::Bencher;

   use super::super::point::Point;

   use super::*;

   #[bench]
   fn bench_clip(b: &mut Bencher) {
      let mut points = Ring::new(1024 * 256);

      b.iter(|| {
         for _ in 0..1000 {
            points.consume();

            points.push(Point::new(50, 50));
            points.push(Point::new(100, 200));
            points.push(Point::new(150, 100));

            hv_clip(h_down_intersect, 150, &mut points);

            hv_clip(h_up_intersect, 80, &mut points);

            hv_clip(v_right_intersect, 70, &mut points);

            hv_clip(v_left_intersect, 140, &mut points);
         }
      });
   }

   #[test]
   fn test_clip() {
      let mut points = Ring::new(30);

      points.push(Point::new(50, 50));
      points.push(Point::new(100, 200));
      points.push(Point::new(150, 100));

      hv_clip(h_down_intersect, 150, &mut points);

      hv_clip(h_up_intersect, 80, &mut points);

      hv_clip(v_right_intersect, 70, &mut points);

      hv_clip(v_left_intersect, 140, &mut points);

      assert_eq!(points[..].len(), 7);

      assert_eq!(&points[..], [
         Point::new(140, 95),
         Point::new(110, 80),
         Point::new(70, 80),
         Point::new(70, 110),
         Point::new(83, 150),
         Point::new(125, 150),
         Point::new(140, 120),
      ]);
   }
}
<commit_msg>Missing import in the ring tests<commit_after>use super::point::Point;
use super::number::NumberOps;
use super::ring::Ring;

#[inline]
pub fn h_down_intersect(y: i64, p1: Point, p2: Point, points: &mut Ring<Point>) {
   if p2.y > y {
      if p1.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y > y && p2.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(p2);
   }
}

#[inline]
pub fn h_up_intersect(y: i64, p1: Point, p2: Point, points: &mut Ring<Point>) {
   if p2.y < y {
      if p1.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y < y && p2.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(p2);
   }
}

#[inline]
pub fn v_left_intersect(x: i64, p1: Point, p2: Point, points: &mut Ring<Point>) {
   if p2.x > x {
      if p1.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x > x && p2.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(p2);
   }
}

#[inline]
pub fn v_right_intersect(x: i64, p1: Point, p2: Point, points: &mut Ring<Point>) {
   if p2.x < x {
      if p1.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x < x && p2.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(p2);
   }
}

#[inline]
pub fn h_intersect(y: i64, p1: Point, p2: Point) -> i64 {
   p1.x + ((p2.x - p1.x) * (y - p1.y)).rounding_div(p2.y - p1.y)
}

#[inline]
pub fn v_intersect(x: i64, p1: Point, p2: Point) -> i64 {
   p1.y + ((p2.y - p1.y) * (x - p1.x)).rounding_div(p2.x - p1.x)
}

pub fn hv_clip<F>(intersect: F, at: i64, points: &mut Ring<Point>) where
   F: Fn(i64, Point, Point, &mut Ring<Point>),
{
   let start = points.start();
   let end = points.end();

   points.rewind(2 * (end - start));

   let rewinded_end = points.end();

   let mut p1 = points[end-1];

   for i in start..end {
      let p2 = points[i];

      intersect(at, p1, p2, points);

      p1 = p2;
   }

   points.consume_at(rewinded_end);
}

pub fn hv_split<F>(split: F, at: i64, write: &mut Ring<Point>, read_write: &mut Ring<Point>) where
   F: Fn(i64, Point, Point, &mut Ring<Point>, &mut Ring<Point>),
{
   let start = read_write.start();
   let end = read_write.end();

   if end - start <= 2 {
      read_write.consume();
      return;
   }

   let double = 2 * (end - start);
   read_write.rewind(double);
   write.rewind(double);

   let rewinded_end = read_write.end();

   let mut p1 = read_write[end-1];

   for i in start..end {
      let p2 = read_write[i];

      split(at, p1, p2, write, read_write);

      p1 = p2;
   }

   read_write.consume_at(rewinded_end);
}

#[inline]
pub fn h_split_edge(y: i64, p1: Point, p2: Point, down: &mut Ring<Point>, up: &mut Ring<Point>) {
   if p2.y > y {
      if p1.y < y {
         let intersection = Point::new(h_intersect(y, p1, p2), y);
         down.push(intersection);
         up.push(intersection);
      }
      up.push(p2);
   } else if p2.y < y {
      if p1.y > y {
         let intersection = Point::new(h_intersect(y, p1, p2), y);
         down.push(intersection);
         up.push(intersection);
      }
      down.push(p2);
   } else {
      down.push(p2);
      up.push(p2);
   }
}

#[inline]
pub fn v_split_edge(x: i64, p1: Point, p2: Point, left: &mut Ring<Point>, right: &mut Ring<Point>) {
   if p2.x > x {
      if p1.x < x {
         let intersection = Point::new(x, v_intersect(x, p1, p2));
         left.push(intersection);
         right.push(intersection);
      }
      right.push(p2);
   } else if p2.x < x {
      if p1.x > x {
         let intersection = Point::new(x, v_intersect(x, p1, p2));
         left.push(intersection);
         right.push(intersection);
      }
      left.push(p2);
   } else {
      left.push(p2);
      right.push(p2);
   }
}

#[cfg(test)]
mod tests {
   use test::Bencher;

   use super::super::point::Point;
   use super::super::ring::Ring;

   use super::*;

   #[bench]
   fn bench_clip(b: &mut Bencher) {
      let mut points = Ring::new(1024 * 256);

      b.iter(|| {
         for _ in 0..1000 {
            points.consume();

            points.push(Point::new(50, 50));
            points.push(Point::new(100, 200));
            points.push(Point::new(150, 100));

            hv_clip(h_down_intersect, 150, &mut points);

            hv_clip(h_up_intersect, 80, &mut points);

            hv_clip(v_right_intersect, 70, &mut points);

            hv_clip(v_left_intersect, 140, &mut points);
         }
      });
   }

   #[test]
   fn test_clip() {
      let mut points = Ring::new(30);

      points.push(Point::new(50, 50));
      points.push(Point::new(100, 200));
      points.push(Point::new(150, 100));

      hv_clip(h_down_intersect, 150, &mut points);

      hv_clip(h_up_intersect, 80, &mut points);

      hv_clip(v_right_intersect, 70, &mut points);

      hv_clip(v_left_intersect, 140, &mut points);

      assert_eq!(points[..].len(), 7);

      assert_eq!(&points[..], [
         Point::new(140, 95),
         Point::new(110, 80),
         Point::new(70, 80),
         Point::new(70, 110),
         Point::new(83, 150),
         Point::new(125, 150),
         Point::new(140, 120),
      ]);
   }
}
<|endoftext|>


<commit_before>use std::ptr;
use std::mem;
use std::str;
use std::ffi::CString;

use gl;
use gl::types::*;

use bootstrap::window::Window;
use bootstrap::gl_utils::{self, GLContext};

use math::point::Point;
use math::matrix::Matrix4;
use geometry::mesh::Mesh;
use geometry::face::Face;

#[allow(dead_code)]
pub struct GLRender {
    context: GLContext // TODO: do we need to hold onto the context?
}

pub struct GLMeshData {
    array_buffer: GLuint,
    vertex_buffer: GLuint,
    index_buffer: GLuint,
    shader: GLuint,
    element_count: usize
}

pub fn init(window: &Window) -> GLRender {
    gl_utils::init(window);
    let context = gl_utils::create_context(window);

    GLRender {
        context: context
    }
}

// pub fn tear_down(renderer: &GLRender) {
//     gl_utils::destroy_context(renderer.context);
// }

impl GLRender {
    pub fn gen_mesh(&self, mesh: &Mesh, vertex_src: &str, frag_src: &str) -> GLMeshData {

        // generate array buffer
        let mut array_buffer = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut array_buffer);
            gl::BindVertexArray(array_buffer);
        }

        // generate vertex buffer,
        // passing the raw data held by the mesh
        let mut vertex_buffer = 0;
        unsafe {
            gl::GenBuffers(1, &mut vertex_buffer);
            gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);

            gl::BufferData(gl::ARRAY_BUFFER,
                           (mesh.vertices.len() * mem::size_of::<Point>()) as GLsizeiptr,
                           mem::transmute(&(mesh.vertices[0].x)),
                           gl::STATIC_DRAW);
        }

        let mut index_buffer = 0;
        unsafe {
            gl::GenBuffers(1, &mut index_buffer);
            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer);

            gl::BufferData(gl::ELEMENT_ARRAY_BUFFER,
                           (mesh.faces.len() * mem::size_of::<Face>()) as GLsizeiptr,
                           mem::transmute(&(mesh.faces[0].indices[0])),
                           gl::STATIC_DRAW);
        }

        // TODO: do some handling of errors here?
        let vs = GLRender::compile_shader(vertex_src, gl::VERTEX_SHADER);
        let fs = GLRender::compile_shader(frag_src, gl::FRAGMENT_SHADER);
        let program = GLRender::link_program(vs, fs);

        // TODO: unbind buffers and stuff?

        GLMeshData {
            array_buffer: array_buffer,
            vertex_buffer: vertex_buffer,
            index_buffer: index_buffer,
            shader: program,
            element_count: mesh.faces.len() * 3
        }
    }

    /// TODO: make this a member of GLMeshData?
    pub fn draw_mesh(&self, mesh: &GLMeshData, transform: Matrix4) { unsafe {

        // Bind the buffers for the mesh
        gl::BindVertexArray(mesh.array_buffer);
        gl::BindBuffer(gl::ARRAY_BUFFER, mesh.vertex_buffer);
        gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, mesh.index_buffer);

        // Set the shader to use
        gl::UseProgram(mesh.shader);

        // Specify the layout of the vertex data
        let vertex_pos_location = gl::GetAttribLocation(
            mesh.shader,
            CString::new(b"vertexPosition").unwrap().as_ptr()); // TODO: Write a helper to make using cstrings easier.
        gl::VertexAttribPointer(
            vertex_pos_location as GLuint,
            4,
            gl::FLOAT,
            gl::FALSE,
            mem::size_of::<Point>() as GLsizei,
            ptr::null());
        gl::EnableVertexAttribArray(vertex_pos_location as GLuint);

        let transform_location =
            gl::GetUniformLocation(mesh.shader, CString::new(b"modelTransform").unwrap().as_ptr());
        gl::UniformMatrix4fv(transform_location,
                             1,
                             gl::TRUE,
                             transform.raw_data());

        // TODO don't clear for every mesh
        gl::ClearColor(0.3, 0.3, 0.3, 1.0);
        gl::Clear(gl::COLOR_BUFFER_BIT);

        gl::DrawElements(gl::TRIANGLES,
                         mesh.element_count as GLsizei,
                         gl::UNSIGNED_INT,
                         0 as *const GLvoid);

        gl_utils::swap_buffers(); // TODO don't swap buffers after every draw
    } }

    fn compile_shader(src: &str, ty: GLenum) -> GLuint {
        unsafe {
            let shader = gl::CreateShader(ty);

            // Attempt to compile the shader
            let c_str = CString::new(src.as_bytes());
            gl::ShaderSource(shader, 1, &c_str.unwrap().as_ptr(), ptr::null());
            gl::CompileShader(shader);

            // Get the compile status
            let mut status = gl::FALSE as GLint;
            gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

            // Fail on error
            if status != (gl::TRUE as GLint) {
                let mut len = 0;
                gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
                let mut buf = Vec::with_capacity(len as usize);
                buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character
                gl::GetShaderInfoLog(shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
                panic!("{}", str::from_utf8(buf.as_slice()).ok().expect("ShaderInfoLog not valid utf8"));
            }

            shader
        }
    }

    fn link_program(vs: GLuint, fs: GLuint) -> GLuint {
        unsafe {
            let program = gl::CreateProgram();
            gl::AttachShader(program, vs);
            gl::AttachShader(program, fs);
            gl::LinkProgram(program);

            // Get the link status
            let mut status = gl::FALSE as GLint;
            gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

            // Fail on error
            if status != (gl::TRUE as GLint) {
                let mut len: GLint = 0;
                gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
                let mut buf = Vec::with_capacity(len as usize);
                buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character
                gl::GetProgramInfoLog(program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
                panic!("{}", str::from_utf8(buf.as_slice()).ok().expect("ProgramInfoLog not valid utf8"));
            }

            program
        }
    }
}
<commit_msg>Enable Depth Checking - Enables depth checking for GLRender.<commit_after>use std::ptr;
use std::mem;
use std::str;
use std::ffi::CString;

use gl;
use gl::types::*;

use bootstrap::window::Window;
use bootstrap::gl_utils::{self, GLContext};

use math::point::Point;
use math::matrix::Matrix4;
use geometry::mesh::Mesh;
use geometry::face::Face;

#[allow(dead_code)]
pub struct GLRender {
    context: GLContext // TODO: do we need to hold onto the context?
}

pub struct GLMeshData {
    array_buffer: GLuint,
    vertex_buffer: GLuint,
    index_buffer: GLuint,
    shader: GLuint,
    element_count: usize
}

// TODO: This should be GLRender::new() for consistency.
pub fn init(window: &Window) -> GLRender {
    gl_utils::init(window);
    let context = gl_utils::create_context(window);

    // do some basic configuration stuff
    unsafe {
        gl::Enable(gl::DEPTH_TEST);
        gl::ClearColor(0.3, 0.3, 0.3, 1.0);
        gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); // TODO: Do we need to clear here?
    }

    GLRender {
        context: context
    }
}

// TODO: This should be Drop for GLRender.
// pub fn tear_down(renderer: &GLRender) {
//     gl_utils::destroy_context(renderer.context);
// }

impl GLRender {
    pub fn gen_mesh(&self, mesh: &Mesh, vertex_src: &str, frag_src: &str) -> GLMeshData {

        // generate array buffer
        let mut array_buffer = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut array_buffer);
            gl::BindVertexArray(array_buffer);
        }

        // generate vertex buffer,
        // passing the raw data held by the mesh
        let mut vertex_buffer = 0;
        unsafe {
            gl::GenBuffers(1, &mut vertex_buffer);
            gl::BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);

            gl::BufferData(gl::ARRAY_BUFFER,
                           (mesh.vertices.len() * mem::size_of::<Point>()) as GLsizeiptr,
                           mem::transmute(&(mesh.vertices[0].x)),
                           gl::STATIC_DRAW);
        }

        let mut index_buffer = 0;
        unsafe {
            gl::GenBuffers(1, &mut index_buffer);
            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer);

            gl::BufferData(gl::ELEMENT_ARRAY_BUFFER,
                           (mesh.faces.len() * mem::size_of::<Face>()) as GLsizeiptr,
                           mem::transmute(&(mesh.faces[0].indices[0])),
                           gl::STATIC_DRAW);
        }

        // TODO: do some handling of errors here?
        let vs = GLRender::compile_shader(vertex_src, gl::VERTEX_SHADER);
        let fs = GLRender::compile_shader(frag_src, gl::FRAGMENT_SHADER);
        let program = GLRender::link_program(vs, fs);

        // TODO: unbind buffers and stuff?

        GLMeshData {
            array_buffer: array_buffer,
            vertex_buffer: vertex_buffer,
            index_buffer: index_buffer,
            shader: program,
            element_count: mesh.faces.len() * 3
        }
    }

    /// TODO: make this a member of GLMeshData?
    pub fn draw_mesh(&self, mesh: &GLMeshData, transform: Matrix4) { unsafe {

        // Bind the buffers for the mesh
        gl::BindVertexArray(mesh.array_buffer);
        gl::BindBuffer(gl::ARRAY_BUFFER, mesh.vertex_buffer);
        gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, mesh.index_buffer);

        // Set the shader to use
        gl::UseProgram(mesh.shader);

        // Specify the layout of the vertex data
        let vertex_pos_location = gl::GetAttribLocation(
            mesh.shader,
            CString::new(b"vertexPosition").unwrap().as_ptr()); // TODO: Write a helper to make using cstrings easier.
        gl::VertexAttribPointer(
            vertex_pos_location as GLuint,
            4,
            gl::FLOAT,
            gl::FALSE,
            mem::size_of::<Point>() as GLsizei,
            ptr::null());
        gl::EnableVertexAttribArray(vertex_pos_location as GLuint);

        let transform_location =
            gl::GetUniformLocation(mesh.shader, CString::new(b"modelTransform").unwrap().as_ptr());
        gl::UniformMatrix4fv(transform_location,
                             1,
                             gl::TRUE,
                             transform.raw_data());

        // TODO don't clear for every mesh
        gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);

        gl::DrawElements(gl::TRIANGLES,
                         mesh.element_count as GLsizei,
                         gl::UNSIGNED_INT,
                         0 as *const GLvoid);

        gl_utils::swap_buffers(); // TODO don't swap buffers after every draw
    } }

    fn compile_shader(src: &str, ty: GLenum) -> GLuint {
        unsafe {
            let shader = gl::CreateShader(ty);

            // Attempt to compile the shader
            let c_str = CString::new(src.as_bytes());
            gl::ShaderSource(shader, 1, &c_str.unwrap().as_ptr(), ptr::null());
            gl::CompileShader(shader);

            // Get the compile status
            let mut status = gl::FALSE as GLint;
            gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

            // Fail on error
            if status != (gl::TRUE as GLint) {
                let mut len = 0;
                gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);
                let mut buf = Vec::with_capacity(len as usize);
                buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character
                gl::GetShaderInfoLog(shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
                panic!("{}", str::from_utf8(buf.as_slice()).ok().expect("ShaderInfoLog not valid utf8"));
            }

            shader
        }
    }

    fn link_program(vs: GLuint, fs: GLuint) -> GLuint {
        unsafe {
            let program = gl::CreateProgram();
            gl::AttachShader(program, vs);
            gl::AttachShader(program, fs);
            gl::LinkProgram(program);

            // Get the link status
            let mut status = gl::FALSE as GLint;
            gl::GetProgramiv(program, gl::LINK_STATUS, &mut status);

            // Fail on error
            if status != (gl::TRUE as GLint) {
                let mut len: GLint = 0;
                gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);
                let mut buf = Vec::with_capacity(len as usize);
                buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character
                gl::GetProgramInfoLog(program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
                panic!("{}", str::from_utf8(buf.as_slice()).ok().expect("ProgramInfoLog not valid utf8"));
            }

            program
        }
    }
}
<|endoftext|>


<commit_before>//! Complete system.

use std::collections::BinaryHeap;

use Result;
use platform::Platform;
use schedule::Schedule;
use traffic::Traffic;
use workload::Workload;

mod event;
mod history;
mod job;

pub use self::event::{Event, EventKind};
pub use self::history::History;
pub use self::job::Job;

/// A complete system.
pub struct System<T, W, P, S> {
    traffic: T,
    workload: W,
    platform: P,
    schedule: S,
    history: History,
    queue: BinaryHeap<Event>,
}

impl<T, W, P, S, D> System<T, W, P, S>
    where T: Traffic, W: Workload, P: Platform<Data=D>, S: Schedule<Data=D>
{
    /// Create a system.
    pub fn new(traffic: T, workload: W, platform: P, schedule: S) -> Result<System<T, W, P, S>> {
        Ok(System {
            traffic: traffic,
            workload: workload,
            platform: platform,
            schedule: schedule,
            history: History::default(),
            queue: BinaryHeap::new(),
        })
    }

    /// Advance to the next event and return the data accumulated since the
    /// previous call.
    pub fn next(&mut self) -> Result<Option<(Event, P::Data)>> {
        match (try!(self.traffic.peek()), self.queue.peek().map(|event| &event.time)) {
            (Some(&traffic), Some(&queue)) => {
                if traffic < queue {
                    self.next_from_traffic()
                } else {
                    self.next_from_queue()
                }
            },
            (Some(_), None) => self.next_from_traffic(),
            (None, Some(_)) => self.next_from_queue(),
            _ => Ok(None),
        }
    }

    /// Return the platform.
    #[inline(always)]
    pub fn platform(&self) -> &P {
        &self.platform
    }

    /// Return the history.
    #[inline(always)]
    pub fn history(&self) -> &History {
        &self.history
    }

    fn next_from_traffic(&mut self) -> Result<Option<(Event, P::Data)>> {
        let time = some!(try!(self.traffic.next()));
        let pattern = try!(self.workload.next(time));
        let job = Job::new(self.history.arrived, time, pattern);

        let event = Event::arrived(time, job.clone());
        self.history.count(&event);

        let data = try!(self.platform.next(time));
        try!(self.schedule.push(time, &data));

        let decision = try!(self.schedule.next(&job));
        self.queue.push(Event::started(decision.start, job.clone()));
        self.queue.push(Event::finished(decision.finish, job.clone()));

        try!(self.platform.push(&job, &decision));

        Ok(Some((event, data)))
    }

    fn next_from_queue(&mut self) -> Result<Option<(Event, P::Data)>> {
        let event = some!(self.queue.pop());
        self.history.count(&event);

        let data = try!(self.platform.next(event.time));
        try!(self.schedule.push(event.time, &data));

        Ok(Some((event, data)))
    }
}
<commit_msg>A cosmetic change<commit_after>//! Complete system.

use std::collections::BinaryHeap;

use Result;
use platform::Platform;
use schedule::Schedule;
use traffic::Traffic;
use workload::Workload;

mod event;
mod history;
mod job;

pub use self::event::{Event, EventKind};
pub use self::history::History;
pub use self::job::Job;

/// A complete system.
pub struct System<T, W, P, S> {
    traffic: T,
    workload: W,
    platform: P,
    schedule: S,
    history: History,
    queue: BinaryHeap<Event>,
}

impl<T, W, P, S, D> System<T, W, P, S>
    where T: Traffic, W: Workload, P: Platform<Data=D>, S: Schedule<Data=D>
{
    /// Create a system.
    pub fn new(traffic: T, workload: W, platform: P, schedule: S) -> Result<System<T, W, P, S>> {
        Ok(System {
            traffic: traffic,
            workload: workload,
            platform: platform,
            schedule: schedule,
            history: History::default(),
            queue: BinaryHeap::new(),
        })
    }

    /// Advance to the next event and return the data accumulated since the
    /// previous call.
    pub fn next(&mut self) -> Result<Option<(Event, P::Data)>> {
        match (try!(self.traffic.peek()), self.queue.peek().map(|event| &event.time)) {
            (Some(&traffic), Some(&queue)) => if traffic < queue {
                self.next_from_traffic()
            } else {
                self.next_from_queue()
            },
            (Some(_), None) => self.next_from_traffic(),
            (None, Some(_)) => self.next_from_queue(),
            _ => Ok(None),
        }
    }

    /// Return the platform.
    #[inline(always)]
    pub fn platform(&self) -> &P {
        &self.platform
    }

    /// Return the history.
    #[inline(always)]
    pub fn history(&self) -> &History {
        &self.history
    }

    fn next_from_traffic(&mut self) -> Result<Option<(Event, P::Data)>> {
        let time = some!(try!(self.traffic.next()));
        let pattern = try!(self.workload.next(time));
        let job = Job::new(self.history.arrived, time, pattern);

        let event = Event::arrived(time, job.clone());
        self.history.count(&event);

        let data = try!(self.platform.next(time));
        try!(self.schedule.push(time, &data));

        let decision = try!(self.schedule.next(&job));
        self.queue.push(Event::started(decision.start, job.clone()));
        self.queue.push(Event::finished(decision.finish, job.clone()));

        try!(self.platform.push(&job, &decision));

        Ok(Some((event, data)))
    }

    fn next_from_queue(&mut self) -> Result<Option<(Event, P::Data)>> {
        let event = some!(self.queue.pop());
        self.history.count(&event);

        let data = try!(self.platform.next(event.time));
        try!(self.schedule.push(event.time, &data));

        Ok(Some((event, data)))
    }
}
<|endoftext|>