local super = require "GraphLayer"

ReferenceGraphLayer = super:new()

local defaults = {
    size = 1.5,
    lineStyle = 'dotted',
}

local nilDefaults = {
    'value', 'paint',
}

local freeGetterNames = {}
local constrainedGetterNames = {}
local commonGetterNames = {'value', 'paint', 'size', 'lineStyle'}

local freeInspectorInfo = {
}

local constrainedInspectorInfo = {
}

local commonInspectorInfo = {
    {'KeyArtifact', {'value'}, 'Value'},
    {'Color', {'getPaint:setPaint', custom = 'hasExplicitPaint:'}, 'Stroke'},
    {'Stroke', {'size', lineStyle = 'lineStyle'}, 'Thickness'},
}

function ReferenceGraphLayer: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 ReferenceGraphLayer:getGetterPieceNames(constrained)
    local result = {}
    if constrained then
        appendtables(result, constrainedGetterNames)
    else
        appendtables(result, freeGetterNames)
    end
    appendtables(result, commonGetterNames)
    return result
end

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

function ReferenceGraphLayer:iterateValues(orientation, mapFunction)
    if orientation == self:getOrientation() then
        local dataset = self:getDataset()
        local sequence = self:getPropertySequence('value', dataset)
        for _, value in sequence:iter() do
            mapFunction(value)
        end
    end
end

function ReferenceGraphLayer:isOrientable()
    return true
end

function ReferenceGraphLayer:getOrientationInspector()
    local inspector = super.getOrientationInspector(self)
    inspector:addHook(Hook:new('ReferenceGraphLayer'), 'icons')
    return inspector
end

function ReferenceGraphLayer:getPaint()
    return self:getProperty('paint') or self:getParent():getAxisPaint()
end

function ReferenceGraphLayer:usesLayerPaint()
    return false
end

function ReferenceGraphLayer:draw(canvas, rect, propertySequence, xScaler, yScaler)
    local parent = self:getParent()
    local baseSize = parent:getBaseSize()
    local defaultPaint = self:getPaint()
    local isVertical = self:getOrientation() == Graph.verticalOrientation
    canvas:clipRect(rect:expand{left = 4, bottom = 4, right = 4, top = 4})

    local line = { x1 = rect.left, y1 = rect.bottom, x2 = rect.right, y2 = rect.top }
    propertySequence:each(function(value, paint, size, lineStyle)
        if isVertical then
            value = yScaler(value)
        else
            value = xScaler(value)
        end
        if value then
            size = size * baseSize
            local dx, dy = 0, 0
            if isVertical then
                line.y1 = value
                line.y2 = value
                dx = 2 * size
            else
                line.x1 = value
                line.x2 = value
                dy = 2 * size
            end
            canvas:setPaint(paint or defaultPaint)
                :setThickness(size)
            if lineStyle == 'solid' then
                canvas:stroke(Path.line(line))
            else
                if lineStyle == 'long-dashed' then
                    dx = dx * 7
                    dy = dy * 7
                elseif lineStyle == 'short-dashed' then
                    dx = dx * 3
                    dy = dy * 3
                end
                local length = math.sqrt((line.x2 - line.x1) ^ 2 + (line.y2 - line.y1) ^ 2)
                local delta = math.sqrt(dx ^ 2 + dy ^ 2)
                local steps = math.floor(length / 2 / delta + 0.25)
                local x = (line.x1 + line.x2) / 2 - dx * steps
                local y = (line.y1 + line.y2) / 2 - dy * steps
                while x <= line.x2 + dx / 4 and y <= line.y2 + dy / 4 do
                    if lineStyle == 'long-dashed' or lineStyle == 'short-dashed' then
                        canvas:stroke(Path.line{ x1 = x - dx / 4, y1 = y - dy / 4, x2 = x + dx / 4, y2 = y + dy / 4 })
                    elseif lineStyle == 'dotted' then
                        canvas:fill(Path.oval{ left = x - size / 2, bottom = y - size / 2, right = x + size / 2, top = y + size / 2 })
                    end
                    x = x + dx
                    y = y + dy
                end
            end
        end
    end)
end

return ReferenceGraphLayer
