package hr.unipu.ksdtoolkit.models

import hr.unipu.ksdtoolkit.entities.Model
import hr.unipu.ksdtoolkit.integration.EulerIntegration
import hr.unipu.ksdtoolkit.modules.ModuleGenericCompoundDecrease

/**
 * SD model of Simple Compound Interest.
 *
 * @author [Siniša Sovilj](mailto:sinisa.sovilj@unipu.hr)
 */

open class ModelGenericSD : Model() {

    // Static properties:
    companion object {
        const val CONSTANT_KEY = "CONSTANT"
        const val CONVERTER_KEY = "converter"
        const val INITIAL_STOCK_KEY = "INITIAL_STOCK"
        const val INFLOW_KEY = "inflow"
        const val OUTFLOW_KEY = "outflow"
        const val STOCK_KEY = "Stock"

        const val CONSTANT_VALUE = 10          // [%]
        const val INITIAL_STOCK_VALUE = 100    // [€]
        const val INITIAL_TIME_VALUE = 0       // [month]
        const val FINAL_TIME_VALUE = 120       // [month]
        const val TIME_STEP_VALUE = 0.25       // [month]
    }


    init {

        // 1. Create the model (with setup of: time boundaries & time step & integrationType type)
        val model = this   // inheritance: Model()

        /*
        val model = Model(
            initialTime = 0,
            finalTime = 100,
            timeStep = 0.25,
            integrationType = EulerIntegration()
        )
        */


        /*
        val model = Model(0, 100, 0.25, EulerIntegration())
            // alternative: Model(INITIAL_TIME_VALUE, FINAL_TIME_VALUE, TIME_STEP_VALUE, EulerIntegration())
        */


        // override default model properties:
        model.initialTime = INITIAL_TIME_VALUE
        model.finalTime = FINAL_TIME_VALUE
        model.timeStep = TIME_STEP_VALUE
        model.integrationType = EulerIntegration()
        model.name = "Generic SD Model"   // name is optional
        model.timeUnit = "month"          // unit is optional



        // 2. Create all system elements:

        // - 2a. Variables (Constants)
        val CONSTANT = model.constant(CONSTANT_KEY)
        val INITIAL_STOCK = model.constant(INITIAL_STOCK_KEY)

        // - 2b. Variables (Converters)
        val converter = model.converter(CONVERTER_KEY)

        // - 2c. Stocks
        val Stock = model.stock(STOCK_KEY)

        // - 2d. Flows
        val inflow = model.flow(INFLOW_KEY)
        val outflow = model.flow(OUTFLOW_KEY)

        // - 2e. Modules
        val Module = model.createModule(
            "Module",
            "hr.unipu.ksdtoolkit.modules.ModuleGenericCompoundDecrease"
        ) as ModuleGenericCompoundDecrease


        // - 2f. (Optional): Entities' descriptions
        CONSTANT.description = "Annual flow rate in [%/year]"
        INITIAL_STOCK.description = "Initial capital in [EUR] in the beginning of the simulation."
        converter.description = "Converts percentage to decimal."
        Stock.description = "Accumulated capital in [EUR] at specific point in time."
        inflow.description = "Interest inflow in [EUR / chosen unit of time], e.g. [EUR/month]."
        outflow.description = "Interest outflow in [EUR / chosen unit of time], e.g. [EUR/month]."

        // - 2g. (Optional): Entities' units
        CONSTANT.unit = "%/year"
        INITIAL_STOCK.unit = "€"
        Stock.unit = "€"
        inflow.unit = "€/month"
        outflow.unit = "€/month"


        // 3. Initial values:

        // - 3a. Stocks
        Stock.initialValue = { INITIAL_STOCK }      // Accepts: Double, Int or ModelEntity



        // 4. Equations:

        // - 4a. Constants
        CONSTANT.equation = { CONSTANT_VALUE }
        INITIAL_STOCK.equation = { INITIAL_STOCK_VALUE }

        // - 4b. Converters
        converter.equation = { CONSTANT / 100.0 / 12.0 }

        // - 4c. Stocks
        Stock.equation = { inflow - outflow }     // Function type can be either Double or ModelEntity.

        // - 4d. Flows:
        inflow.equation = { Stock * converter }    // Simplified converters so that only equations are used.
        outflow.equation = { Module.outflow }      // Alternative, instead of lambda, member reference: Module::outflow

        // - 4e. Modules:
        Module.inflow.equation = { outflow }


    }

}
