#!/usr/bin/env python3
#  Copyright 2016 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.

from fruit_test_common import *

COMMON_DEFINITIONS = '''
    #include "test_common.h"

    struct Listener;

    struct X {};
        
    struct Annotation {};
    struct Annotation1 {};
    using ListenerAnnot = fruit::Annotated<Annotation, Listener>;
    '''

def test_get_none():
    source = '''
        fruit::Component<> getComponent() {
          return fruit::createComponent();
        }

        int main() {
          fruit::Injector<> injector(getComponent);

          std::vector<X*> multibindings = injector.getMultibindings<X>();
          (void) multibindings;
          Assert(multibindings.empty());
        }
        '''
    expect_success(
        COMMON_DEFINITIONS,
        source)

def test_multiple_various_kinds():
    source = '''
        static int numNotificationsToListener1 = 0;
        static int numNotificationsToListener2 = 0;
        static int numNotificationsToListener3 = 0;

        struct Listener {
        public:
          virtual ~Listener() = default;

          virtual void notify() = 0;
        };

        struct Listener1 : public Listener {
        public:
          INJECT(Listener1()) = default;

          virtual ~Listener1() = default;

          void notify() override {
            ++numNotificationsToListener1;
          }
        };

        struct Writer {
        public:
          virtual void write(std::string s) = 0;
        };

        struct StdoutWriter : public Writer {
        public:
          INJECT(StdoutWriter()) = default;

          void write(std::string s) override {
            std::cout << s << std::endl;
          }
        };

        struct Listener2 : public Listener {
        private:
          Writer* writer;

        public:
          INJECT(Listener2(Writer* writer))
            : writer(writer) {
          }

          virtual ~Listener2() = default;

          void notify() override {
            (void) writer;
            ++numNotificationsToListener2;
          }
        };

        struct Listener3 : public Listener {
        private:
          Writer* writer;

        public:
          INJECT(Listener3(Writer* writer))
            : writer(writer) {
          }

          virtual ~Listener3() = default;

          void notify() override {
            (void) writer;
            ++numNotificationsToListener3;
          }
        };

        fruit::Component<> getListenersComponent() {
          return fruit::createComponent()
            .bind<Writer, StdoutWriter>()
            // Note: this is just to exercise the other method, but in real code you should split this in
            // an addMultibinding<Listener, Listener1> and a registerProvider with the lambda.
            .addMultibindingProvider([]() {
              Listener1* listener1 = new Listener1();
              return static_cast<Listener*>(listener1);
            })
            .addMultibinding<Listener, Listener2>()
            .addMultibinding<ListenerAnnot, Listener3>();
        }

        int main() {
          fruit::Injector<> injector(getListenersComponent);
          std::vector<Listener*> listeners = injector.getMultibindings<Listener>();
          for (Listener* listener : listeners) {
            listener->notify();
          }

          std::vector<Listener*> listeners2 = injector.getMultibindings<Listener>();
          Assert(listeners == listeners2);

          if (numNotificationsToListener1 != 1 || numNotificationsToListener2 != 1
            || numNotificationsToListener3 != 0) {
            abort();
          }

          std::vector<Listener*> listenersWithAnnotation = injector.getMultibindings<ListenerAnnot>();
          for (Listener* listener : listenersWithAnnotation) {
            listener->notify();
          }

          if (numNotificationsToListener1 != 1 || numNotificationsToListener2 != 1
            || numNotificationsToListener3 != 1) {
            abort();
          }
        }
        '''
    expect_success(
        COMMON_DEFINITIONS,
        source)

def test_order():
    source = '''
        std::vector<int> numbers = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
        // *
        // |-- 0
        // |-- A
        // |   |-- 1
        // |   |-- B
        // |   |   |-- 2
        // |   |   `-- 3
        // |   |-- 4
        // |   |-- C
        // |   |   |-- 5
        // |   |   |-- 6
        // |   |   |-- D
        // |   |   |   |-- 7
        // |   |   |   |-- E
        // |   |   |   |   |-- 8
        // |   |   |   |   `-- 9
        // |   |   |   `-- 10
        // |   |   |-- 11
        // |   |   |-- F
        // |   |   |   |-- 12
        // |   |   |   `-- 13
        // |   |   `-- 14
        // |   |-- 15
        // |   |-- C (won't be expanded)
        // |   `-- 16
        // |-- 17
        // |-- C (won't be expanded)
        // `-- 18
        
        fruit::Component<> getRootComponent();
        fruit::Component<> getComponentA();
        fruit::Component<> getComponentB();
        fruit::Component<> getComponentC();
        fruit::Component<> getComponentD();
        fruit::Component<> getComponentE();
        fruit::Component<> getComponentF();

        fruit::Component<> getRootComponent() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[0])
            .install(getComponentA)
            .addInstanceMultibinding(numbers[17])
            .install(getComponentC)
            .addInstanceMultibinding(numbers[18]);
        }
        
        fruit::Component<> getComponentA() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[1])
            .install(getComponentB)
            .addInstanceMultibinding(numbers[4])
            .install(getComponentC)
            .addInstanceMultibinding(numbers[15])
            .install(getComponentC)
            .addInstanceMultibinding(numbers[16]);
        }

        fruit::Component<> getComponentB() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[2])
            .addInstanceMultibinding(numbers[3]);
        }

        fruit::Component<> getComponentC() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[5])
            .addInstanceMultibinding(numbers[6])
            .install(getComponentD)
            .addInstanceMultibinding(numbers[11])
            .install(getComponentF)
            .addInstanceMultibinding(numbers[14]);
        }

        fruit::Component<> getComponentD() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[7])
            .install(getComponentE)
            .addInstanceMultibinding(numbers[10]);
        }

        fruit::Component<> getComponentE() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[8])
            .addInstanceMultibinding(numbers[9]);
        }

        fruit::Component<> getComponentF() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[12])
            .addInstanceMultibinding(numbers[13]);
        }

        int main() {
          fruit::Injector<> injector(getRootComponent);
          std::vector<int*> result_ptrs = injector.getMultibindings<int>();
          std::vector<int> results;
          std::cout << "Results: ";
          for (int* result : result_ptrs) {
            std::cout << *result << ", ";
            results.push_back(*result);
          }
          std::cout << std::endl;
          Assert(results == numbers);
        }
        '''
    expect_success(
        COMMON_DEFINITIONS,
        source)


