using Colors
DEFAULTSYMBOLS = [:original, :auto, :default]
iter_expand(e) = Base.Iterators.repeated(e)
iter_expand(l::Vector) = Base.Iterators.cycle(l)
iter_expand(r::AbstractRange) = IterGen(st->rand(r))
iter_expand(t::Tuple) = IterGen(st->rand(t))
struct IterGen
    generator
end
Base.iterate(it::IterGen, state=0) = it.generator(state),state+1
Base.IteratorSize(it::IterGen) = Base.IsInfinite()


"""
load a img as mask, recolor, or resize, etc
## examples
* loadmask(open("res/heart.jpg"), 256, 256) #resize to 256*256  
* loadmask("res/heart.jpg", ratio=0.3) #scale 0.3  
* loadmask("res/heart.jpg", color="red", ratio=2) #set forecolor  
* loadmask("res/heart.jpg", transparent=rgba->maximum(rgba[1:3])*(rgba[4]/255)>128) #set transparent with a Function 
* loadmask("res/heart.jpg", color="red", transparent=(1,1,1)) #set forecolor and transparent  
* loadmask("res/heart.svg") #other arguments are not supported
About orther keyword arguments like outline, linecolor, smoothness, see function `outline`.
"""
function loadmask(img::AbstractMatrix{<:TransparentRGB}, args...; 
    color=:auto, backgroundcolor=:auto, transparent=:auto, 
    outline=0,  linecolor="black", smoothness=0.5, padding=0, kargs...)
    copied = false
    if !(isempty(args) && isempty(kargs))
        img = imresize(img, args...; kargs...)
        copied = true
    end
    if color ∉ DEFAULTSYMBOLS || backgroundcolor ∉ DEFAULTSYMBOLS
        copied || (img = copy(img))
        mask = imagemask(img, transparent)
        if color ∉ DEFAULTSYMBOLS
            color = parsecolor(color)
            alpha(color) == 1 || @warn "the alpha channel is ignored"
            m = @view img[mask]
            Render.recolor!(m, color) #保持透明度
        end
        if backgroundcolor ∉ DEFAULTSYMBOLS
            backgroundcolor = parsecolor(backgroundcolor)
            m = @view img[.~mask]
            m .= convert.(eltype(img), backgroundcolor) #不保持透明度
        end
    end
    if outline > 0
        img = Render.outline(img, linewidth=outline, color=linecolor, smoothness=smoothness, 
        transparent=transparent)
    end
    if padding != 0
        bc = backgroundcolor in DEFAULTSYMBOLS ? :auto : bc
        img = Render.padding(img, padding, backgroundcolor=bc)
    end
    img
end
function loadmask(img::AbstractMatrix{<:Colorant}, args...; kargs...)
    loadmask(ARGB.(img), args...; kargs...)
end
function loadmask(img::SVGImageType, args...; padding=0, transparent=:auto, outline=0, linecolor=:auto, kargs...)
    if !isempty(args) || !isempty(v for v in values(values(kargs)) if v ∉ DEFAULTSYMBOLS) || outline != 0
        @warn "editing svg file is not supported: $args $kargs"
    end
    if padding != 0
        bc = get(kargs, :backgroundcolor, (0,0,0,0))
        bc = bc in DEFAULTSYMBOLS ? (0,0,0,0) : bc
        img = Render.padding(img, padding, backgroundcolor=bc)
    end
    img
end
function loadmask(file, args...; kargs...)
    mask = Render.load(file)
    loadmask(mask, args...; kargs...)
end

"like `paint` but export svg"
function paintsvg(wc::WC; background=true)
    imgs = getsvgimages(wc)
    poss = getpositions(wc)
    if background == false || background === nothing
        sz = size(wc.mask)
        bgcolor = (1,1,1,0)
    else
        if background == true
            bgcolor = getbackgroundcolor(wc)
            bgcolor = bgcolor in DEFAULTSYMBOLS ? (1,1,1,0) : bgcolor
            background = getsvgmask(wc)
            if background === nothing
                @warn "embed bitmap into SVG. You can set `background=false` to remove background."
                background = getmask(wc)
            end
        else
            bgcolor = (1,1,1,0)
        end
        imgs = Iterators.flatten(((background,), imgs))
        poss = Iterators.flatten((((1, 1),), poss))
        sz = size(background)
    end
    Render.overlay(imgs, poss, backgroundcolor=bgcolor, size=reverse(sz))
end
function paintsvg(wc::WC, file, args...; kargs...)
    img = paintsvg(wc, args...; kargs...)
    Render.save(file, img)
    img
end

"""
# examples
* paint(wc::WC)
* paint(wc::WC, background=false) #no background
* paint(wc::WC, background=outline(wc.mask)) #use a new background
* paint(wc::WC, ratio=0.5) #resize the result
* paint(wc::WC, "result.png", ratio=0.5) #save as png file, other bitmap formats may also work
* paint(wc::WC, "result.svg") #save as svg file
"""
function paint(wc::WC, args...; background=true, kargs...)
    if background == true
        bgcolor = getbackgroundcolor(wc)
        bgcolor = bgcolor in DEFAULTSYMBOLS ? (1,1,1,0) : bgcolor
        background = fill(convert(eltype(wc.mask), parsecolor(bgcolor)), size(wc.mask))
        overlay!(background, wc.mask)
    elseif background == false || background === nothing
        background = fill(convert(eltype(wc.mask), parsecolor((1,1,1,0))), size(wc.mask))
    else
        background = copy(background)
    end
    overlay!(background, wc.imgs, getpositions(wc))
    if !(isempty(args) && isempty(kargs))
        background = ARGB.(background) #https://github.com/JuliaImages/ImageTransformations.jl/issues/97
        background = imresize(background, args...; kargs...)
    end
    background
end

function paint(wc::WC, file, args...; kargs...)
    if endswith(file, r".svg|.SVG")
        img = paintsvg(wc, args...; kargs...)
    else
        img = paint(wc, args...; kargs...)
    end
    Render.save(file, img)
    img
end
        
function record(wc::WC, label::AbstractString, gif_callback=x->x)
#     @show size(n1)
    resultpic = overlay!(paint(wc), 
        rendertextoutlines(label, 32, color="black", linecolor="white", linewidth=1), 20, 20)
    gif_callback(resultpic)
end


runexample(example=:random) = @time evalfile(pkgdir(WordCloud)*"/examples/$(example).jl")
showexample(example=:random) = read(pkgdir(WordCloud)*"/examples/$(example).jl", String)|>print
examples = [e[1:prevind(e, end, 3)] for e in basename.(readdir(pkgdir(WordCloud)*"/examples")) if endswith(e, ".jl")]
@doc "Available values: [" * join(":".*examples, ", ") * "]" runexample
@doc "Available values: [" * join(":".*examples, ", ") * "]" showexample
function runexamples(examples=examples)
    println(length(examples), " examples: ", examples)
    for (i,e) in enumerate(examples)
        println("="^20, "\n# ",i,"/",length(examples), "\t", e, "\n", "="^20)
        runexample(e)
    end
end
