local super = require "Axis"

NumericAxis = super:new()

local defaults = {
}

local nilDefaults = {
    'min', 'max',
}

local _min = math.min
local _max = math.max

function NumericAxis: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.valueInspectorInfo = {}
    
    return self
end

function NumericAxis:getSubclasses()
    return {
        {'LinearAxis', 'Linear'},
        {'LogarithmicAxis', 'Logarithmic'},
        {'DateAxis', 'Date'},
    }
end

function NumericAxis:isQuantitative()
    return true
end

function NumericAxis:getValueInspectors(parent, axisName)
    local list = super.getValueInspectors(self, parent)
    local type
    if self:allowsNumberValues() then
        type = 'NumericAxis.values.number'
    elseif self:allowsDateValues() then
        type = 'NumericAxis.values.Date'
    end
    assert(type)
    local inspector = Inspector:new{
        title = axisName,
        undoTitle = axisName .. ' Range',
        type = type,
        constraint = function()
            return NumericAxis:getSubclasses()
        end,
    }
    local hook = Hook:new(
        function()
            return self:class()
        end,
        function(value)
            local orientation = self:getOrientation()
            self = _G[value]:new():mirror(self)
            if orientation == Graph.horizontalOrientation then
                parent:setHorizontalAxis(self)
            elseif orientation == Graph.verticalOrientation then
                parent:setVerticalAxis(self)
            end
            unarchived()
        end)
    inspector:addHook(hook)
    for hookName, propertyName in pairs(self.valueInspectorInfo) do
        local hook = self:getPropertyHook(propertyName)
        if hook then
            inspector:addHook(hook, hookName)
        end
    end
    list:add(inspector)
    return list
end

function NumericAxis:setRange(min, max)
    self:setProperty('min', min)
    self:setProperty('max', max)
end

function NumericAxis:setAutoRange(min, max)
    self._autoMin = min
    self._autoMax = max
end

function NumericAxis:getRange()
    local min, max = self:getExplicitRange()
    min = min or self._autoMin
    max = max or self._autoMax
    return min, max
end

function NumericAxis:getExplicitRange()
    return self:getProperty('min'), self:getProperty('max')
end

function NumericAxis:getValueConverters()
    return function(value) end, function(value) end
end

function NumericAxis:setAutoRangeForValueRange(min, max)
end

function NumericAxis:updateAutoRange(groupIterator)
    local valueMin, valueMax = math.huge, -math.huge
    local fixedMin, fixedMax = self:getExplicitRange()
    if not fixedMin or not fixedMax then
        local numericValueGetters = {}
        local converter, inverseConverter = self:getValueConverters()
        groupIterator(function()
            local groupMin, groupMax = math.huge, -math.huge
            numericValueGetters[#numericValueGetters + 1] = function()
                return groupMin, groupMax
            end
            local valuePutter = function(value)
                value = converter(value)
                if value and value == value then
                    groupMin = _min(groupMin, value)
                    groupMax = _max(groupMax, value)
                end
            end
            local resultGetter = function()
                return inverseConverter(groupMin), inverseConverter(groupMax)
            end
            return valuePutter, resultGetter
        end)
        for index = 1, #numericValueGetters do
            local groupMin, groupMax = numericValueGetters[index]()
            valueMin = _min(valueMin, groupMin)
            valueMax = _max(valueMax, groupMax)
        end
    end
    self:setAutoRangeForValueRange(valueMin, valueMax)
end

return NumericAxis
