Documentation
pub mod array;
pub mod dynamic_object;
pub mod typed_array;

use std::any::Any;
use object::Object;
use function::Function;
use value::{Value, ValueContext};
use executor::ExecutorImpl;
use errors::{VMError, FieldNotFoundError};
use generic_arithmetic;
use self::typed_array::TypedArray;
use self::typed_array::TypedArrayElement;

pub struct BuiltinObject {

}

impl BuiltinObject {
    pub fn new() -> BuiltinObject {
        BuiltinObject {}
    }
}

impl Object for BuiltinObject {
    fn get_children(&self) -> Vec<usize> {
        Vec::new()
    }

    fn as_any(&self) -> &Any {
        self as &Any
    }

    fn as_any_mut(&mut self) -> &mut Any {
        self as &mut Any
    }

    fn call_field(&self, name: &str, executor: &mut ExecutorImpl) -> Value {
        match name {
            "new_array" => {
                let array_obj: Box<Object> = Box::new(array::Array::new());
                Value::Object(
                    executor.get_object_pool_mut().allocate(array_obj)
                )
            },
            "new_dynamic" => {
                let prototype = match executor.get_current_frame().must_get_argument(0) {
                    Value::Object(id) => Some(id),
                    Value::Null => None,
                    _ => panic!(VMError::from("Invalid prototype object"))
                };
                Value::Object(executor.get_object_pool_mut().allocate(
                    Box::new(dynamic_object::DynamicObject::new(prototype))
                ))
            },
            "freeze_dynamic" => {
                let target_id = match executor.get_current_frame().must_get_argument(0) {
                    Value::Object(id) => id,
                    _ => panic!(VMError::from("Invalid target object"))
                };
                let target: &dynamic_object::DynamicObject = executor.get_object_pool().must_get_direct_typed(target_id);
                target.freeze();
                Value::Null
            },
            "optimize" => {
                let target_id = match executor.get_current_frame().must_get_argument(0) {
                    Value::Object(id) => id,
                    _ => panic!(VMError::from("Invalid target object"))
                };
                let target = executor.get_object_pool().must_get_typed::<Function>(target_id);
                target.dynamic_optimize(executor.get_object_pool_mut());
                Value::Null
            },
            "new_typed_array" => {
                let type_name = ValueContext::new(
                    &executor.get_current_frame().must_get_argument(0),
                    executor.get_object_pool()
                ).to_str().to_string();
                let size = ValueContext::new(
                    &executor.get_current_frame().must_get_argument(1),
                    executor.get_object_pool()
                ).to_i64() as usize;
                let default_value = executor.get_current_frame().must_get_argument(2);

                let obj_id = executor.get_object_pool_mut().allocate(match type_name.as_str() {
                    "i8" => Box::new(TypedArray::new(
                        i8::must_from_value(default_value),
                        size
                    )),
                    "u8" => Box::new(TypedArray::new(
                        u8::must_from_value(default_value),
                        size
                    )),
                    "i16" => Box::new(TypedArray::new(
                        i16::must_from_value(default_value),
                        size
                    )),
                    "u16" => Box::new(TypedArray::new(
                        u16::must_from_value(default_value),
                        size
                    )),
                    "i32" => Box::new(TypedArray::new(
                        i32::must_from_value(default_value),
                        size
                    )),
                    "u32" => Box::new(TypedArray::new(
                        u32::must_from_value(default_value),
                        size
                    )),
                    "i64" => Box::new(TypedArray::new(
                        i64::must_from_value(default_value),
                        size
                    )),
                    "u64" => Box::new(TypedArray::new(
                        u64::must_from_value(default_value),
                        size
                    )),
                    _ => panic!(VMError::from("Unknown type"))
                });
                Value::Object(obj_id)
            },
            "add" => {
                let (left, right) = (executor.get_current_frame().must_get_argument(0), executor.get_current_frame().must_get_argument(1));
                generic_arithmetic::exec_add(executor, left, right)
            },
            "sub" => {
                let (left, right) = (executor.get_current_frame().must_get_argument(0), executor.get_current_frame().must_get_argument(1));
                generic_arithmetic::exec_sub(executor, left, right)
            },
            "mul" => {
                let (left, right) = (executor.get_current_frame().must_get_argument(0), executor.get_current_frame().must_get_argument(1));
                generic_arithmetic::exec_mul(executor, left, right)
            },
            "div" => {
                let (left, right) = (executor.get_current_frame().must_get_argument(0), executor.get_current_frame().must_get_argument(1));
                generic_arithmetic::exec_div(executor, left, right)
            },
            "mod" => {
                let (left, right) = (executor.get_current_frame().must_get_argument(0), executor.get_current_frame().must_get_argument(1));
                generic_arithmetic::exec_mod(executor, left, right)
            },
            "pow" => {
                let (left, right) = (executor.get_current_frame().must_get_argument(0), executor.get_current_frame().must_get_argument(1));
                generic_arithmetic::exec_pow(executor, left, right)
            },
            _ => panic!(VMError::from(FieldNotFoundError::from_field_name(name)))
        }
    }
}