local super = require "GraphLayer"

ArrowGraphLayer = super:new()

local defaults = {
    width = 1,
}

local nilDefaults = {
    'x', 'open', 'close', 'paint',
}

local freeGetterNames = {'x'}
local constrainedGetterNames = {'#'}
local commonGetterNames = {'open', 'close', 'width', 'paint'}

local freeInspectorInfo = {
    {'KeyArtifact', {'x'}, 'Position'},
    {'KeyArtifact', {'open'}, 'Start'},
    {'KeyArtifact', {'close'}, 'End'},
    {'number', {'width'}, 'Width'},
}

local constrainedInspectorInfo = {
    {'KeyArtifact', {'open'}, 'Start'},
    {'KeyArtifact', {'close'}, 'End'},
}

local commonInspectorInfo = {
    {'Color', {'getPaint:setPaint', custom = 'hasExplicitPaint:'}, 'Color'},
}

function ArrowGraphLayer: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
    
    return self
end

function ArrowGraphLayer:unarchived()
    local dataset = self:getDataset()
    if dataset then
        local xField
        if self:isPositionConstrained() then
            if self:getProperty('x') == nil then
                local xFields = self:peerPropertyKeyArtifactValues(GraphLayer, 'x')
                xField = xFields[#xFields] or dataset:pickField('number')
                if xField then
                    self:setProperty('x', KeyArtifact:new(xField))
                end
            end
        end
        if self:getProperty('open') == nil and self:getProperty('close') == nil then
            local axis
            if self:getOrientation() == Graph.verticalOrientation then
                axis = self:getParent():getVerticalAxis()
            else
                axis = self:getParent():getHorizontalAxis()
            end
            local avoidingFields = { xField }
            local openField = dataset:pickField(axis:getPreferredType(), avoidingFields)
            avoidingFields[#avoidingFields + 1] = openField
            local closeField = dataset:pickField(axis:getPreferredType(), avoidingFields)
            if openField and closeField then
                self:setProperty('open', KeyArtifact:new(openField))
                self:setProperty('close', KeyArtifact:new(closeField))
            end
        end
    end
    super.unarchived(self)
end

function ArrowGraphLayer:getGetterPieceNames(constrained)
    local result = {}
    if constrained then
        appendtables(result, constrainedGetterNames)
    else
        appendtables(result, freeGetterNames)
    end
    appendtables(result, commonGetterNames)
    return result
end

function ArrowGraphLayer:getInspectorInfo(constrained)
    local result = {}
    if constrained then
        appendtables(result, constrainedInspectorInfo)
    else
        appendtables(result, freeInspectorInfo)
    end
    appendtables(result, commonInspectorInfo)
    return result
end

function ArrowGraphLayer:iterateValues(orientation, mapFunction)
    local dataset = self:getDataset()
    local propertyNames
    if self:isPositionConstrained() then
        propertyNames = {'open', 'close'}
    else
        if orientation == Graph.horizontalOrientation then
            propertyNames = {'x'}
        else
            propertyNames = {'open', 'close'}
        end
    end
    for index = 1, #propertyNames do
        local sequence = self:getPropertySequence(propertyNames[index], dataset)
        for _, value in sequence:iter() do
            mapFunction(value)
        end
    end
end

function ArrowGraphLayer:isGroupable()
    return true
end

function ArrowGraphLayer:draw(canvas, rect, propertySequence, xScaler, yScaler)
    local defaultPaint = self:getPaint()
    local thickness = 1
    local isVertical = self:getOrientation() == Graph.verticalOrientation
    canvas:clipRect(rect)
    
    propertySequence:each(function(position, open, close, width, paint)
        if isVertical then
            local left, right = xScaler(position, width)
            open, close = yScaler(open), yScaler(close)
            if left and right and open and close then
                VerticalArrowStamp(canvas, (left + right) / 2, open, close, (right - left) * 0.5, paint or defaultPaint)
            end
        else
            local bottom, top = yScaler(position, width)
            open, close = xScaler(open), xScaler(close)
            if bottom and top and open and close then
                HorizontalArrowStamp(canvas, (bottom + top) / 2, open, close, (top - bottom) * 0.5, paint or defaultPaint)
            end
        end
    end)
end

return ArrowGraphLayer
