local super = require "Object"

TableColumn = super:new()

local defaults = {
    width = 0,
    weight = 1,
}

local nilDefaults = {
    'title',
}

function TableColumn:new()
    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.stamp = nil
    self.getterNames = {}
    self.inspectorInfo = {}
    
    self._datasetHook = nil
    self._colorSchemeHook = nil
    self._typographySchemeHook = nil
    
    self._propertySequence = nil
    self._propertySequenceInvalidator = function() self._propertySequence = nil collectgarbage() end
    
    return self
end

function TableColumn:unarchived()
    if not self:getTitle() then
        self:setTitle('Column')
    end
    super.unarchived(self)
end

function TableColumn:peerPropertyKeyArtifactValues(class, propertyName)
    local values = {}
    for column in self:getParent():getColumnList():iter() do
        if column == self then
            break
        end
        if Object.isa(column, class) then
            local property = column:getProperty(propertyName)
            if Object.isa(property, KeyArtifact) then
                values[#values + 1] = property:getInput()
            end
        end
    end
    return values
end

function TableColumn:getSubclasses()
    return {
        {'TextColumn', 'Text'},
        {'NumberColumn', 'Number'},
        {'DateColumn', 'Date'},
        {'BubbleColumn', 'Size Bubble'},
        {'CheckMarkColumn', 'Checkmark'},
        {'FractionBarColumn', 'Fraction Bar'},
        {'RangeBarColumn', 'Range Bar'},
        {'BalanceBarColumn', 'Balance Bar'},
        {'ArrowColumn', 'Arrow'},
    }
end

function TableColumn:getDataset()
    return self._datasetHook:getValue()
end

function TableColumn:setTitle(title)
    self:setProperty('title', title)
end

function TableColumn:getTitle()
    return self:getProperty('title')
end

function TableColumn:getWeight()
    return self:getProperty('weight')
end

function TableColumn:getWidth()
    return self:getProperty('width')
end

function TableColumn:getInspectors()
    local list = List:new()
    local inspector, hook
    local columnList = self.parent:getColumnList()
    inspector = Inspector:new{
        title = 'Type',
        type = 'Class',
        constraint = function()
            return TableColumn:getSubclasses()
        end,
    }
    hook = Hook:new(
        function()
            return self:class()
        end,
        function(value)
            local index = columnList:index(self)
            local clone = _G[value]:new():mirror(self)
            columnList:replace(self, clone)
            unarchived()
        end)
    inspector:addHook(hook)
    list:add(inspector)
    list:add(self:createInspector('string', {'title'}, 'Title'))
    for i = 1, #self.inspectorInfo do
        local info = self.inspectorInfo[i]
        list:add(self:createInspector(unpack(info)))
    end
    return list
end

function TableColumn:createInspector(type, hooks, title, undoTitle)
    local inspector = super.createInspector(self, type, hooks, title, undoTitle)
    inspector:addHook(self._datasetHook, 'dataSource')
    return inspector
end

function TableColumn:setParent(parent)
    self.parent = parent
end

function TableColumn:getParent()
    return self.parent
end

function TableColumn:setColorSchemeHook(hook)
    if self._colorSchemeHook ~= hook then
        hook:addObserver(self)
    end
    self._colorSchemeHook = hook
end

function TableColumn:setTypographySchemeHook(hook)
    if self._typographySchemeHook ~= hook then
        hook:addObserver(self)
    end
    self._typographySchemeHook = hook
end

function TableColumn:setDatasetHook(hook)
    self._datasetHook = hook
end

function TableColumn:getDataset()
    return self._datasetHook:getValue()
end

function TableColumn:getDefaultPaint()
    return self:getParent():getColumnPaint()
end

function TableColumn:getPaint()
    return self:getProperty('paint') or self:getDefaultPaint()
end

function TableColumn:setPaint(value)
    self:setProperty('paint', value)
end

function TableColumn:hasExplicitPaint()
    return self:getProperty('paint') ~= nil
end

function TableColumn:makePropertySequence()
    if not self._propertySequence then
        Profiler.time(self:class() .. ':makePropertySequence', function()
            self._datasetHook:addObserver(self._propertySequenceInvalidator)
            local sequences = {}
            local dataset = self:getDataset()
            local propertyNames = self.getterNames
            for index = 1, #propertyNames do
                local propertyName = propertyNames[index]
                local propertyHook = self:getPropertyHook(propertyName)
                propertyHook:addObserver(self._propertySequenceInvalidator) -- TODO: try to re-calculate only the affected sequences
                sequences[#sequences + 1] = self:getPropertySequence(propertyName, dataset)
            end
            self._propertySequence = Sequence:newWithSequenceList(sequences)
        end)
    end
    return self._propertySequence
end

function TableColumn:getAlignment()
    return self:getProperty('halign') or 0.5
end

function TableColumn:drawCell(canvas, rect, ...)
    self.stamp(canvas, rect, ...)
end

return TableColumn
