/*
 * Copyright 2018 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// import the flatbuffers runtime library
extern crate flatbuffers;

// import the generated code
#[allow(dead_code, unused_imports)]
#[allow(clippy::all)]
mod rust_generated;
pub use rust_generated::my_game::sample::{
    Color, Equipment, Monster, MonsterArgs, Vec3, Weapon, WeaponArgs,
};

// Example how to use FlatBuffers to create and read binary buffers.
#[allow(clippy::float_cmp)]
fn main() {
    // Build up a serialized buffer algorithmically.
    // Initialize it with a capacity of 1024 bytes.
    let mut builder = flatbuffers::FlatBufferBuilder::with_capacity(1024);

    // Serialize some weapons for the Monster: A 'sword' and an 'axe'.
    let weapon_one_name = builder.create_string("Sword");
    let weapon_two_name = builder.create_string("Axe");

    // Use the `Weapon::create` shortcut to create Weapons with named field
    // arguments.
    let sword =
        Weapon::create(&mut builder, &WeaponArgs { name: Some(weapon_one_name), damage: 3 });
    let axe = Weapon::create(&mut builder, &WeaponArgs { name: Some(weapon_two_name), damage: 5 });

    // Name of the Monster.
    let name = builder.create_string("Orc");

    // Inventory.
    let inventory = builder.create_vector(&[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9]);

    // Create a FlatBuffer `vector` that contains offsets to the sword and axe
    // we created above.
    let weapons = builder.create_vector(&[sword, axe]);

    // Create the path vector of Vec3 objects:
    //let x = Vec3::new(1.0, 2.0, 3.0);
    //let y = Vec3::new(4.0, 5.0, 6.0);
    //let path = builder.create_vector(&[x, y]);

    // Note that, for convenience, it is also valid to create a vector of
    // references to structs, like this:
    // let path = builder.create_vector(&[&x, &y]);

    // Create the monster using the `Monster::create` helper function. This
    // function accepts a `MonsterArgs` struct, which supplies all of the data
    // needed to build a `Monster`. To supply empty/default fields, just use the
    // Rust built-in `Default::default()` function, as demonstrated below.
    let orc = Monster::create(
        &mut builder,
        &MonsterArgs {
            pos: Some(&Vec3::new(1.0f32, 2.0f32, 3.0f32)),
            mana: 150,
            hp: 80,
            name: Some(name),
            inventory: Some(inventory),
            color: Color::Red,
            weapons: Some(weapons),
            equipped_type: Equipment::Weapon,
            equipped: Some(axe.as_union_value()),
            //path: Some(path),
            ..Default::default()
        },
    );

    // Serialize the root of the object, without providing a file identifier.
    builder.finish(orc, None);

    // We now have a FlatBuffer we can store on disk or send over a network.

    // ** file/network code goes here :) **

    // Instead, we're going to access it right away (as if we just received it).
    // This must be called after `finish()`.
    let buf = builder.finished_data(); // Of type `&[u8]`

    // Get access to the root:
    let monster = flatbuffers::root::<Monster>(buf).unwrap();

    // Get and test some scalar types from the FlatBuffer.
    let hp = monster.hp();
    let mana = monster.mana();
    let name = monster.name();

    assert_eq!(hp, 80);
    assert_eq!(mana, 150); // default
    assert_eq!(name, Some("Orc"));

    // Get and test a field of the FlatBuffer's `struct`.
    assert!(monster.pos().is_some());
    let pos = monster.pos().unwrap();
    let x = pos.x();
    let y = pos.y();
    let z = pos.z();
    assert_eq!(x, 1.0f32);
    assert_eq!(y, 2.0f32);
    assert_eq!(z, 3.0f32);

    // Get an element from the `inventory` FlatBuffer's `vector`.
    assert!(monster.inventory().is_some());
    let inv = monster.inventory().unwrap();
    let third_item = inv.get(2);
    assert_eq!(third_item, 2);

    // Get and test the `weapons` FlatBuffers's `vector`.
    assert!(monster.weapons().is_some());
    let weps = monster.weapons().unwrap();
    //let weps_len = weps.len();
    let wep2 = weps.get(1);
    let second_weapon_name = wep2.name();
    let second_weapon_damage = wep2.damage();
    assert_eq!(second_weapon_name, Some("Axe"));
    assert_eq!(second_weapon_damage, 5);

    // Get and test the `Equipment` union (`equipped` field).
    assert_eq!(monster.equipped_type(), Equipment::Weapon);
    let equipped = monster.equipped_as_weapon().unwrap();
    let weapon_name = equipped.name();
    let weapon_damage = equipped.damage();
    assert_eq!(weapon_name, Some("Axe"));
    assert_eq!(weapon_damage, 5);

    // Get and test the `path` FlatBuffers's `vector`.
    //assert_eq!(monster.path().unwrap().len(), 2);
    //assert_eq!(monster.path().unwrap()[0].x(), 1.0);
    //assert_eq!(monster.path().unwrap()[1].x(), 4.0);

    println!("The FlatBuffer was successfully created and accessed!");
    dbg!(monster);
}

#[cfg(test)]
#[test]
fn test_main() {
    main()
}
