local super = require "Object"

DataSource = super:new()

local defaults = {
}

local nilDefaults = {
    'filter', 'sort',
}

function DataSource:new(dataset)
    self = super.new(self)
    
    for k, v in pairs(defaults) do
        self:addProperty(k, v)
    end
    for _, k in pairs(nilDefaults) do
        self:addProperty(k)
    end
    
    self._datasetHook = PropertyHook:new()
    self._datasetHook:setValue(dataset)
    self._datasetHook:addObserver(self)
    
    self._dataset = nil
    self._lastUpdate = nil
    self._presentedDataset = nil
    
    self._invalidator = function() self._presentedDataset = nil end
    self:getPropertyHook('filter'):addObserver(self._invalidator)
    self:getPropertyHook('sort'):addObserver(self._invalidator)
    
    return self
end

function DataSource:archive(refs)
    local dataset = self:getDataset()
    if dataset then
        local _, properties = super.archive(self)
        local typeName, datasetProperties = unpack(refs[dataset])
        properties.index = datasetProperties.index
        return typeName, properties
    end
end

function DataSource:unarchiveFilter(archived)
    local filter = unarchive(archived)
    local presenter
    if Object.isa(filter, FilterDataPresenter) then
        presenter = filter
    elseif Object.isa(filter, Artifact) then
        presenter = FilterDataPresenter:new()
        local rule = FilterRule:new()
        rule:setArtifact(filter)
        rule:setTest(FilterRule.test.valueIsTrue)
        presenter:addRule(rule)
    end
    self:setFilterPresenter(presenter)
end

function DataSource:unarchiveSort(archived)
    local sort = unarchive(archived)
    local presenter
    if Object.isa(sort, SortDataPresenter) then
        presenter = sort
    elseif Object.isa(sort, Artifact) then
        presenter = SortDataPresenter:new()
        local rule = SortRule:new()
        rule:setArtifact(sort)
        presenter:addRule(rule)
    end
    self:setSortPresenter(presenter)
end

function DataSource:unarchiveIndex(archived)
end

function DataSource:unarchived()
    if not self:getFilterPresenter() then
        local presenter = FilterDataPresenter:new()
        self:setFilterPresenter(presenter)
    end
    if not self:getSortPresenter() then
        local presenter = SortDataPresenter:new()
        self:setSortPresenter(presenter)
    end
    super.unarchived(self)
end

function DataSource:setFilterPresenter(presenter)
    self:setProperty('filter', presenter)
end

function DataSource:getFilterPresenter()
    return self:getProperty('filter')
end

function DataSource:isFiltered()
    local presenter = self:getFilterPresenter()
    return presenter and presenter:hasRules()
end

function DataSource:setSortPresenter(presenter)
    self:setProperty('sort', presenter)
end

function DataSource:getSortPresenter()
    return self:getProperty('sort')
end

function DataSource:isSorted()
    local presenter = self:getSortPresenter()
    return presenter and presenter:hasRules()
end

function DataSource:getPresenters()
    local presenters = {}
    presenters[#presenters + 1] = self:getFilterPresenter()
    presenters[#presenters + 1] = self:getSortPresenter()
    return presenters
end

function DataSource:setDataset(dataset)
    self._datasetHook:setValue(dataset)
end

function DataSource:getDataset()
    local dataset = self._datasetHook:getValue()
    return dataset and dataset:isInDocument() and dataset
end

function DataSource:getDatasetHook()
    return self._datasetHook
end

function DataSource:entrySequence(fieldName)
    local dataset = self:getDataset()
    if dataset and dataset:isInDocument() then
        if self._dataset ~= dataset or self._lastUpdate ~= dataset:lastUpdate() or not self._presentedDataset then
            self._dataset = dataset
            self._lastUpdate = dataset:lastUpdate()
            local presenters = self:getPresenters()
            for index = 1, #presenters do
                dataset = presenters[index]:apply(dataset)
            end
            self._presentedDataset = dataset
        end
        return self._presentedDataset:entrySequence(fieldName)
    end
    return sequence:newWithArray({})
end

function DataSource:entryCount()
    return self:entrySequence():length()
end

function DataSource:heuristicFieldType(...)
    local dataset = self:getDataset()
    if dataset and dataset:isInDocument() then
        return dataset:heuristicFieldType(...)
    end
end

function DataSource:pickField(...)
    local dataset = self:getDataset()
    if dataset and dataset:isInDocument() then
        return dataset:pickField(...)
    end
end

return DataSource