def test_order_with_normalized_component():
    source = '''
        std::vector<int> numbers = {
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
            19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37};
        // root1
        // |-- 0
        // |-- A
        // |   |-- 1
        // |   |-- B
        // |   |   |-- 2
        // |   |   `-- 3
        // |   |-- 4
        // |   |-- C
        // |   |   |-- 5
        // |   |   |-- 6
        // |   |   |-- D
        // |   |   |   |-- 7
        // |   |   |   |-- E
        // |   |   |   |   |-- 8
        // |   |   |   |   `-- 9
        // |   |   |   `-- 10
        // |   |   |-- 11
        // |   |   |-- F
        // |   |   |   |-- 12
        // |   |   |   `-- 13
        // |   |   `-- 14
        // |   |-- 15
        // |   |-- C (won't be expanded)
        // |   `-- 16
        // |-- 17
        // |-- C (won't be expanded)
        // `-- 18
        
        // root2
        // |-- 19
        // |-- A2
        // |   |-- 20
        // |   |-- B2
        // |   |   |-- 21
        // |   |   `-- 22
        // |   |-- 23
        // |   |-- C2
        // |   |   |-- 24
        // |   |   |-- 25
        // |   |   |-- D2
        // |   |   |   |-- 26
        // |   |   |   |-- E2
        // |   |   |   |   |-- 27
        // |   |   |   |   `-- 28
        // |   |   |   `-- 29
        // |   |   |-- 30
        // |   |   |-- F2
        // |   |   |   |-- 31
        // |   |   |   `-- 32
        // |   |   `-- 33
        // |   |-- 34
        // |   |-- C2 (won't be expanded)
        // |   `-- 35
        // |-- 36
        // |-- C2 (won't be expanded)
        // `-- 37

        fruit::Component<> getRootComponent();
        fruit::Component<> getComponentA();
        fruit::Component<> getComponentB();
        fruit::Component<> getComponentC();
        fruit::Component<> getComponentD();
        fruit::Component<> getComponentE();
        fruit::Component<> getComponentF();
        
        fruit::Component<> getRootComponent2();
        fruit::Component<> getComponentA2();
        fruit::Component<> getComponentB2();
        fruit::Component<> getComponentC2();
        fruit::Component<> getComponentD2();
        fruit::Component<> getComponentE2();
        fruit::Component<> getComponentF2();

        fruit::Component<> getRootComponent() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[0])
            .install(getComponentA)
            .addInstanceMultibinding(numbers[17])
            .install(getComponentC)
            .addInstanceMultibinding(numbers[18]);
        }
        
        fruit::Component<> getComponentA() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[1])
            .install(getComponentB)
            .addInstanceMultibinding(numbers[4])
            .install(getComponentC)
            .addInstanceMultibinding(numbers[15])
            .install(getComponentC)
            .addInstanceMultibinding(numbers[16]);
        }

        fruit::Component<> getComponentB() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[2])
            .addInstanceMultibinding(numbers[3]);
        }

        fruit::Component<> getComponentC() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[5])
            .addInstanceMultibinding(numbers[6])
            .install(getComponentD)
            .addInstanceMultibinding(numbers[11])
            .install(getComponentF)
            .addInstanceMultibinding(numbers[14]);
        }

        fruit::Component<> getComponentD() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[7])
            .install(getComponentE)
            .addInstanceMultibinding(numbers[10]);
        }

        fruit::Component<> getComponentE() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[8])
            .addInstanceMultibinding(numbers[9]);
        }

        fruit::Component<> getComponentF() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[12])
            .addInstanceMultibinding(numbers[13]);
        }
        
        fruit::Component<> getRootComponent2() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[19])
            .install(getComponentA2)
            .addInstanceMultibinding(numbers[36])
            .install(getComponentC2)
            .addInstanceMultibinding(numbers[37]);
        }
        
        fruit::Component<> getComponentA2() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[20])
            .install(getComponentB2)
            .addInstanceMultibinding(numbers[23])
            .install(getComponentC2)
            .addInstanceMultibinding(numbers[34])
            .install(getComponentC2)
            .addInstanceMultibinding(numbers[35]);
        }

        fruit::Component<> getComponentB2() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[21])
            .addInstanceMultibinding(numbers[22]);
        }

        fruit::Component<> getComponentC2() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[24])
            .addInstanceMultibinding(numbers[25])
            .install(getComponentD2)
            .addInstanceMultibinding(numbers[30])
            .install(getComponentF2)
            .addInstanceMultibinding(numbers[33]);
        }

        fruit::Component<> getComponentD2() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[26])
            .install(getComponentE2)
            .addInstanceMultibinding(numbers[29]);
        }

        fruit::Component<> getComponentE2() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[27])
            .addInstanceMultibinding(numbers[28]);
        }

        fruit::Component<> getComponentF2() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[31])
            .addInstanceMultibinding(numbers[32]);
        }        

        int main() {
          fruit::NormalizedComponent<> normalizedComponent(getRootComponent);
          fruit::Injector<> injector(normalizedComponent, getRootComponent2);
          std::vector<int*> result_ptrs = injector.getMultibindings<int>();
          std::vector<int> results;
          std::cout << "Results: ";
          for (int* result : result_ptrs) {
            std::cout << *result << ", ";
            results.push_back(*result);
          }
          std::cout << std::endl;
          Assert(results == numbers);
        }
        '''
    expect_success(
        COMMON_DEFINITIONS,
        source)

