local super = require "GraphLayer"

LineGraphLayer = super:new()

local defaults = {
    size = 2,
}

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

local freeGetterNames = {'x', 'y'}
local constrainedGetterNames = {'#', 'value'}
local commonGetterNames = {'paint', 'size'}

local freeInspectorInfo = {
    {'KeyArtifact', {'x'}, 'X'},
    {'KeyArtifact', {'y'}, 'Y'},
}

local constrainedInspectorInfo = {
    {'KeyArtifact', {'value'}, 'Value'},
}

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

function LineGraphLayer: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 LineGraphLayer:unarchived()
    local dataset = self:getDataset()
    if dataset then
        if self:isPositionConstrained() then
            if self:getProperty('value') == nil then
                local valueFields = self:peerPropertyKeyArtifactValues(LineGraphLayer, 'value')
                local valueField = dataset:pickField('number', valueFields)
                if valueField then
                    self:setProperty('value', KeyArtifact:new(valueField))
                end
            end
        else
            if self:getProperty('x') == nil and self:getProperty('y') == nil then
                local xFields = self:peerPropertyKeyArtifactValues(GraphLayer, 'x')
                local xField = xFields[#xFields] or dataset:pickField(self:getParent():getHorizontalAxis():getPreferredType())
                local yFields = self:peerPropertyKeyArtifactValues(LineGraphLayer, 'y')
                yFields[#yFields + 1] = xField
                local yField = dataset:pickField(self:getParent():getVerticalAxis():getPreferredType(), yFields)
                if xField and yField then
                    self:setProperty('x', KeyArtifact:new(xField))
                    self:setProperty('y', KeyArtifact:new(yField))
                end
            end
        end
    end
    super.unarchived(self)
end

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

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

function LineGraphLayer:iterateValues(orientation, mapFunction)
    local dataset = self:getDataset()
    local propertyName = 'value'
    if not self:isPositionConstrained() then
        if orientation == Graph.horizontalOrientation then
            propertyName = 'x'
        else
            propertyName = 'y'
        end
    end
    local sequence = self:getPropertySequence(propertyName, dataset)
    for _, value in sequence:iter() do
        mapFunction(value)
    end
end

function LineGraphLayer: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)
    
    local linePath, pathLength, lastx, lasty, paint, size
    local drawAccumulatedPath = function()
        canvas:setPaint(paint or defaultPaint):setThickness(size and size * baseSize):stroke(linePath)
        linePath = nil
        lastx, lasty = nil, nil
        pathLength = 0
    end
    
    local sqrt = math.sqrt
    propertySequence:each(function(position, value, newPaint, newSize)
        local x, y
        if isVertical then
            x, y = xScaler(position), yScaler(value)
        else
            y, x = yScaler(position), xScaler(value)
        end
        if x and y then
            if linePath and sqrt((x - lastx) * (x - lastx) + (y - lasty) * (y - lasty)) > 0.25 then
                linePath:addLineXY(x, y)
                lastx, lasty = x, y
                pathLength = pathLength + 1
            end
            if not linePath or size ~= newSize or paint ~= newPaint or pathLength == 1000 then
                drawAccumulatedPath()
                paint = newPaint
                size = newSize
                linePath = Path.point{ x = x, y = y }
                lastx, lasty = x, y
            end
        else
            drawAccumulatedPath()
        end
    end)
    
    drawAccumulatedPath()
end

return LineGraphLayer
