#![cfg_attr(feature = "nightly", feature(plugin))]
#![cfg_attr(feature = "nightly", plugin(elastic_date_macros))]

#[cfg(not(feature = "nightly"))]
#[macro_use]
extern crate elastic_date_macros;

#[macro_use]
extern crate json_str;

#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate elastic_types_derive;

#[macro_use]
extern crate maplit;

extern crate serde;
extern crate serde_json;
pub extern crate chrono;
extern crate geo as georust;
extern crate geojson;

#[macro_use]
extern crate elastic_types;

pub mod date_fixtures {
    use elastic_types::prelude::*;

    #[derive(Default, Clone)]
    pub struct MyDateMapping;
    impl DateMapping for MyDateMapping {
        type Format = EpochMillis;

        fn null_value() -> Option<Date<Self::Format, Self>> {
            Some(Date::build(2015, 3, 14, 16, 45, 13, 778))
        }

        fn boost() -> Option<f32>               { Some(1.01) }

        fn index() -> Option<bool>              { Some(true) }

        fn doc_values() -> Option<bool>         { Some(true) }

        fn include_in_all() -> Option<bool>     { Some(false) }

        fn store() -> Option<bool>              { Some(true) }

        fn ignore_malformed() -> Option<bool>   { Some(true) }
    }
}

pub mod string_fixtures {
    use std::collections::BTreeMap;
    use elastic_types::prelude::*;

    #[derive(Default, Clone)]
    pub struct MyTextMapping;
    impl TextMapping for MyTextMapping {
        fn fields() -> Option<BTreeMap<&'static str, StringField>> {
            let mut fields = BTreeMap::new();

            fields.insert("raw", StringField::Keyword(
                KeywordFieldMapping {
                    analyzer: Some("my_analyzer"),
                    ..Default::default()
                })
            );

            fields.insert("count", StringField::TokenCount(
                ElasticTokenCountFieldMapping::default())
            );

            fields.insert("comp", StringField::Completion(
                ElasticCompletionFieldMapping::default())
            );

            Some(fields)
        }

        fn fielddata_frequency_filter() -> Option<FieldDataFrequencyFilter> { 
            Some(FieldDataFrequencyFilter { min: Some(0.0), ..Default::default() })
        }

        fn analyzer() -> Option<&'static str>               { Some("my_analyzer") }

        fn boost() -> Option<f32>                           { Some(1.3) }

        fn eager_global_ordinals() -> Option<bool>          { Some(false) }

        fn fielddata() -> Option<bool>                      { Some(true) }

        fn include_in_all() -> Option<bool>                 { Some(true) }

        fn ignore_above() -> Option<u32>                    { Some(512) }

        fn index() -> Option<bool>                          { Some(false) }

        fn index_options() -> Option<IndexOptions>          { Some(IndexOptions::Freqs) }

        fn norms() -> Option<bool>                          { Some(true) }

        fn position_increment_gap() -> Option<u32>          { Some(1) }

        fn store() -> Option<bool>                          { Some(true) }

        fn search_analyzer() -> Option<&'static str>        { Some("my_analyzer") }

        fn search_quote_analyzer() -> Option<&'static str>  { Some("my_analyzer") }

        fn similarity() -> Option<&'static str>             { Some("BM25") }

        fn term_vector() -> Option<TermVector>              { Some(TermVector::Yes) }
    }

    #[derive(Default, Clone)]
    pub struct MyKeywordMapping;
    impl KeywordMapping for MyKeywordMapping {
        fn fields() -> Option<BTreeMap<&'static str, StringField>> {
            let mut fields = BTreeMap::new();

            fields.insert("text", StringField::Text(
                TextFieldMapping {
                    analyzer: Some("my_analyzer"),
                    ..Default::default()
                })
            );

            fields.insert("count", StringField::TokenCount(
                ElasticTokenCountFieldMapping::default())
            );

            fields.insert("comp", StringField::Completion(
                ElasticCompletionFieldMapping::default())
            );

            Some(fields)
        }

        fn analyzer() -> Option<&'static str>           { Some("my_analyzer") }

        fn boost() -> Option<f32>                       { Some(1.03) }

        fn doc_values() -> Option<bool>                 { Some(true) }

        fn eager_global_ordinals() -> Option<bool>      { Some(false) }

        fn include_in_all() -> Option<bool>             { Some(false) }

        fn ignore_above() -> Option<u32>                { Some(256) }

        fn index() -> Option<bool>                      { Some(true) }

        fn index_options() -> Option<IndexOptions>      { Some(IndexOptions::Docs) }

        fn norms() -> Option<bool>                      { Some(false) }

        fn null_value() -> Option<&'static str>         { Some("my string") }

        fn store() -> Option<bool>                      { Some(false) }

        fn search_analyzer() -> Option<&'static str>    { Some("my_analyzer") }

        fn similarity() -> Option<&'static str>         { Some("classic") }
    }
}