def test_with_normalized_component_lazy_components_not_deduped_across():
    source = '''
        std::vector<int> numbers = {0, 1, 2, 3, 4};
        
        // *
        // |-- 0
        // |-- A (lazy)
        // |   |-- 1
        // |   `-- 2
        // |-- 3
        // |-- A (lazy, won't be expanded)
        // `-- 4
        
        fruit::Component<> getRootComponent();
        fruit::Component<> getComponentA();
        
        fruit::Component<> getRootComponent() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[0])
            .install(getComponentA)
            .addInstanceMultibinding(numbers[3])
            .install(getComponentA)
            .addInstanceMultibinding(numbers[4]);
        }
        
        fruit::Component<> getComponentA() {
          return fruit::createComponent()
            .addInstanceMultibinding(numbers[1])
            .addInstanceMultibinding(numbers[2]);
        }

        int main() {
          fruit::NormalizedComponent<> normalizedComponent(getRootComponent);
          fruit::Injector<> injector(normalizedComponent, getRootComponent);
          std::vector<int*> result_ptrs = injector.getMultibindings<int>();
          std::vector<int> results;
          std::cout << "Results: ";
          for (int* result : result_ptrs) {
            std::cout << *result << ", ";
            results.push_back(*result);
          }
          std::cout << std::endl;
          std::vector<int> expected_numbers = {0, 1, 2, 3, 4};
          Assert(results == expected_numbers);
        }
        '''
    expect_success(
        COMMON_DEFINITIONS,
        source)

@pytest.mark.parametrize('XVariantAnnot,XVariantRegexp', [
    ('const X', 'const X'),
    ('X*', 'X\*'),
    ('const X*', 'const X\*'),
    ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
    ('fruit::Annotated<Annotation1, const X>', 'const X'),
    ('fruit::Annotated<Annotation1, X*>', 'X\*'),
    ('fruit::Annotated<Annotation1, const X*>', 'const X\*'),
    ('fruit::Annotated<Annotation1, std::shared_ptr<X>>', 'std::shared_ptr<X>'),
])
def test_multibindings_get_error_non_class_type(XVariantAnnot, XVariantRegexp):
    source = '''
        void f(fruit::Injector<> injector) {
          injector.getMultibindings<XVariantAnnot>();
        }
        '''
    expect_compile_error(
        'NonClassTypeError<XVariantRegexp,X>',
        'A non-class type T was specified. Use C instead.',
        COMMON_DEFINITIONS,
        source,
        locals())

@pytest.mark.parametrize('XVariantAnnot,XVariantRegexp', [
    ('X&', 'X&'),
    ('const X&', 'const X&'),
    ('fruit::Annotated<Annotation1, X&>', 'X&'),
    ('fruit::Annotated<Annotation1, const X&>', 'const X&'),
])
def test_multibindings_get_error_reference_type(XVariantAnnot, XVariantRegexp):
    source = '''
        void f(fruit::Injector<> injector) {
          injector.getMultibindings<XVariantAnnot>();
        }
        '''
    expect_generic_compile_error(
        'declared as a pointer to a reference of type'
        '|forming pointer to reference type'
        '|fruit::Injector<.*>::getMultibindings.: no matching overloaded function found',
        COMMON_DEFINITIONS,
        source,
        locals())

if __name__== '__main__':
    main(__file__)