use super::project;

#[test]
fn method() {
    project()
        .file(
            "foo.webidl",
            r#"
                [Constructor(double value)]
                interface Foo {
                    [Pure]
                    boolean myCmp(Foo bar);
                };
            "#,
        )
        .file(
            "foo.js",
            r#"
                export class Foo {
                    constructor(value) {
                        this.value = value;
                    }
                    myCmp(other) {
                        return this.value === other.value;
                    }
                }
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                #![feature(use_extern_macros)]

                extern crate wasm_bindgen;

                use wasm_bindgen::prelude::*;

                pub mod foo;

                use foo::Foo;

                #[wasm_bindgen]
                pub fn test() {
                    let pi = Foo::new(3.14159).unwrap();
                    let e = Foo::new(2.71828).unwrap();
                    assert!(pi.my_cmp(&pi));
                    assert!(!pi.my_cmp(&e));
                    assert!(!e.my_cmp(&pi));
                    assert!(e.my_cmp(&e));
                }
            "#,
        )
        .test();
}

#[test]
fn property() {
    project()
        .file(
            "foo.webidl",
            r#"
                [Constructor(double value)]
                interface Foo {
                    [Pure]
                    attribute double value;
                };
            "#,
        )
        .file(
            "foo.js",
            r#"
                export class Foo {
                    constructor(value) {
                        this._value = value;
                    }

                    get value() {
                        return this._value;
                    }

                    set value(value) {
                        this._value = value;
                    }
                }
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                #![feature(use_extern_macros)]

                extern crate wasm_bindgen;

                use wasm_bindgen::prelude::*;

                pub mod foo;

                use foo::Foo;

                #[wasm_bindgen]
                pub fn test() {
                    let x = Foo::new(3.14159).unwrap();
                    assert_eq!(x.value(), 3.14159);
                    assert_ne!(x.value(), 2.71828);
                    x.set_value(2.71828);
                    assert_ne!(x.value(), 3.14159);
                    assert_eq!(x.value(), 2.71828);
                }
            "#,
        )
        .test();
}

#[test]
fn named_constructor() {
    project()
        .file(
            "foo.webidl",
            r#"
                [NamedConstructor=Bar(double value)]
                interface Foo {
                    [Pure]
                    readonly attribute double value;
                };
            "#,
        )
        .file(
            // Not a perfect test, but it gets the job done.
            "foo.js",
            r#"
                export class Foo {
                    constructor() {
                        this._value = 0;
                    }

                    get value(){
                        return this._value;
                    }
                }

                export class Bar extends Foo {
                    constructor(_value) {
                        super();
                        this._value = _value;
                    }
                }
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                #![feature(use_extern_macros)]

                extern crate wasm_bindgen;

                use wasm_bindgen::prelude::*;

                pub mod foo;

                use foo::Foo;

                #[wasm_bindgen]
                pub fn test() {
                    let x = Foo::new(3.14159).unwrap();
                    assert_eq!(x.value(), 3.14159);
                    assert_ne!(x.value(), 0.);
                }
            "#,
        )
        .test();
}

#[test]
fn static_method() {
    project()
        .file(
            "foo.webidl",
            r#"
                interface Foo {
                    static double swap(double value);
                };
            "#,
        )
        .file(
            "foo.js",
            r#"
                export class Foo {
                    static swap(value) {
                        const res = Foo.value;
                        Foo.value = value;
                        return res;
                    }
                }

                Foo.value = 0;
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                #![feature(use_extern_macros)]

                extern crate wasm_bindgen;

                use wasm_bindgen::prelude::*;

                pub mod foo;

                use foo::Foo;

                #[wasm_bindgen]
                pub fn test() {
                    assert_eq!(Foo::swap(3.14159), 0.);
                    assert_eq!(Foo::swap(2.71828), 3.14159);
                    assert_ne!(Foo::swap(2.71828), 3.14159);
                    assert_eq!(Foo::swap(3.14159), 2.71828);
                    assert_ne!(Foo::swap(3.14159), 2.71828);
                }
            "#,
        )
        .test();
}

#[test]
fn static_property() {
    project()
        .file(
            "foo.webidl",
            r#"
                interface Foo {
                    static attribute double value;
                };
            "#,
        )
        .file(
            "foo.js",
            r#"
                export class Foo {
                    static get value(){
                        return Foo._value;
                    }

                    static set value(value) {
                        Foo._value = value;
                    }
                }

                Foo._value = 0;
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                #![feature(use_extern_macros)]

                extern crate wasm_bindgen;

                use wasm_bindgen::prelude::*;

                pub mod foo;

                use foo::Foo;

                #[wasm_bindgen]
                pub fn test() {
                    assert_eq!(Foo::value(), 0.);
                    Foo::set_value(3.14159);
                    assert_eq!(Foo::value(), 3.14159);
                    assert_ne!(Foo::value(), 2.71828);
                    Foo::set_value(2.71828);
                    assert_eq!(Foo::value(), 2.71828);
                    assert_ne!(Foo::value(), 3.14159);
                }
            "#,
        )
        .test();
}