pub mod boolean_fixtures {
    use elastic_types::prelude::*;

    #[derive(Default, Clone)]
    pub struct MyBooleanMapping;
    impl BooleanMapping for MyBooleanMapping {
        fn boost() -> Option<f32>           { Some(1.01) }

        fn index() -> Option<bool>          { Some(false) }

        fn doc_values() -> Option<bool>     { Some(true) }

        fn store() -> Option<bool>          { Some(true) }

        fn null_value() -> Option<bool>     { Some(false) }
    }
}

pub mod number_fixtures {
    use elastic_types::prelude::*;

    #[derive(Default, Clone)]
    pub struct MyIntegerMapping;
    impl IntegerMapping for MyIntegerMapping {
        fn coerce() -> Option<bool>             { Some(true) }

        fn boost() -> Option<f32>               { Some(1.1) }

        fn doc_values() -> Option<bool>         { Some(false) }

        fn ignore_malformed() -> Option<bool>   { Some(true) }

        fn include_in_all() -> Option<bool>     { Some(true) }

        fn index() -> Option<bool>              { Some(false) }

        fn store() -> Option<bool>              { Some(true) }

        fn null_value() -> Option<i32>          { Some(42) }
    }

    #[derive(Default, Clone)]
    pub struct MyLongMapping;
    impl LongMapping for MyLongMapping {
        fn coerce() -> Option<bool>             { Some(true) }

        fn boost() -> Option<f32>               { Some(1.1) }

        fn doc_values() -> Option<bool>         { Some(false) }

        fn ignore_malformed() -> Option<bool>   { Some(true) }

        fn include_in_all() -> Option<bool>     { Some(true) }

        fn index() -> Option<bool>              { Some(false) }

        fn store() -> Option<bool>              { Some(true) }

        fn null_value() -> Option<i64>          { Some(-42) }
    }

    #[derive(Default, Clone)]
    pub struct MyShortMapping;
    impl ShortMapping for MyShortMapping {
        fn coerce() -> Option<bool>             { Some(true) }

        fn boost() -> Option<f32>               { Some(1.1) }

        fn doc_values() -> Option<bool>         { Some(false) }

        fn ignore_malformed() -> Option<bool>   { Some(true) }

        fn include_in_all() -> Option<bool>     { Some(true) }

        fn index() -> Option<bool>              { Some(false) }

        fn store() -> Option<bool>              { Some(true) }

        fn null_value() -> Option<i16>          { Some(42) }
    }

    #[derive(Default, Clone)]
    pub struct MyByteMapping;
    impl ByteMapping for MyByteMapping {
        fn coerce() -> Option<bool>             { Some(true) }

        fn boost() -> Option<f32>               { Some(1.1) }

        fn doc_values() -> Option<bool>         { Some(false) }

        fn ignore_malformed() -> Option<bool>   { Some(true) }

        fn include_in_all() -> Option<bool>     { Some(true) }

        fn index() -> Option<bool>              { Some(false) }

        fn store() -> Option<bool>              { Some(true) }

        fn null_value() -> Option<i8>           { Some(1) }
    }

    #[derive(Default, Clone)]
    pub struct MyFloatMapping;
    impl FloatMapping for MyFloatMapping {
        fn coerce() -> Option<bool>             { Some(true) }

        fn boost() -> Option<f32>               { Some(1.1) }

        fn doc_values() -> Option<bool>         { Some(false) }

        fn ignore_malformed() -> Option<bool>   { Some(true) }

        fn include_in_all() -> Option<bool>     { Some(true) }

        fn index() -> Option<bool>              { Some(false) }

        fn store() -> Option<bool>              { Some(true) }

        fn null_value() -> Option<f32>          { Some(1.04) }
    }

    #[derive(Default, Clone)]
    pub struct MyDoubleMapping;
    impl DoubleMapping for MyDoubleMapping {
        fn coerce() -> Option<bool>             { Some(true) }

        fn boost() -> Option<f32>               { Some(1.1) }

        fn doc_values() -> Option<bool>         { Some(false) }

        fn ignore_malformed() -> Option<bool>   { Some(true) }

        fn include_in_all() -> Option<bool>     { Some(true) }

        fn index() -> Option<bool>              { Some(false) }

        fn store() -> Option<bool>              { Some(true) }

        fn null_value() -> Option<f64>          { Some(-0.00002) }
    }
}

pub mod ip_fixtures {
    use std::net::Ipv4Addr;
    use elastic_types::prelude::*;

    #[derive(Default, Clone)]
    pub struct MyIpMapping;
    impl IpMapping for MyIpMapping {
        fn boost() -> Option<f32>               { Some(1.01) }

        fn index() -> Option<bool>              { Some(false) }

        fn doc_values() -> Option<bool>         { Some(true) }

        fn store() -> Option<bool>              { Some(true) }

