local super = require "View"

Container = super:new()

local defaults = {
}

local nilDefaults = {
}

function Container: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:notUndoably(function()
        self:setProperty('colorScheme', ColorScheme:get('basic2'))
        self:setProperty('typographyScheme', TypographyScheme:get('basic'))
    end)
    
    self._removalObserver = nil
    self._children = List:new()
    self._children:setUndoable(true)
    
    return self
end

function Container:unarchiveChildren(archived)
    self._children:setUndoable(false)
    for index = 1, #archived do
        local child = unarchive(archived[index])
        if Object.isa(child, View) then
            self._children:add(child)
        end
    end
    if self._children:count() > 0 then
        local childIter = self._children:iter()
        local rect = childIter():rect()
        local left, top = rect.left, rect.top
        for child in childIter do
            rect = child:rect()
            left = math.min(left, rect.left)
            top = math.max(top, rect.top)
        end
        local newLeft, newTop = math.max(left, 0), math.min(top, 0)
        if FormatVersion.current < FormatVersion.minimum.noDocumentPadding then
            local legacyPadding = ({ point = 50, inch = 0.5 * 72, centimeter = 1 * 72 / 2.54 })[Meta.unit] or 0
            local legacyMaximum = ({ point = 400, inch = 4 * 72, centimeter = 10 * 72 / 2.54 })[Meta.unit] or 0
            newLeft, newTop = math.max(newLeft, legacyPadding), math.min(newTop, -legacyPadding)
            newLeft, newTop = math.min(newLeft, legacyMaximum), math.max(newTop, -legacyMaximum)
        end
        if left ~= newLeft or top ~= newTop then
            for child in self._children:iter() do
                child:setRect(child:rect():offset(newLeft - left, newTop - top))
            end
        end
    end
    self._children:setUndoable(true)
end

function Container:unarchiveBackground(archived)
    -- NOTE: Version 1.3 and earlier stored the page background separately from the color scheme.
    self._legacyPageBackgroundColor = unarchive(archived)
end

function Container:unarchived()
    if self._legacyPageBackgroundColor then
        self:setPaint(ColorScheme.pageBackgroundPaint, self._legacyPageBackgroundColor)
    end

    self._addChildObserver = function(view)
        view:setParent(self)
        view:addObserver(self)
        view:setMagnet(function(x, y) return self.magnet(x, y) end)
        self:invalidate(view)
    end
    self._children:addEventObserver('add', self._addChildObserver)
    self._removeChildObserver = function(view)
        if self._removalObserver then
            self._removalObserver(view)
        end
        view:removeObserver(self)
        view:setMagnet(nil)
        self:invalidate(view)
    end
    self._children:addEventObserver('remove', self._removeChildObserver)
    for child in self._children:iter() do
        self._addChildObserver(child)
    end

    super.unarchived(self)
end

