local super = require "GraphLayer"

LabelGraphLayer = super:new()

local defaults = {
    halign = 0.5,
    valign = 0,
    distance = 5,
    rotation = 0,
}

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

local freeGetterNames = {'x', 'y'}
local constrainedGetterNames = {'#', 'value'}
local commonGetterNames = {'text', 'paint', 'font', 'halign', 'valign', 'distance', 'rotation'}

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

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

local commonInspectorInfo = {
    {'KeyArtifact', {'text'}, 'Text'},
    {'Color', {'getPaint:setPaint', custom = 'hasExplicitPaint:'}, 'Color'},
    {'Font', {'getFont:setFont', custom = 'hasExplicitFont:'}, 'Font'},
    {'Position', {x = 'halign', y = 'valign', distance = 'distance'}, 'Position'},
    {'Rotation', {'rotation'}, 'Rotation'},
}

local _sqrt = math.sqrt
local _rad = math.rad

function LabelGraphLayer: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 LabelGraphLayer:unarchived()
    local dataset = self:getDataset()
    if dataset then
        if self:isPositionConstrained() then
            if self:getProperty('value') == nil then
                local valueFields = self:peerPropertyKeyArtifactValues(LabelGraphLayer, '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(LabelGraphLayer, '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
        if self:getProperty('text') == nil then
            local textFields = self:peerPropertyKeyArtifactValues(LabelGraphLayer, 'text')
            local textField = dataset:pickField('string', textFields)
            if textField then
                self:setProperty('text', KeyArtifact:new(textField))
            end
        end
    end
    super.unarchived(self)
end

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

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

function LabelGraphLayer:getGetterHooks()
    return getterHooks
end

function LabelGraphLayer: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 LabelGraphLayer:getPaint()
    return self:getProperty('paint') or self:getParent():getLayerLabelPaint()
end

function LabelGraphLayer:usesLayerPaint()
    return false
end

function LabelGraphLayer:getFont()
    return self:getProperty('font') or self:getParent():getLayerLabelFont()
end

function LabelGraphLayer:setFont(value)
    self:setProperty('font', value)
end

function LabelGraphLayer:hasExplicitFont()
    return self:getProperty('font') ~= nil
end

function LabelGraphLayer:draw(canvas, rect, propertySequence, xScaler, yScaler)
    local defaultPaint = self:getPaint()
    local defaultFont = self:getParent():getLayerLabelFont()
    local isVertical = self:getOrientation() == Graph.verticalOrientation
    canvas:clipRect(rect:expand{left = 8, bottom = 8, right = 8, top = 8})
    
    propertySequence:each(function(position, value, text, paint, font, halign, valign, distance, rotation)
        local x, y
        if isVertical then
            x, y = xScaler(position), yScaler(value)
        else
            y, x = yScaler(position), xScaler(value)
        end
        if text and x and y and rect.left <= x and x <= rect.right and rect.bottom <= y and y <= rect.top then
            local transformation = Transformation:identity()
            local dx, dy = 1 - 2 * halign, 1 - 2 * valign
            if dx ~= 0 or dy ~= 0 then
                distance = distance / _sqrt(dx ^ 2 + dy ^ 2)
                dx, dy = dx * distance, dy * distance
                transformation:translate(dx, dy)
            end
            if rotation and rotation ~= 0 then
                transformation:rotate(_rad(rotation))
            end
            TextPointStamp(canvas, x, y, text, paint or defaultPaint, font or defaultFont, halign, valign, transformation)
        end
    end)
end

return LabelGraphLayer