        fn null_value() -> Option<Ipv4Addr>     { Some(Ipv4Addr::new(127, 0, 0, 1)) }   
    }
}

pub mod geo_point_fixtures {
    use elastic_types::prelude::*;

    #[derive(Default, Clone)]
    pub struct MyGeoPointMapping;
    impl GeoPointMapping for MyGeoPointMapping {
        type Format = GeoPointArray;

        fn geohash() -> Option<bool>                { Some(false) }

        fn geohash_precision() -> Option<Distance>  { Some(Distance(50.0, DistanceUnit::Meters)) }

        fn geohash_prefix() -> Option<bool>         { Some(true) }

        fn ignore_malformed() -> Option<bool>       { Some(true) }

        fn lat_lon() -> Option<bool>                { Some(true) }
    }
}

pub mod geo_shape_fixtures {
    use elastic_types::prelude::*;

    #[derive(Default, Clone)]
    pub struct MyGeoShapeMapping;
    impl GeoShapeMapping for MyGeoShapeMapping {
        fn tree() -> Option<Tree>                   { Some(Tree::Geohash) }

        fn precision() -> Option<Distance>          { Some(Distance(50.0, DistanceUnit::Meters)) }

        fn tree_levels() -> Option<i32>             { Some(8) }

        fn strategy() -> Option<Strategy>           { Some(Strategy::Recursive) }

        fn distance_error_pct() -> Option<f32>      { Some(0.5) }

        fn orientation() -> Option<Orientation>     { Some(Orientation::Clockwise) }

        fn points_only() -> Option<bool>            { Some(false) }
    }
}

pub mod object_fixtures {
    use serde::Serializer;
    use serde::ser::SerializeStruct;
    use elastic_types::prelude::*;

    #[derive(Serialize)]
    pub struct SimpleType {
        pub field1: Date<EpochMillis>,
        pub field2: SimpleNestedType
    }

    impl DocumentType<SimpleTypeMapping> for SimpleType { }

    #[derive(Default, Clone)]
    pub struct SimpleTypeMapping;
    impl DocumentMapping for SimpleTypeMapping {
        fn name() -> &'static str { "simpletype" }
    }
    impl PropertiesMapping for SimpleTypeMapping {
        fn props_len() -> usize { 2 }
        
        fn serialize_props<S>(state: &mut S) -> Result<(), S::Error> 
            where S: SerializeStruct
        {
            try!(field_ser(state, "field1", Date::<EpochMillis>::mapping()));
            try!(field_ser(state, "field2", SimpleNestedType::mapping()));

            Ok(())
        }
    }

    #[derive(Serialize)]
    pub struct SimpleNestedType {
        pub field: i32
    }

    impl DocumentType<SimpleNestedTypeMapping> for SimpleNestedType { }

    #[derive(Default, Clone)]
    pub struct SimpleNestedTypeMapping;
    impl DocumentMapping for SimpleNestedTypeMapping {
        fn name() -> &'static str { "simplenestedtype" }
    }
    impl PropertiesMapping for SimpleNestedTypeMapping {
        fn props_len() -> usize { 1 }
        
        fn serialize_props<S>(state: &mut S) -> Result<(), S::Error> 
            where S: SerializeStruct
        {
            try!(field_ser(state, "field", i32::mapping()));

            Ok(())
        }
    }
}

pub mod object_macro_fixtures {
    use elastic_types::prelude::*;

    // Make sure we can derive `ElasticType` with no `uses`.
    pub mod no_prelude {
        #[derive(Serialize, ElasticType)]
        pub struct TypeWithNoPath {
            id: i32
        }
    }

    #[derive(Serialize, ElasticType)]
    pub struct SimpleType {
        pub field1: Date<EpochMillis>,
        pub field2: SimpleNestedType
    }

    #[derive(Serialize, ElasticType)]
    pub struct SimpleNestedType {
        pub field: i32
    }

    #[derive(Serialize, ElasticType)]
    #[elastic(mapping="CustomTypeMapping")]
    pub struct CustomType {
        pub field: i32,
        #[serde(skip_serializing)]
        pub ignored_field: i32,
        #[serde(rename="renamed_field")]
        pub field2: i32
    }

    #[derive(Default)]
    pub struct CustomTypeMapping;
    impl DocumentMapping for CustomTypeMapping {
        fn name() -> &'static str { "renamed_type" }
    }

    #[derive(Serialize, ElasticType)]
    pub struct Wrapped {
        pub field1: Vec<i32>,
        pub field2: Option<bool>,
        pub field3: &'static str
    }

    #[derive(Default, Serialize)]
    pub struct Index {
        mappings: Mappings
    }

    #[derive(Default, Serialize)]
    pub struct Mappings {
        simpletype: Document<SimpleTypeMapping>
    }
}

pub mod object;
pub mod object_macro;
pub mod geo_point;
pub mod geo_shape;
pub mod date;
pub mod string;
pub mod number;
pub mod boolean;