@startuml

skinparam class {
	BackgroundColor White
	ArrowColor Grey
	BorderColor Black
}
skinparam shadowing false

package API {
    class ConfigurationAPI {
        + get() : JSON
        + post(configuration : JSON) : JSON
    }
    class RecommenderAPI {
        + post(preferences : JSON, configuration : JSON) : JSON
    }
    class ProductStructureAPI {
        + put(productStructure : JSON) : JSON
        + get() : JSON
    }
}
"/config/" ()-- ConfigurationAPI
"/recommender/" ()-- RecommenderAPI
"/product_structure/" ()-- ProductStructureAPI

package Manager {
    class RecommendationManager {
        + getRecommentdation(strategy : String, preferences : Preferences, current_configuration : Configuration) : Configuration
    }
}

package Model{
    package ConfigurationModel {
        class Configuration {
            configuration : List<String>
        }
        class ConfigurationVariable {

        }
        Configuration *-- ConfigurationVariable : variables
    }
    package PreferenceModel {
        class Preferences {
            + getAllUserPreferences() : List<UserPreferences>
            + getAllRatingsByCode(code : String) : List<Ratings>
            + getRatingValue(code : String, user : String) : float
        }
        class UserPreference {
            user : String
            + getAllRatings() : List<Rating>
        }
        class Rating {
            code : String
            value : float
        }
        Preferences *-- UserPreference : preferences
        UserPreference *-- Rating : ratings
    }

    package ProductStructure {
        class ProductStructureModel {
            + get_list_of_features(self) : List<ProductStructureElementModel>
            + get_list_of_characteristics(self) : List<ProductStructureElementModel>
            + isCharacteristic(code:String) : bool
        }
        class ProductStructureElementModel {
            elementId : String
            name : String
        }
        enum ProductStructureTypeEnum {
            CHARACTERISTIC
            FEATURE
            CLUSTER
            VARIABLE
        }

        ProductStructureModel *-- ProductStructureElementModel
        ProductStructureElementModel <-- ProductStructureElementModel:children
        ProductStructureElementModel --> ProductStructureTypeEnum:type
    }
}

package DAO {
    class ConfigurationDAO {
        - {static} instance : ConfigurationDAO
        + {static} getInstance() : ConfigurationDAO
        + getAll_as_objects() : List<Configuration>
        + getAll() : JSON
        + add(config : JSON)
        + exists(config : JSON) : bool
    }
    class ProductStructureDAO {
        - {static} instance : ProductStructureDAO
        + {static} getInstance() : ProductStructureDAO
        + get_as_objects() : ProductStructureModel
        + get() : JSON
        + replace(structure : JSON)
        - get_highest_id() : Integer
    }
}

package Scoring {  
    
    

    package List {
        class ListFunction
        class ListToListFunction
        class ListToValueFunction
        class Average
        class Product
        class ForEachValue
    }

    package Value {
        class ValueToValueFunction
        class Threshold
    }

    package RatingConverter {
        class PreferenceToListFunction
        class FlatToListConverter
        class PerUserToListConverter
        class PerFeatureToListConverter
    }
    class ScoringFunction {
        + calc_score(currentConfiguration : Configuration, preferences : Preferences, toRate : ConfigurationModel) : float
    }
    class ScoringFunctionFactory{
        + build_scoring_function(params : List<String>) : ScoringFunction
    }
    ScoringFunctionFactory --> ScoringFunction : builds


    PreferenceScoringFunction --> "1" PreferenceToListFunction : preferenceToListFunction
    PreferenceScoringFunction --> "0..*" ListToListFunction : listToListFunctions
    PreferenceScoringFunction --> "1" ListToValueFunction : listToValueFunction
    PreferenceScoringFunction --> "0..*" ValueToValueFunction : valueToValues

    ScoringFunction <|-- PreferenceScoringFunction
    class PreferenceScoringFunction{
        + calc_score(currentConfiguration : Configuration, preferences : Preferences, toRate : ConfigurationModel) : float
    }
    ScoringFunction <|-- ConfigurationPenealty
    abstract class ConfigurationPenealty{
        + ConfigurationPenealty(product_Structure : ProductStructure)
    }
    ConfigurationPenealty <|-- RatioConfigurationPenalty
    class RatioConfigurationPenalty {
        + calc_score(currentConfiguration : Configuration, preferences : Preferences, toRate : ConfigurationModel) : float
    }
    ConfigurationPenealty <|-- PreferenceWeightedConfigurationPenalty
    ListToValueFunction --* "1" PreferenceWeightedConfigurationPenalty
    class PreferenceWeightedConfigurationPenalty {
        
    }

    ScoringFunction <|-- ReduceScoringFunction
    ReduceScoringFunction *-- "2..*" ScoringFunction
    class ReduceScoringFunction{
        reduce_operator : Operator
        + ReduceScoringFunction(scoringFunctions : List<ScoringFunction>, reduceOperator : Operator)
        + calc_score(currentConfiguration : Configuration, preferences : Preferences, toRate : ConfigurationModel) : float
    }

    interface PreferenceToListFunction {
        + convertToList(preferences : Preferences, toRate : Configuration) : List<float>:
    }

    PreferenceToListFunction <|-- PerFeatureToListConverter
    PerFeatureToListConverter --> ListToValueFunction :uses
    class PerFeatureToListConverter {
        + PerFeatureToListConverter(listToValueFunction : ListToValueFunction)
        + convertToList(preferences : Preferences, toRate : Configuration) : List<float>:
    }
    PreferenceToListFunction <|-- PerUserToListConverter
    PerUserToListConverter --> ListToValueFunction :uses
    class PerUserToListConverter {
        + PerUserToListConverter(listToValueFunction : ListToValueFunction)
        + convertToList(preferences : Preferences, toRate : Configuration) : List<float>:
    }
    PreferenceToListFunction <|-- FlatToListConverter
    class FlatToListConverter {
        + convertToList(preferences : Preferences, toRate : Configuration) : List<float>:
    }
 
    interface ListFunction {

    }

    ListFunction <|-- ListToListFunction
    abstract class ListToListFunction {
            + {abstract} applyToList(list : List[float]) : List[float]
    }

    ListToListFunction <|-- ForEachValue
    ForEachValue --> ValueToValueFunction:uses
    class ForEachValue  {
            + FoEachValue(valueFunction : ValueToValueFunction)
            + applyToList(list : List[float]) : List[float]
    }


    ListFunction <|-- ListToValueFunction
    abstract class ListToValueFunction {
        + {abstract} convertToFloat(list : List<float>) : float
    }

    ListToValueFunction <|-- Average
    class Average {
        + convertToFloat(list : List<float>) : float
    }
    ListToValueFunction <|-- Product
    class Product {
        + convertToFloat(list : List<float>) : float
    }

    interface ValueToValueFunction{
        + applyToValue(value : float) : float
    }

    ValueToValueFunction <|-- Threshold
    class Threshold {
        - threshold : float
        + Threshold(threshold : float)
        + applyToValue(value : float) : float
    }
}

RecommendationManager *-- ScoringFunction

ConfigurationAPI --> ConfigurationDAO
ProductStructureAPI --> ProductStructureDAO
RecommenderAPI --> RecommendationManager

RecommendationManager --> ProductStructureDAO
RecommendationManager --> ConfigurationDAO
RecommendationManager --> ScoringFunctionFactory :uses


ConfigurationDAO --> Configuration
ProductStructureDAO --> ProductStructureModel

Scoring --> Model


@enduml