@startuml UML
skinparam classAttributeIconSize 0

' generic class for an experimenter
' note that experiments will likely be stored by user id instead of by all experiment info
' We could do hasing, but it's probably easier for now (for the UML sake) to represent this as a collection of experiments
class User {
    -userId: Integer
    -info: ContactInformation
    -controller: SearchController
    -ownedExperiments: Collection<Experiment>
    -participatingExperiments: Collection<Experiment>
    +addGeoLocation(exp: Experiment): Location
    +seeGeoLocation(experiments: Collection<Experiment>): Map
}

' what information does a subscription hold? an experiment & list of users subscribed? currently assumes all subscribers are experimenters of some kind
class Subscription {
    -experiment: Experiment
    -subscribed: Collection<User>
    +getSubscribers(): Collection<User>
}

class Question {
    -question: String
    -user: User
    -reply: Reply
    +getQuestion(): String
    +getQuestion(): User
    +getReply(): Reply
}

' assume that anyone who can reply to a question is a type of experimenter
class Reply {
    -reply: String
    -user: User
    +getReply(): String
}

' in charge of questions & their replies
class QuestionController {
    +createQuestion(currentExperiment: Experiment, question: String, creator: User): Question
    +createReply(currentExperiment: Experiment, question: String, owner: User): Reply
}

' allows the experiment to enforce only one trial type
enum ExperimentType {
    Count
    NaturalCount
    Binomial
    Measurement
}

' right now will just look for owner based on their id
' idea: make this 'experimentInfo' and give every type of experiment an experimentInfo instead
' rational: for displaying info and searching we don't need the results, just the info
' both are implemented so whichever we don't use can just be deleted
abstract class Experiment implements Graph {
    -experimentId: Integer
    -description: String
    -minTrials: Integer
    -isActve: Boolean
    -isPublished: Boolean
    -experimentId: int
    ' this is the userId of the owner
    -ownerId: Integer
    -type: ExperimentType
    -crowdUsers: Collection<Integer>
    +getExperimentId(): Integer
}



class CountExperiment extends Experiment {
    -results: Collection<CountTrial>
    +recordResult(result: int)
}

class NaturalCountExperiment extends Experiment {
    -results: Collection<NaturalCountTrial>
    +recordResult(result: int)
}

class BinomialExperiment extends Experiment {
    -results: Collection<BinomialTrial>
    +recordResult(result: boolean)
}

class MeasurementExperiment extends Experiment {
    -results: Collection<MeasurementTrial>
    +recordResult(result: float)
}

class StatSummary {
    +getMean(trials: Collection<Trial>): Float
    +getMedian(trials: Collection<Trial>): Float
    +getStdev(trials: Collection<Trial>): Float
    +getFirstQuartile(trials: Collection<Trial>): (Float, Float)
    +getSecondQuartile(trials: Collection<Trial>): (Float, Float)
    +getThirdQuartile(trials: Collection<Trial>): (Float, Float)
    +getFourthQuartile(trials: Collection<Trial>): (Float, Float)

}

' each trial has a unique QRCode associated with it
abstract class Trial {
    ' just going to user experimenter ID here
    -collector: Integer
    -QRcode: QRCode
    -location: Optional<Location>
    -date: Date
}

class CountTrial extends Trial {
    ' essentially just a trial but it's own class since other types of trial aren't a count trial
}

' no unsigned ints in java, not sure how to enforce natural numbers only
class NaturalCountTrial extends Trial {
    -result: Integer
    +getResults(): Integer
}

' will likely specify true = pass, false = fail
class BinomialTrial extends Trial {
    -result: Boolean
    +getResults(): Boolean
}

class MeasurementTrial extends Trial {
    -result: Float
    +getResults(): Float
}

class QRCodeGenerator {
    ' implementation details unclear at the moment
    +getQRCode(): QRCode
}

class QRCodeReader {
    ' implementation details unclear at the moment
    +readQRCode(qr: QRCode)
}

class QRCode {
    ' somehow represent this?
    -code: int
    -isReadable: boolean
    ' a lot of QR codes use a check digit to determine if they are valid or not
    +checkDigit(code): boolean
}

class Location {
  -latitude: double
  -longitude: double
}

class GeographicalMap{
 -Collection<Location>
 +generateMap(loc: Collection<Location): Image
}

GeographicalMap -- "1..*" Location


interface "Graph" as Graph <<Interface>>> {
    +generateHistogram(Collection<Trial>): Image
    +generatePlot(Collection<Trial>): Image
}

class ContactInformation {
    -name: String
    -email: Optional<String>
    -phone: Optional<String>
}

class SearchController {
    ' functionality here is searchbar functions required by Java so likely don't need to be specified in UML
    -keyword: String
    +searchKeyWord(keyword: String): Collection<Experiment>
}

User "1" *-- "1" ContactInformation
User "1" *-- "0...*" Experiment

Subscription "0...*" o-- "0...*" User
Subscription "1" o-- "1" Experiment

Question "0...*" o-- "1" User
' Note: does each question get one reply? or does it turn into a thread?
Question "1" *-- "0...1" Reply

Reply "0...*" o-- "1" User

' Question: do we draw both aggregations since they are different and both are true?
Experiment "0...*" o-- "0...*" User

CountExperiment "1" *-- "0...*" CountTrial


NaturalCountExperiment "1" *-- "0...*" NaturalCountTrial

BinomialExperiment "1" *-- "0...*" BinomialTrial

MeasurementExperiment "1" *-- "0...*" MeasurementTrial

Trial "0...*" o-- "1" User
Trial "1" *-- "1" QRCode
Trial "1" *-- "1" Location

QRCodeGenerator --- QRCode
QRCodeReader --- QRCode

Experiment *-- "1" ExperimentType


User "0..*" -- "1" SearchController

QuestionController  o-- Question
QuestionController o-- Reply

StatSummary -- Experiment


@enduml