#[test]
fn one_method_using_an_undefined_import_doesnt_break_all_other_methods() {
    project()
        .file(
            "foo.webidl",
            r#"
                [Constructor()]
                interface Foo {
                    boolean ok_method();
                    boolean bad_method(UndefinedType undef);
                };
            "#,
        )
        .file(
            "foo.js",
            r#"
                export class Foo {
                    constructor() {}
                    ok_method() {
                        return true;
                    }
                }
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                #![feature(use_extern_macros)]
                extern crate wasm_bindgen;
                use wasm_bindgen::prelude::*;

                pub mod foo;

                #[wasm_bindgen]
                pub fn test() {
                    let f = foo::Foo::new().unwrap();
                    assert!(f.ok_method());
                }
            "#,
        )
        .test();
}

#[test]
fn unforgeable_is_structural() {
    project()
        .file(
            "foo.webidl",
            r#"
                [Constructor()]
                interface Foo {
                    [Unforgeable] readonly attribute short uno;
                                  readonly attribute short dos;
                };
            "#,
        )
        .file(
            "foo.js",
            r#"
                export class Foo {
                    constructor() {
                        this.uno = 1;
                    }
                    get dos() {
                        return 2;
                    }
                }
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                #![feature(use_extern_macros)]
                extern crate wasm_bindgen;
                use wasm_bindgen::prelude::*;

                pub mod foo;

                #[wasm_bindgen]
                pub fn test() {
                    let f = foo::Foo::new().unwrap();
                    assert_eq!(f.uno(), 1);
                    assert_eq!(f.dos(), 2);
                }
            "#,
        )
        .test();
}

#[test]
fn partial_interface() {
    project()
        .file(
            "foo.webidl",
            r#"
                [Constructor]
                interface Foo {
                    readonly attribute short un;
                    short deux();
                };

                partial interface Foo {
                    readonly attribute short trois;
                    short quatre();
                };
            "#,
        )
        .file(
            "foo.js",
            r#"
                export class Foo {
                    get un() {
                        return 1;
                    }
                    deux() {
                        return 2;
                    }
                    get trois() {
                        return 3;
                    }
                    quatre() {
                        return 4;
                    }
                }
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                #![feature(use_extern_macros)]
                extern crate wasm_bindgen;
                use wasm_bindgen::prelude::*;

                pub mod foo;

                #[wasm_bindgen]
                pub fn test() {
                    let f = foo::Foo::new().unwrap();
                    assert_eq!(f.un(), 1);
                    assert_eq!(f.deux(), 2);
                    assert_eq!(f.trois(), 3);
                    assert_eq!(f.quatre(), 4);
                }
            "#,
        )
        .test();
}

#[test]
fn mixin() {
    project()
        .file(
            "foo.webidl",
            r#"
                [Constructor(short bar)]
                interface Foo {
                    static attribute short defaultBar;
                };

                interface mixin Bar {
                    readonly attribute short bar;
                };

                partial interface mixin Bar {
                    void addToBar(short other);
                };

                Foo includes Bar;
            "#,
        )
        .file(
            "foo.js",
            r#"
                export class Foo {
                    constructor(bar) {
                        this._bar = bar | Foo.defaultBar;
                    }
                    static get defaultBar() {
                        return Foo._defaultBar;
                    }
                    static set defaultBar(defaultBar) {
                        Foo._defaultBar = defaultBar;
                    }
                    get bar() {
                        return this._bar;
                    }
                    addToBar(other) {
                        this._bar += other;
                    }
                }
            "#,
        )
        .file(
            "src/lib.rs",
            r#"
                #![feature(use_extern_macros)]
                extern crate wasm_bindgen;
                use wasm_bindgen::prelude::*;

                pub mod foo;

                use foo::Foo;

                #[wasm_bindgen]
                pub fn test() {
                    let f = Foo::new(1).unwrap();
                    assert_eq!(f.bar(), 1);
                    Foo::set_default_bar(7);
                    f.add_to_bar(Foo::default_bar());
                    assert_eq!(f.bar(), 8);
                }
            "#,
        )
        .test();
}