package net.pagejects.guice

import com.google.inject.AbstractModule
import com.google.inject.Provider
import net.pagejects.core.PagejectsManager
import net.pagejects.core.annotation.PageObject
import net.pagejects.core.annotation.PublicAPI
import net.pagejects.core.impl.PageObjectCache
import org.reflections.Reflections

/**
 * Guice module for Pagejects
 *
 * This module will bind all interfaces from package `packageName` that annotated by [PageObject]
 *
 * @author Andrey Paslavsky
 * @since 0.1
 * @constructor Please take attention: all implementations should have public constructor without parameters
 * @param packageName This package name will be used to locate Page Objects that should be bound to the Guice
 */
@PublicAPI
abstract class PagejectsModule(private val packageName: String) : AbstractModule() {
    /**
     * See [AbstractModule.configure]
     */
    final override fun configure() {
        configure(PagejectsManager.instance)
        Reflections(packageName).getTypesAnnotatedWith(PageObject::class.java).forEach {
            bindPageObject(it)
        }
    }

    /**
     * This method using to setup Pagejects before load Page Objects
     *
     * @param pagejectsManager [PagejectsManager] provides set of methods to configure Pagejects
     */
    @PublicAPI
    abstract fun configure(pagejectsManager: PagejectsManager): Unit

    @Suppress("UNCHECKED_CAST")
    private fun <T : Any> bindPageObject(pageObjectClass: Class<T>) {
        bind(pageObjectClass).toProvider(Provider { PageObjectCache[pageObjectClass] })
    }
}