package com.jdbernard.timeanalyzer

import org.joda.time.Duration

/**
 * A category represents a collection of like events.
 */
public abstract class Category implements Comparable<Category> {

    public List<Event> events
    public List<Category> categories
    public List<CategorizationPlan> categorizationPlans
    public String description

    public Category() {
        events = []
        categories = []
        categorizationPlans = []
        description = "Unnamed Category"
    }

    public Category(String description) {
        events = []
        categories = []
        categorizationPlans = []
        this.description = description
    }

    public abstract boolean matchesEvent(Event e)

    public void addEvent(Event event) {

        // see if we have or can create a subcategory that will hold this event
        Event addedEvent = addToSubcategory(event)

        // no, let's just add it on ourself
        if (!addedEvent) {
            events << event
            addedEvent = event
        }

        return addedEvent
    }

    public boolean addToSubcategory(Event e) {

        // find all matching subcategories
        def matchingCategories = categories.findAll { it.matchesEvent(e) }

        if (matchingCategories) 
            return matchingCategories[0].addEvent(e)

        // no matching subcategories, can we create a new one based on one
        // of our plans?
        def matchingPlans = categorizationPlans.findAll
            { it.deservesNewCategory(e, events) }

        if (matchingPlans) {
            // create the new category
            def newCategory = matchingPlans[0].newCategory(e, events)

            // add it to our list of cateogries
            categories << newCategory

            // add the new event to the category
            def addedEvent = newCategory.addEvent(e)

            // move all the events that match the new category over
            def existingEvents = matchingPlans[0].findEventsToRecategorize(e, events)
            events -= existingEvents
            existingEvents.each { newCategory.addEvent(it) }
            
            // return the new entry
            return addedEvent
        }

        return null
    }

    public Category filter(List<CategoryFilters> filters) {

        // create new filtered category
        FilteredCategory fc = new FilteredCategory(description)
        fc.filters = filters

        // filter all events and add them to the category
        fc.events

        // TODO
    }

    public Category filter(CategoryFilter filter) { return filter([filter]) }

    public Category filter(Closure c) { return filter(c as CategoryFilter) }

    public Duration getDuration() { 
        return categories.sum(new Duration(0)) { it.duration } +
               events.sum(new Duration(0)) { it.duration }
    }

    public int compareTo(Category other) {
        return this.getDuration().compareTo(other.getDuration())
    }

    public String toString() { return description }

}