function Container:archive()
    local typeName, properties = super.archive(self)
    local children = {}
    for child in self._children:iter() do
        children[#children + 1] = child
    end
    properties.children = children
    return typeName, properties
end

function Container:getInspectors()
    local list = super.getInspectors(self)
    local inspector
    inspector = Inspector:new{
        title = 'Page Margins',
        type = 'Popup',
        icon = 'Ruler',
        target = function()
            local list = List:new()
            local inspector
            inspector = self:createInspector('Margins', {}, nil, 'Page Margins')
            list:add(inspector)
            return list
        end,
    }
    list:add(inspector)
    inspector = Inspector:new{
        title = 'Colors',
        type = 'Popup',
        icon = 'Colors',
        target = function()
            return self:getColorSchemeInspectors()
        end,
    }
    list:add(inspector)
    inspector = Inspector:new{
        title = 'Fonts',
        type = 'Popup',
        icon = 'Fonts',
        target = function()
            return self:getTypographySchemeInspectors()
        end,
    }
    list:add(inspector)
    return list
end

function Container:getColorInspectors()
    local list = super.getColorInspectors(self)
    list:add(self:createColorInspector(ColorScheme.pageBackgroundPaint, 'Page'))
    list:add(self:createColorInspector(ColorScheme.backgroundPaint, 'Background'))
    list:add(self:createColorInspector(ColorScheme.titlePaint, 'Titles'))
    list:add(self:createColorInspector(ColorScheme.labelPaint, 'Labels'))
    list:add(self:createColorInspector(ColorScheme.strokePaint, 'Axes'))
    list:add(self:createColorInspector(ColorScheme.gridPaint, 'Grid Lines'))
    list:add(self:createColorInspector(ColorScheme.alternateBackgroundPaint, 'Alternating Rows'))
    list:add(self:createColorInspector(ColorScheme.highlightPaint, 'Highlight'))
    list:add(self:createColorInspector(ColorScheme.emptyFillPaint, 'Empty Fill'))
    return list
end

function Container:getDataColorInspectors()
    local list = super.getDataColorInspectors(self)
    for index = 1, self:getColorScheme():getDataPaintCount() do
        list:add(self:createDataColorInspector(index, 'Data ' .. index))
    end
    return list
end

function Container:getFontInspectors()
    local list = super.getFontInspectors(self)
    list:add(self:createFontInspector(TypographyScheme.titleFont, 'Titles'))
    list:add(self:createFontInspector(TypographyScheme.subtitleFont, 'Axis & Column Titles'))
    list:add(self:createFontInspector(TypographyScheme.quantityFont, 'Axis Values'))
    list:add(self:createFontInspector(TypographyScheme.categoryFont, 'Axis Categories'))
    list:add(self:createFontInspector(TypographyScheme.labelFont, 'Data'))
    list:add(self:createFontInspector(TypographyScheme.blockFont, 'Text Blocks'))
    return list
end

local function drawThumbnail(canvas, rect, fonts, paints)
    rect = rect:inset{ left = 8, bottom = 8, right = 8, top = 2 + fonts.title:ascent() }
    local maxy, height = rect:maxy(), rect:height()
    local y1 = maxy
    local y2 = maxy - height * 1 / 4
    local y3 = maxy - height * 2 / 4
    local y4 = maxy - height * 3 / 4
    local y5 = maxy - height * 4 / 4
    canvas:setPaint(paints.title):setFont(fonts.title)
        :drawText('Titles', rect:minx(), y1)
    canvas:setPaint(paints.subtitle):setFont(fonts.subtitle)
        :drawText('Axis & Column Titles', rect:minx(), y2)
    canvas:setPaint(paints.quantity):setFont(fonts.quantity)
        :drawText('Axis Values', rect:minx(), y3)
    canvas:setPaint(paints.label):setFont(fonts.label)
        :drawText('Data & Labels', rect:minx(), y4)
    canvas:setPaint(paints.block):setFont(fonts.block)
        :drawText('Text Blocks', rect:minx(), y5)
end

function Container:drawTypographySchemePreview(canvas, rect, typographyScheme)
    local SIZE = 12
    local fonts = {
        title = typographyScheme:getFont(TypographyScheme.titleFont, SIZE),
        subtitle = typographyScheme:getFont(TypographyScheme.subtitleFont, SIZE),
        quantity = typographyScheme:getFont(TypographyScheme.quantityFont, SIZE),
        label = typographyScheme:getFont(TypographyScheme.labelFont, SIZE),
        block = typographyScheme:getFont(TypographyScheme.blockFont, SIZE),
    }
    local paints = {
        title = Color.gray(0, 1),
        subtitle = Color.gray(0, 1),
        quantity = Color.gray(0, 1),
        label = Color.gray(0, 1),
        block = Color.gray(0, 1),
    }
    drawThumbnail(canvas, rect, fonts, paints)
end

function Container:setRect(rect)
end

function Container:rect()
    local rect = Rect:zero()
    local contentsRect = self:contentsRect()
    if contentsRect then
        rect = rect:union(contentsRect)
    end
    return rect
end

function Container:contentsRect()
    local rect
    for child in self._children:iter() do
        if rect then
            rect = rect:union(child:rect())
        else
            rect = child:rect()
        end
    end
    return rect
end

function Container:add(view)
    self._children:add(view)
end

function Container:insert(view, index)
    self._children:insert(view, index)
end

function Container:count()
    return self._children:count()
end

function Container:child(index)
    return self._children:get(index)
end

function Container:childIndex(view)
    return self._children:index(view)
end

function Container:remove(view)
    self._children:remove(view)
end

function Container:draw(canvas)
    local dirtyRect = canvas:dirtyRect()
    for child in self._children:iter() do
        local childRect = child:clippingRect(canvas:metrics())
        if dirtyRect:intersects(childRect) then
            Profiler.time(child:class() .. ":draw", function()
                canvas:preserve(function(canvas)
                    canvas:clipRect(childRect)
                    child:draw(canvas)
                    child:didDraw()
                end, true)
            end)
        end
    end
end

-- NOTE: Don't call this function directly! Give it to pcall.
local function __PROTECTED__drawChildAndTest(canvas, child)
    child:draw(canvas)
    return canvas:test()
end

function Container:drawAndTest(canvas)
    for child in self._children:reverseIter() do
        local result = canvas:pcall(function() return __PROTECTED__drawChildAndTest(canvas, child) end)
        if result then
            return child
        end
    end
end

function Container:setRemove(observer)
    self._removalObserver = observer
end

function Container:getBackgroundPaint()
    return self:getPaint(ColorScheme.pageBackgroundPaint)
end

return Container
