text:
"""
    function bid_argmax_profit(args...; kwargs)

Calculates the bid that returns the maximum profit of the slack bus based on an aproximation
of the profit function which is calculated using the Transmission Constrained Residual
Demand Derivative (TCRDD). A plot of this approximation can be displayed.

Detailed description: The algorithm considers the Locational Marginal Price (LMP) for a
determined bid in the slack generator which matches to a single point of the real profit
function. Then, using the TCRDD of that specific point, an approximation of the real profit
function is build. The function then evaluates the aproximated function from the minimum
active power (Pmin) to the maximum active power (Pmax) of the slack generator. The bid that
provides the maximum profit is returned.

# Arguments
- `sys::System`:                            Power system in p.u. (from PowerSystems.jl)
- `res::PowerSimulations.
    OperationsProblemResults`:              Results of the solved OPF for the system
                                             (from PowerSimulations.jl)
- `BaseMVA::Float64`:                       Apparent Power Base for the system [MVA]
- `lmp::DataFrame`:                         LMP of all buses of the system
- `tcrdd_slack::Float64`:                   TCRDD [pu]
- `bid::Float64`:                           Bid for the slack generator [pu]
- `Pmin_orig::Float64`:                     Active power minimum limit [pu]
- `Pmax_orig::Float64`:                     Active power maximum limit [pu]

# Keywords
- `segm_bid_argmax_profit::Int64 = 50000:   Segments to evaluate the approx profit function
- `print_plots::Bool = true`:               Flag to print plots

# Throws
- `NotFoundError`:                          No Slack bus found in the system
"""
function bid_argmax_profit(
    sys::System,
    res::PowerSimulations.OperationsProblemResults,
    BaseMVA::Float64 ,
    lmp::DataFrame,
    tcrdd_slack::Float64,
    bid::Float64,
    Pmin_orig::Float64,
    Pmax_orig::Float64;
    segm_bid_argmax_profit::Int64 = 50000,
    print_plots::Bool = true
    )
    # Calculate the bid with argmax in a function that gets evaluated.
    bus_slack_name = "Empty"
    has_slack = false
    # Find slackbus
    buses = get_components(Bus, sys)
    for bus in buses
        if get_bustype(bus) == BusTypes.REF
            has_slack = true
            bus_slack_name = bus.name
        end
    end
    # Storage the lmp of the slack bus
    if has_slack
        lmp_slack = lmp[1,bus_slack_name]
    else
        error("NotFoundError: No Slack found in the system")
    end

    # Get Slack Generator component, ID and name
    gen_thermal_slack,gen_thermal_slack_id,gen_thermal_slack_name = get_thermal_slack(sys)

    # Get Optimised Pg of slack
    all_PGenThermal = get_variables(res)[:P__ThermalStandard] #Optimised PGen
    Pg_slack = all_PGenThermal[1, gen_thermal_slack_name]

    # Get Slack Generator Costs gen_cost = αPg² + βPg + γ
    α, β = get_cost(get_variable(get_operation_cost(gen_thermal_slack)))
    γ = get_fixed(get_operation_cost(gen_thermal_slack))

    # Correct tcrdd pu using the MVA base
    tcrdd_slack = tcrdd_slack/(BaseMVA^2)

    # Calculate profits for all segments of Pg using the tcrdd
    segment = 0
    bids = Array{Float64}(undef,segm_bid_argmax_profit + 1);
    profits = Array{Float64}(undef,segm_bid_argmax_profit + 1);
    step = (Pmax_orig - Pmin_orig)/segm_bid_argmax_profit;
    for Pg in Pmin_orig:step:Pmax_orig
        segment = segment + 1
        #calculate the lmp aproximation using tcrdd
        tan_inv_RDC = (((1/tcrdd_slack)*(Pg-Pg_slack)) + lmp_slack)
        gen_cost =  α*(Pg^2)*(BaseMVA^2) + β*Pg*BaseMVA + γ
        bids[segment] = Pg
        profits[segment] = (tan_inv_RDC*Pg*BaseMVA - gen_cost)
    end
    if print_plots == true
        plotly()
        p=plot(bids, profits,title = "Approximated Profit function at bid: $bid",
            label = ["Profit(bid, $bid"], legend = :bottomright, xlim = [0, 1], ylim = [0, 3e5]);
        xlabel!("bids/Pg(pu)");
        ylabel!("profit(\$/hr)")
        display(p)
    end
    profit_argmax, segment_profit_argmax = findmax(profits)
    bid_argmax = bids[segment_profit_argmax]
    return profit_argmax, bid_argmax
end

max_stars_repo_name: invenia/MaximumGeneratorProfitTCRDD

max_issues_repo_name: invenia/MaximumGeneratorProfitTCRDD

max_forks_repo_name: invenia/MaximumGeneratorProfitTCRDD


text:
using MyTestPackage
using Test

@testset "MyTestPackage.jl" begin
    @test sayhi() == "Hi!"
end

ext: jl


text:
function functions(ex::Expr,s::Set)
    if ((ex.head == :function || ex.head == :(=)) && isa(ex.args[1],Expr) && ex.args[1].head == :call)
        fn = ex.args[1].args[1]
        isa(fn, Expr) && fn.head == :curly && (fn = fn.args[1])
        if !in(fn,s)
            push!(s,fn)
            if !isa(fn,Symbol)
                println("# $fn: Not a symbol")
            elseif !isdefined(fn)
                println("# $fn: Not exported")
            else
                println("# $fn")
            end
        end
    else
        for a in ex.args
            if isa(a, Expr)
                functions(a,s)
            end
        end
    end
end

ex = parse("module Foo\n"*readall(ARGS[1])*"\nend\n")
functions(ex,Set())


text:
module FFTW

export fft, bfft, ifft, rfft, brfft, irfft,
       plan_fft, plan_bfft, plan_ifft, plan_rfft, plan_brfft, plan_irfft,
       fft!, bfft!, ifft!, plan_fft!, plan_bfft!, plan_ifft!,
       r2r, r2r!, plan_r2r, plan_r2r!,
       export_wisdom, import_wisdom, import_system_wisdom, forget_wisdom,
       MEASURE, DESTROY_INPUT, UNALIGNED, CONSERVE_MEMORY, EXHAUSTIVE,
       PRESERVE_INPUT, PATIENT, ESTIMATE, WISDOM_ONLY, NO_TIMELIMIT,
       R2HC, HC2R, DHT, REDFT00, REDFT01, REDFT10, REDFT11,
       RODFT00, RODFT01, RODFT10, RODFT11,
       fftwNumber, fftwReal, fftwComplex

## FFT: Implement fft by calling fftw.

@unix_only begin
const libfftw = "libfftw3_threads"
const libfftwf = "libfftw3f_threads"
end

@windows_only begin
const libfftw = "libfftw3"
const libfftwf = "libfftw3f"
end

## Direction of FFT

const FORWARD = int32(-1)
const BACKWARD = int32(1)

## FFTW Flags from fftw3.h

const MEASURE         = uint32(0)
const DESTROY_INPUT   = uint32(1 << 0)
const UNALIGNED       = uint32(1 << 1)
const CONSERVE_MEMORY = uint32(1 << 2)
const EXHAUSTIVE      = uint32(1 << 3)   # NO_EXHAUSTIVE is default
const PRESERVE_INPUT  = uint32(1 << 4)   # cancels DESTROY_INPUT
const PATIENT         = uint32(1 << 5)   # IMPATIENT is default
const ESTIMATE        = uint32(1 << 6)
const WISDOM_ONLY     = uint32(1 << 21)

## R2R transform kinds

const R2HC    = int32(0)
const HC2R    = int32(1)
const DHT     = int32(2)
const REDFT00 = int32(3)
const REDFT01 = int32(4)
const REDFT10 = int32(5)
const REDFT11 = int32(6)
const RODFT00 = int32(7)
const RODFT01 = int32(8)
const RODFT10 = int32(9)
const RODFT11 = int32(10)

# FFTW floating-point types:

typealias fftwNumber Union(Float64,Float32,Complex128,Complex64)
typealias fftwReal Union(Float64,Float32)
typealias fftwComplex Union(Complex128,Complex64)
typealias fftwDouble Union(Float64,Complex128)
typealias fftwSingle Union(Float32,Complex64)
typealias fftwTypeDouble Union(Type{Float64},Type{Complex128})
typealias fftwTypeSingle Union(Type{Float32},Type{Complex64})

## Julia wrappers around FFTW functions

# Wisdom

# Import and export wisdom to/from a single file for all precisions,
# which is more user-friendly than requiring the user to call a
# separate routine depending on the fp precision of the plans.  This
# requires a bit of trickness since we have to (a) use the libc file
# I/O routines with fftw_export_wisdom_to_file/import_wisdom_from_file
# (b) we need 256 bytes of space padding between the wisdoms to work
# around FFTW's internal file i/o buffering [see the BUFSZ constant in
# FFTW's api/import-wisdom-from-file.c file].

function export_wisdom(fname::String)
    f = ccall(:fopen, Ptr{Void}, (Ptr{Uint8},Ptr{Uint8}),
              bytestring(fname), bytestring("w"))
    systemerror("could not open wisdom file $fname for writing", f == C_NULL)
    ccall((:fftw_export_wisdom_to_file,libfftw), Void, (Ptr{Void},), f)
    ccall(:fputs, Int32, (Ptr{Uint8},Ptr{Void}), bytestring(" "^256), f)
    ccall((:fftwf_export_wisdom_to_file,libfftwf), Void, (Ptr{Void},), f)
    ccall(:fclose, Void, (Ptr{Void},), f)
end

function import_wisdom(fname::String)
    f = ccall(:fopen, Ptr{Void}, (Ptr{Uint8},Ptr{Uint8}),
              bytestring(fname), bytestring("r"))
    systemerror("could not open wisdom file $fname for reading", f == C_NULL)
    if ccall((:fftw_import_wisdom_from_file,libfftw),Int32,(Ptr{Void},),f)==0||
       ccall((:fftwf_import_wisdom_from_file,libfftwf),Int32,(Ptr{Void},),f)==0
        error("failed to import wisdom from $fname")
    end
    ccall(:fclose, Void, (Ptr{Void},), f)
end

function import_system_wisdom()
    if ccall((:fftw_import_system_wisdom,libfftw), Int32, ()) == 0 ||
       ccall((:fftwf_import_system_wisdom,libfftwf), Int32, ()) == 0
        error("failed to import system wisdom")
    end
end

function forget_wisdom()
    ccall((:fftw_forget_wisdom,libfftw), Void, ())
    ccall((:fftwf_forget_wisdom,libfftwf), Void, ())
end

# Threads

let initialized = false
    global set_num_threads
    function set_num_threads(nthreads::Integer)
        if !initialized
            # must re-initialize FFTW if any FFTW routines have been called
            ccall((:fftw_cleanup,libfftw), Void, ())
            ccall((:fftwf_cleanup,libfftwf), Void, ())
            stat = ccall((:fftw_init_threads,libfftw), Int32, ())
            statf = ccall((:fftwf_init_threads,libfftwf), Int32, ())
            if stat == 0 || statf == 0
                error("could not initialize FFTW threads")
            end
            initialized = true
        end
        ccall((:fftw_plan_with_nthreads,libfftw), Void, (Int32,), nthreads)
        ccall((:fftwf_plan_with_nthreads,libfftwf), Void, (Int32,), nthreads)
    end
end

# Execute

execute(precision::fftwTypeDouble, plan) =
    ccall((:fftw_execute,libfftw), Void, (Ptr{Void},), plan)

execute(precision::fftwTypeSingle, plan) =
    ccall((:fftwf_execute,libfftwf), Void, (Ptr{Void},), plan)

execute(plan, X::StridedArray{Complex128}, Y::StridedArray{Complex128}) =
    ccall((:fftw_execute_dft,libfftw), Void,
          (Ptr{Void},Ptr{Complex128},Ptr{Complex128}), plan, X, Y)

execute(plan, X::StridedArray{Complex64}, Y::StridedArray{Complex64}) =
    ccall((:fftwf_execute_dft,libfftwf), Void,
          (Ptr{Void},Ptr{Complex64},Ptr{Complex64}), plan, X, Y)

execute(plan, X::StridedArray{Float64}, Y::StridedArray{Complex128}) =
    ccall((:fftw_execute_dft_r2c,libfftw), Void,
          (Ptr{Void},Ptr{Float64},Ptr{Complex128}), plan, X, Y)

execute(plan, X::StridedArray{Float32}, Y::StridedArray{Complex64}) =
    ccall((:fftwf_execute_dft_r2c,libfftwf), Void,
          (Ptr{Void},Ptr{Float32},Ptr{Complex64}), plan, X, Y)

execute(plan, X::StridedArray{Complex128}, Y::StridedArray{Float64}) =
    ccall((:fftw_execute_dft_c2r,libfftw), Void,
          (Ptr{Void},Ptr{Complex128},Ptr{Float64}), plan, X, Y)

execute(plan, X::StridedArray{Complex64}, Y::StridedArray{Float32}) =
    ccall((:fftwf_execute_dft_c2r,libfftwf), Void,
          (Ptr{Void},Ptr{Complex64},Ptr{Float32}), plan, X, Y)

execute_r2r{T<:fftwDouble}(plan, X::StridedArray{T}, Y::StridedArray{T}) =
    ccall((:fftw_execute_r2r,libfftw), Void,
          (Ptr{Void},Ptr{T},Ptr{T}), plan, X, Y)

execute_r2r{T<:fftwSingle}(plan, X::StridedArray{T}, Y::StridedArray{T}) =
    ccall((:fftwf_execute_r2r,libfftwf), Void,
          (Ptr{Void},Ptr{T},Ptr{T}), plan, X, Y)

execute{T<:fftwReal}(plan, X::StridedArray{T}, Y::StridedArray{T}) =
    execute_r2r(plan, X, Y)

# Destroy plan

destroy_plan(precision::fftwTypeDouble, plan) =
    ccall((:fftw_destroy_plan,libfftw), Void, (Ptr{Void},), plan)

destroy_plan(precision::fftwTypeSingle, plan) =
    ccall((:fftwf_destroy_plan,libfftwf), Void, (Ptr{Void},), plan)

# Planner timelimits

const NO_TIMELIMIT = -1.0 # from fftw3.h

set_timelimit(precision::fftwTypeDouble,seconds) =
    ccall((:fftw_set_timelimit,libfftw), Void, (Float64,), seconds)

set_timelimit(precision::fftwTypeSingle,seconds) =
    ccall((:fftwf_set_timelimit,libfftwf), Void, (Float64,), seconds)

# Array alignment mod 16:
#   FFTW plans may depend on the alignment of the array mod 16 bytes,
#   i.e. the address mod 16 of the first element of the array, in order
#   to exploit SIMD operations.  Julia arrays are, by default, aligned
#   to 16-byte boundaries (address mod 16 == 0), but this may not be
#   true for data imported from external C code, or for SubArrays.
#   Use the undocumented routine fftw_alignment_of to determine the
#   alignment of a given pointer modulo whatever FFTW needs.

alignment_of{T<:fftwDouble}(A::StridedArray{T}) =
   ccall((:fftw_alignment_of, libfftw), Int32, (Ptr{T},), A)

alignment_of{T<:fftwSingle}(A::StridedArray{T}) =
   ccall((:fftwf_alignment_of, libfftwf), Int32, (Ptr{T},), A)

# Plan (low-level)

# low-level storage of the FFTW plan, along with the information
# needed to determine whether it is applicable.   We need to put
# this into a type to support a finalizer on the fftw_plan.
type Plan{T<:fftwNumber}
    plan::Ptr{Void}
    sz::Dims # size of array on which plan operates (Int tuple)
    istride::Dims # strides of input
    ialign::Int32 # alignment mod 16 of input
    function Plan(plan::Ptr{Void}, sz::Dims, istride::Dims, ialign::Int32)
        p = new(plan,sz,istride,ialign)
        finalizer(p, p -> destroy_plan(T, p.plan))
        return p
    end
end
Plan{T<:fftwNumber}(plan::Ptr{Void}, X::StridedArray{T}) = Plan{T}(plan, size(X), strides(X), alignment_of(X))

# Check whether a Plan is applicable to a given input array, and
# throw an informative error if not:
function assert_applicable{T<:fftwNumber}(p::Plan{T}, X::StridedArray{T})
    if size(X) != p.sz
        throw(ArgumentError("FFTW plan applied to wrong-size array"))
    elseif strides(X) != p.istride
        throw(ArgumentError("FFTW plan applied to wrong-strides array"))
    elseif alignment_of(X) != p.ialign
        throw(ArgumentError("FFTW plan applied to array with wrong memory alignment"))
    end
end

# NOTE ON GC (garbage collection):
#    The Plan has a finalizer so that gc will destroy the plan,
#    which is necessary for gc to work with plan_fft.  However,
#    even when we are creating a single-use Plan [e.g. for fftn(x)],
#    we intentionally do NOT call destroy_plan explicitly, and instead
#    wait for garbage collection.  The reason is that, in the common
#    case where the user calls fft(x) a second time soon afterwards,
#    if destroy_plan has not yet been called then FFTW will internally
#    re-use the table of trigonometric constants from the first plan.

# Compute dims and howmany for FFTW guru planner
function dims_howmany(X::StridedArray, Y::StridedArray,
                      sz::Array{Int,1}, region)
    reg = [region...]
    if length(unique(reg)) < length(reg)
        throw(ArgumentError("each dimension can be transformed at most once"))
    end
    ist = [strides(X)...]
    ost = [strides(Y)...]
    dims = [sz[reg] ist[reg] ost[reg]]'
    oreg = [1:ndims(X)]
    oreg[reg] = 0
    oreg = filter(d -> d > 0, oreg)
    howmany = [sz[oreg] ist[oreg] ost[oreg]]'
    return (dims, howmany)
end

# check & convert kinds into int32 array with same length as region
function fix_kinds(region, kinds)
    if length(kinds) != length(region)
        if length(kinds) > length(region)
            throw(ArgumentError("too many transform kinds"))
        else
            if length(kinds) == 0
                throw(ArgumentError("must supply a transform kind"))
            end
            k = Array(Int32, length(region))
            k[1:length(kinds)] = [kinds...]
            k[length(kinds)+1:end] = kinds[end]
            kinds = k
        end
    else
        kinds = int32([kinds...])
    end
    for i = 1:length(kinds)
        if kinds[i] < 0 || kinds[i] > 10
            throw(ArgumentError("invalid transform kind"))
        end
    end
    return kinds
end

# low-level Plan creation (for internal use in FFTW module)

for (Tr,Tc,fftw,lib) in ((:Float64,:Complex128,"fftw",libfftw),
                         (:Float32,:Complex64,"fftwf",libfftwf))

    @eval function Plan(X::StridedArray{$Tc}, Y::StridedArray{$Tc},
                        region, direction::Integer,
                        flags::Unsigned, timelimit::Real)
        set_timelimit($Tr, timelimit)
        dims, howmany = dims_howmany(X, Y, [size(X)...], region)
        plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib),
                     Ptr{Void},
                     (Int32, Ptr{Int}, Int32, Ptr{Int},
                      Ptr{$Tc}, Ptr{$Tc}, Int32, Uint32),
                     size(dims,2), dims, size(howmany,2), howmany,
                     X, Y, direction, flags)
        set_timelimit($Tr, NO_TIMELIMIT)
        if plan == C_NULL
            error("FFTW could not create plan") # shouldn't normally happen
        end
        return Plan(plan, X)
    end

    @eval function Plan(X::StridedArray{$Tr}, Y::StridedArray{$Tc},
                        region, flags::Unsigned, timelimit::Real)
        region = circshift([region...],-1) # FFTW halves last dim
        set_timelimit($Tr, timelimit)
        dims, howmany = dims_howmany(X, Y, [size(X)...], region)
        plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib),
                     Ptr{Void},
                     (Int32, Ptr{Int}, Int32, Ptr{Int},
                      Ptr{$Tr}, Ptr{$Tc}, Uint32),
                     size(dims,2), dims, size(howmany,2), howmany,
                     X, Y, flags)
        set_timelimit($Tr, NO_TIMELIMIT)
        if plan == C_NULL
            error("FFTW could not create plan") # shouldn't normally happen
        end
        return Plan(plan, X)
    end

    @eval function Plan(X::StridedArray{$Tc}, Y::StridedArray{$Tr},
                        region, flags::Unsigned, timelimit::Real)
        region = circshift([region...],-1) # FFTW halves last dim
        set_timelimit($Tr, timelimit)
        dims, howmany = dims_howmany(X, Y, [size(Y)...], region)
        plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib),
                     Ptr{Void},
                     (Int32, Ptr{Int}, Int32, Ptr{Int},
                      Ptr{$Tc}, Ptr{$Tr}, Uint32),
                     size(dims,2), dims, size(howmany,2), howmany,
                     X, Y, flags)
        set_timelimit($Tr, NO_TIMELIMIT)
        if plan == C_NULL
            error("FFTW could not create plan") # shouldn't normally happen
        end
        return Plan(plan, X)
    end

    @eval function Plan_r2r(X::StridedArray{$Tr}, Y::StridedArray{$Tr},
                            region, kinds,
                            flags::Unsigned, timelimit::Real)
        kinds = fix_kinds(region, kinds)
        set_timelimit($Tr, timelimit)
        dims, howmany = dims_howmany(X, Y, [size(X)...], region)
        plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
                     Ptr{Void},
                     (Int32, Ptr{Int}, Int32, Ptr{Int},
                      Ptr{$Tr}, Ptr{$Tr}, Ptr{Int32}, Uint32),
                     size(dims,2), dims, size(howmany,2), howmany,
                     X, Y, kinds, flags)
        set_timelimit($Tr, NO_TIMELIMIT)
        if plan == C_NULL
            error("FFTW could not create plan") # shouldn't normally happen
        end
        return Plan(plan, X)
    end

    # support r2r transforms of complex = transforms of real & imag parts
    @eval function Plan_r2r(X::StridedArray{$Tc}, Y::StridedArray{$Tc},
                            region, kinds,
                            flags::Unsigned, timelimit::Real)
        kinds = fix_kinds(region, kinds)
        set_timelimit($Tr, timelimit)
        dims, howmany = dims_howmany(X, Y, [size(X)...], region)
        dims[2:3, 1:size(dims,2)] *= 2
        howmany[2:3, 1:size(howmany,2)] *= 2
        howmany = [howmany [2,1,1]] # append loop over real/imag parts
        plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib),
                     Ptr{Void},
                     (Int32, Ptr{Int}, Int32, Ptr{Int},
                      Ptr{$Tc}, Ptr{$Tc}, Ptr{Int32}, Uint32),
                     size(dims,2), dims, size(howmany,2), howmany,
                     X, Y, kinds, flags)
        set_timelimit($Tr, NO_TIMELIMIT)
        if plan == C_NULL
            error("FFTW could not create plan") # shouldn't normally happen
        end
        return Plan(plan, X)
    end

end

# Convert arrays of numeric types to FFTW-supported packed complex-float types
# (FIXME: is there a way to use the Julia promotion rules more cleverly here?)
complexfloat{T<:fftwComplex}(X::StridedArray{T}) = X
complexfloat{T<:fftwReal}(X::StridedArray{T}) = complex(X)
complexfloat{T<:Real}(X::StridedArray{T}) = complex128(X)
complexfloat{T<:Complex}(X::StridedArray{T}) = complex128(X)

# In the Julia interface, a "plan" is just a function that executes
# an efficient FFT of fixed size/strides/alignment.  For each FFT function
# (fft, bfft, ifft, rfft, ...), we have at least two interfaces:
#
#   fft(x [, region]) - FFT of x, creating and destroying a plan,
#                       optionally acting only on a subset of the dimensions
#   p = plan_fft(x, [, region [, flags [, timelimit]]])
#       -- returns a function p(x) that performs efficient FFTs
#          of a given size (on given dimensions) with variants
#          to specify the FFTW planner flags (default is ESTIMATE) and
#          timelimit (default: NO_TIMELIMIT).
#
# along with in-place variants fft! and plan_fft! if feasible.

for (f,direction) in ((:fft,:FORWARD), (:bfft,:BACKWARD))
    f! = symbol(string(f,"!"))
    plan_f = symbol(string("plan_",f))
    plan_f! = symbol(string("plan_",f,"!"))
    @eval begin
        function $f{T<:fftwComplex}(X::StridedArray{T}, region)
            Y = similar(X, T)
            p = Plan(X, Y, region, $direction, ESTIMATE, NO_TIMELIMIT)
            execute(T, p.plan)
            # do not destroy_plan ... see above note on gc
            return Y
        end

        # in-place version
        function $f!{T<:fftwComplex}(X::StridedArray{T},region)
            p = Plan(X, X, region, $direction, ESTIMATE, NO_TIMELIMIT)
            execute(T, p.plan)
            # do not destroy_plan ... see above note on gc
            return X
        end

        function $f{T<:Number}(X::StridedArray{T}, region)
            Y = complexfloat(X) # in-place transform
            return $f!(Y, region)
        end

        function $plan_f{T<:fftwComplex}(X::StridedArray{T},
                                                         region,
                                                         flags::Unsigned,
                                                         tlim::Real)
            Y = similar(X, T)
            p = Plan(X, Y, region, $direction, flags, tlim)
            return Z::StridedArray{T} -> begin
                assert_applicable(p, Z)
                W = similar(Z, T)
                execute(p.plan, Z, W)
                return W
            end
        end

        function $plan_f{T<:Number}(X::StridedArray{T}, region,
                                    flags::Unsigned, tlim::Real)
            Y = complexfloat(X) # in-place transform
            p = Plan(Y, Y, region, $direction, flags, tlim)
            return Z::StridedArray{T} -> begin
                W = complexfloat(Z) # in-place transform
                assert_applicable(p, W)
                execute(p.plan, W, W)
                return W
            end
        end

        function $plan_f!{T<:fftwComplex}(X::StridedArray{T},
                                                          region,
                                                          flags::Unsigned,
                                                          tlim::Real)
            p = Plan(X, X, region, $direction, flags, tlim)
            return Z::StridedArray{T} -> begin
                assert_applicable(p, Z)
                execute(p.plan, Z, Z)
                return Z
            end
        end

        $f(X::StridedArray) = $f(X, 1:ndims(X))
        $f!(X::StridedArray) = $f!(X, 1:ndims(X))
    end
    for pf in (plan_f, plan_f!)
        @eval begin
            $pf{T<:Number}(X::StridedArray{T}, region, flags::Unsigned) =
                $pf(X, region, flags, NO_TIMELIMIT)
            $pf{T<:Number}(X::StridedArray{T}, region) =
                $pf(X, region, ESTIMATE, NO_TIMELIMIT)
            $pf{T<:Number}(X::StridedArray{T}) =
                $pf(X, 1:ndims(X), ESTIMATE, NO_TIMELIMIT)
        end
    end
end

# Normalization for ifft

normalization(X::StridedArray, region) = 1 / prod([size(X)...][[region...]])
normalization(X::StridedArray) = 1 / length(X)

# Normalized ifft inverse transforms:

for (f,fb) in ((:ifft,:bfft), (:ifft!,:bfft!))
    pf = symbol(string("plan_", f))
    pfb = symbol(string("plan_", fb))
    @eval begin
        $f(X, region) = scale!($fb(X, region), normalization(X, region))
        $f(X) = scale!($fb(X), normalization(X))

        function $pf(X, region, flags, tlim)
            nrm = normalization(X, region)
            p = $pfb(X, region, flags, tlim)
            return Z -> scale!(p(Z), nrm)
        end
        $pf(X, region, flags) = $pf(X, region, flags, NO_TIMELIMIT)
        $pf(X, region) = $pf(X, region, ESTIMATE, NO_TIMELIMIT)
        function $pf(X)
            nrm = normalization(X)
            p = $pfb(X)
            return Z -> scale!(p(Z), nrm)
        end
    end
end

# rfft/brfft and planned variants.  No in-place version for now.

for (Tr,Tc) in ((:Float32,:Complex64),(:Float64,:Complex128))
    @eval begin
        function rfft(X::StridedArray{$Tr}, region)
            d1 = region[1]
            osize = [size(X)...]
            osize[d1] = osize[d1]>>1 + 1
            Y = Array($Tc, osize...)
            p = Plan(X, Y, region, ESTIMATE, NO_TIMELIMIT)
            execute($Tr, p.plan)
            # do not destroy_plan ... see above note on gc
            return Y
        end

        function rfft{T<:Real}(X::StridedArray{T}, region)
            Xr = float(X)
            return rfft(Xr, region)
        end

        function plan_rfft(X::StridedArray{$Tr}, region,
                           flags::Unsigned, tlim::Real)
            d1 = region[1]
            osize = [size(X)...]
            osize[d1] = osize[d1]>>1 + 1
            Y = Array($Tc, osize...)
            p = Plan(X, Y, region, flags, tlim)
            return Z::StridedArray{$Tr} -> begin
                assert_applicable(p, Z)
                W = Array($Tc, osize...)
                execute(p.plan, Z, W)
                return W
            end
        end

        # FFTW currently doesn't support PRESERVE_INPUT for
        # multidimensional out-of-place c2r transforms, so
        # we have to handle 1d and >1d cases separately.  Ugh.

        function brfft(X::StridedArray{$Tc}, d::Integer, region::Integer)
            osize = [size(X)...]
            @assert osize[region] == d>>1 + 1
            osize[region] = d
            Y = Array($Tr, osize...)
            p = Plan(X, Y, region, ESTIMATE | PRESERVE_INPUT, NO_TIMELIMIT)
            execute($Tr, p.plan)
            # do not destroy_plan ... see above note on gc
            return Y
        end

        # variant that destroys input X
        function brfftd(X::StridedArray{$Tc}, d::Integer, region)
            d1 = region[1]
            osize = [size(X)...]
            @assert osize[d1] == d>>1 + 1
            osize[d1] = d
            Y = Array($Tr, osize...)
            p = Plan(X, Y, region, ESTIMATE, NO_TIMELIMIT)
            execute($Tr, p.plan)
            # do not destroy_plan ... see above note on gc
            return Y
        end

        function brfft(X::StridedArray{$Tc}, d::Integer, region)
            if length(region) == 1
                return brfft(X, d, convert(Int, region[1]))
            end
            X = copy(X) # TODO: work in-place instead?
            return brfftd(X, d, region)
        end

        function brfft{T<:Number}(X::StridedArray{T}, d::Integer, region)
            Xc = complexfloat(X)
            return brfftd(Xc, d, region)
        end

        function plan_brfft(X::StridedArray{$Tc}, d::Integer, region::Integer,
                            flags::Unsigned, tlim::Real)
            osize = [size(X)...]
            @assert osize[region] == d>>1 + 1
            osize[region] = d
            Y = Array($Tr, osize...)
            p = Plan(X, Y, region, flags | PRESERVE_INPUT, tlim)
            return Z::StridedArray{$Tc} -> begin
                assert_applicable(p, Z)
                W = Array($Tr, osize...)
                execute(p.plan, Z, W)
                return W
            end
        end

        function plan_brfft(X::StridedArray{$Tc}, d::Integer, region,
                            flags::Unsigned, tlim::Real)
            if length(region) == 1
                return plan_brfft(X, d, convert(Int, region[1]), flags, tlim)
            end
            d1 = region[1]
            osize = [size(X)...]
            @assert osize[d1] == d>>1 + 1
            osize[d1] = d
            Y = Array($Tr, osize...)
            X = copy(X)
            p = Plan(X, Y, region, flags, tlim)
            return Z::StridedArray{$Tc} -> begin
                assert_applicable(p, Z)
                Z = copy(Z)
                W = Array($Tr, osize...)
                execute(p.plan, Z, W)
                return W
            end
        end

        rfft(X::StridedArray) = rfft(X, 1:ndims(X))
        brfft(X::StridedArray,d) = brfft(X, d, 1:ndims(X))

        plan_rfft(X::StridedArray, region, flags) =
            plan_rfft(X, region, flags, NO_TIMELIMIT)
        plan_rfft(X::StridedArray, region) =
            plan_rfft(X, region, ESTIMATE, NO_TIMELIMIT)
        plan_rfft(X::StridedArray) =
            plan_rfft(X, 1:ndims(X), ESTIMATE, NO_TIMELIMIT)

        plan_brfft(X::StridedArray, d,  region, flags) =
            plan_brfft(X, d, region, flags, NO_TIMELIMIT)
        plan_brfft(X::StridedArray, d,  region) =
            plan_brfft(X, d, region, ESTIMATE, NO_TIMELIMIT)
        plan_brfft(X::StridedArray, d) =
            plan_brfft(X, d, 1:ndims(X), ESTIMATE, NO_TIMELIMIT)
    end
end

# Normalized rfft inverse transforms:

function irfft(X, d, region)
    Y = brfft(X, d, region)
    return scale!(Y, normalization(Y, region))
end

function irfft(X, d)
    Y = brfft(X, d)
    return scale!(Y, normalization(Y))
end

function plan_irfft(X::StridedArray, d::Integer, region, flags, tlim)
    p = plan_brfft(X, d, region, flags, tlim)
    d1 = region[1]
    osize = [size(X)...]
    osize[d1] = d
    nrm = 1 / prod(osize[[region...]])
    return Z -> scale!(p(Z), nrm)
end

plan_irfft(X, d, region, flags) = plan_irfft(X, d, region, flags, NO_TIMELIMIT)
plan_irfft(X, d, region) = plan_irfft(X, d, region, ESTIMATE, NO_TIMELIMIT)
plan_irfft(X, d) = plan_irfft(X, d, 1:ndims(X), ESTIMATE, NO_TIMELIMIT)

# A DFT is unambiguously defined as just the identity operation for scalars
fft(x::Number) = x
ifft(x::Number) = x
bfft(x::Number) = x
rfft(x::Real) = x
irfft(x::Number, d::Integer) = d == 1 ? real(x) : throw(BoundsError())
brfft(x::Number, d::Integer) = d == 1 ? real(x) : throw(BoundsError())
fft(x::Number, dims) = length(dims) == 0 || dims[1] == 1 ? x : throw(BoundsError())
ifft(x::Number, dims) = length(dims) == 0 || dims[1] == 1 ? x : throw(BoundsError())
bfft(x::Number, dims) = length(dims) == 0 || dims[1] == 1 ? x : throw(BoundsError())
fft(x::Number, dims) = length(dims) == 0 || dims[1] == 1 ? x : throw(BoundsError())
rfft(x::Real, dims) = dims[1] == 1 ? x : throw(BoundsError())
irfft(x::Number, d::Integer, dims) = d == 1 && dims[1] == 1 ? real(x) : throw(BoundsError())
brfft(x::Number, d::Integer, dims) = d == 1 && dims[1] == 1 ? real(x) : throw(BoundsError())

plan_fft(x::Number) = x -> x
plan_ifft(x::Number) = x -> x
plan_bfft(x::Number) = x -> x
plan_rfft(x::Real) = x -> x
plan_irfft(x::Number, d::Integer) = (irfft(x,d); x -> real(x))
plan_brfft(x::Number, d::Integer) = (brfft(x,d); x -> real(x))

plan_fft(x::Number, dims) = (fft(x,dims); x -> x)
plan_ifft(x::Number, dims) = (ifft(x,dims); x -> x)
plan_bfft(x::Number, dims) = (bfft(x,dims); x -> x)
plan_rfft(x::Real, dims) = (rfft(x,dims); x -> x)
plan_irfft(x::Number, d::Integer, dims) = (irfft(x,d,dims); x -> real(x))
plan_brfft(x::Number, d::Integer, dims) = (brfft(x,d,dims); x -> real(x))

plan_fft(x::Number, dims, flags) = plan_fft(x, dims)
plan_ifft(x::Number, dims, flags) = plan_ifft(x, dims)
plan_bfft(x::Number, dims, flags) = plan_bfft(x, dims)
plan_rfft(x::Real, dims, flags) = plan_rfft(x, dims)
plan_irfft(x::Number, d::Integer, dims, flags) = plan_irfft(x, d, dims)
plan_brfft(x::Number, d::Integer, dims, flags) = plan_brfft(x, d, dims)

plan_fft(x::Number, dims, flags, tlim) = plan_fft(x, dims)
plan_ifft(x::Number, dims, flags, tlim) = plan_ifft(x, dims)
plan_bfft(x::Number, dims, flags, tlim) = plan_bfft(x, dims)
plan_rfft(x::Real, dims, flags, tlim) = plan_rfft(x, dims)
plan_irfft(x::Number, d::Integer, dims, flags, tlim) = plan_irfft(x, d, dims)
plan_brfft(x::Number, d::Integer, dims, flags, tlim) = plan_brfft(x, d, dims)

# FFTW r2r transforms (low-level interface)

function r2r{T<:fftwNumber}(X::StridedArray{T}, kinds, region)
    Y = similar(X, T)
    p = Plan_r2r(X, Y, region, kinds, ESTIMATE, NO_TIMELIMIT)
    execute(T, p.plan)
    # do not destroy_plan ... see above note on gc
    return Y
end

function r2r!{T<:fftwNumber}(X::StridedArray{T}, kinds, region)
    p = Plan_r2r(X, X, region, kinds, ESTIMATE, NO_TIMELIMIT)
    execute(T, p.plan)
    # do not destroy_plan ... see above note on gc
    return X
end

r2r{T<:Real}(X::StridedArray{T}, kinds, region) = r2r!(float(X), kinds, region)
r2r{T<:Complex}(X::StridedArray{T}, kinds, region) = r2r!(complexfloat(X), kinds, region)

for f in (:r2r, :r2r!)
    @eval $f(X, kinds) = $f(X, kinds, 1:ndims(X))
end

function plan_r2r{T<:fftwNumber}(
    X::StridedArray{T}, kinds, region, flags::Unsigned, tlim::Real)
    Y = similar(X, T)
    p = Plan_r2r(X, Y, region, kinds, flags, tlim)
    return Z::StridedArray{T} -> begin
        assert_applicable(p, Z)
        W = similar(Z, T)
        execute_r2r(p.plan, Z, W)
        return W
    end
end

function plan_r2r{T<:Number}(X::StridedArray{T}, kinds, region,
                             flags::Unsigned, tlim::Real)
    Y = T<:Complex ? complexfloat(X) : float(X)
    p = Plan_r2r(Y, Y, region, kinds, flags, tlim)
    return Z::StridedArray{T} -> begin
        W = T<:Complex ? complexfloat(Z) : float(Z)
        execute_r2r(p.plan, W, W)
        return W
    end
end

function plan_r2r!{T<:fftwNumber}(
    X::StridedArray{T}, kinds,region,flags::Unsigned, tlim::Real)
    p = Plan_r2r(X, X, region, kinds, flags, tlim)
    return Z::StridedArray{T} -> begin
        assert_applicable(p, Z)
        execute_r2r(p.plan, Z, Z)
        return Z
    end
end

for f in (:plan_r2r, :plan_r2r!)
    @eval begin
        $f(X, kinds, region, flags) = $f(X, kinds, region, flags, NO_TIMELIMIT)
        $f(X, kinds, region) = $f(X, kinds, region, ESTIMATE, NO_TIMELIMIT)
        $f(X, kinds) = $f(X, kinds, 1:ndims(X), ESTIMATE, NO_TIMELIMIT)
    end
end

end # module

max_stars_repo_name: jakebolewski/julia

max_issues_repo_name: jakebolewski/julia

max_forks_repo_name: jakebolewski/julia


text:
module TestPartitionSingle
  using Base.Test
  using Brim

  A = [1 0; 0 1]
  S = [1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1]

  M = partition_single(A)

  # The modularity should be the diagonal matrix
  @test sum(abs(M.S .- S)) == 0

end


text:
###
#   Set operations
###

# Envolpe/hull. Keeps it as a IntervalUnion
env(x :: IntervalUnion) = IntervalUnion([hull(x.v)])
hull(x :: IntervalUnion) = IntervalUnion([hull(x.v)])

# Computes the complement of a IntervalUnion
function complement(x :: IntervalUnion)

    v = sort(x.v)
    vLo = left.(v)
    vHi = right.(v)

    vLo[1] == -∞ ? popfirst!(vLo) : pushfirst!(vHi, -∞)
    vHi[end] == ∞ ? pop!(vHi) : push!(vLo, ∞)

    complements = interval.(vHi,vLo)

    return intervalUnion(complements)
end

complement(x :: Interval) = complement(intervalUnion(x))

# bisect x at α
function bisect( x :: IntervalUnion, α = 0)

    v = deepcopy(x.v)

    αIn = α .∈ v
    these = findall(αIn .== 1)

    if isempty(these); return x ; end

    this = popat!(v, these[1])

    β = (α - this.lo) /(this.hi - this.lo)

    bs = bisect(this, β)

    new = IntervalUnion(v ∪ bs[1] ∪ bs[2])
    remove_empties!(new)
    sort!(new.v)
    return new

end

function intersect(x :: IntervalUnion, y :: IntervalUnion)
    intersects = [intersect(xv, yv) for xv in x.v, yv in y.v]
    if all(intersects .== ∅); return  ∅; end
    return intervalUnion(intersects[:])
end

function intersect(x :: IntervalUnion, y :: Interval)
    intersects = [intersect(xv, y) for xv in x.v]
    if all(intersects .== ∅); return  ∅; end
    return intervalUnion(intersects[:])
end

function intersect(x :: Interval, y :: IntervalUnion)
    intersects = [intersect(x, yv) for yv in y.v]
    if all(intersects .== ∅); return  ∅; end
    return intervalUnion(intersects[:])
end

function setdiff(x :: IntervalUnion, y :: IntervalUnion)
    yc = complement(y)
    return intersect(x, yc)
end

\(x :: IntervalUnion, y :: IntervalUnion) = setdiff(x, y)
\(x :: Interval, y :: IntervalUnion) = setdiff(intervalUnion(x), y)
\(x :: IntervalUnion, y :: Interval) = setdiff(x, intervalUnion(y))

function ⊂(x :: IntervalUnion, y :: IntervalUnion)
    issubs = [xv .⊂ y.v for xv in x.v]
    return all(any.(issubs))
end

function ⊂(x :: Interval, y :: IntervalUnion)
    if length(y.v) == 1; return x ⊂ y; end
    issubs = x .⊆ y.v
    return any(issubs)
end

function ⊂( x::IntervalUnion, y :: Interval)
    issubs = x.v .⊂ y
    return all(issubs)
end

function ⊆(x :: IntervalUnion, y :: IntervalUnion)
    issubs = [xv .⊆ y.v for xv in x.v]
    return all(any.(issubs))
end

function ⊆(x :: Interval, y :: IntervalUnion)
    issubs = x .⊆ y.v
    return any(issubs)
end

function ⊆( x::IntervalUnion, y :: Interval)
    issubs = x.v .⊆ y
    return all(issubs)
end

function in(x :: Real, y :: IntervalUnion)
    for i = 1:length(y.v)
        if x ∈ y.v[i]; return true; end
    end
    return false
end

function ==(x :: IntervalUnion, y ::IntervalUnion)

    xv = sort(x.v);
    yv = sort(y.v);

    return all(xv .== yv)

end

function ==(x :: IntervalUnion, y :: Interval)
    if length(x.v) == 1
        return x.v[1] == y
    end
    return false
end

==(x :: Interval, y :: IntervalUnion) = y == x

isempty(x::IntervalUnion) = isempty(x.v)


text:
## Qt implementations

## use PySide here
QtGui = PyCall.pyimport("PySide.QtGui")
QStyle = QtGui["QStyle"]

## Icons
function get_icon(::MIME"application/x-qt", o::StockIcon)
    if isa(o.nm, Nothing)
        Qt.QIcon()
    else
        file = Pkg.dir("JGUI", "icons", string(o.theme), string(o.nm) * ".png")
        Qt.QIcon(file)
    end
end
function get_icon(::MIME"application/x-qt", o::FileIcon)
    Qt.QIcon(o.file)
end

## Widget methods
getEnabled(::MIME"application/x-qt", o::Widget) = o[:widget][:enabled]
setEnabled(::MIME"application/x-qt", o::Widget, value::Bool) = o[:widget][:setEnabled](true)

getVisible(::MIME"application/x-qt", o::Widget) =  o[:widget][:visible]
setVisible(::MIME"application/x-qt", o::Widget, value::Bool) = o[:widget][:setVisible](value)

function getSize(::MIME"application/x-qt", o::Widget)
    sz = o[:widget][:size]()
    [sz[:width](), sz[:height]()]
end
setSize(::MIME"application/x-qt", o::Widget, value)  =  o[:widget][:resize](value[1], value[2])

getFocus(::MIME"application/x-qt", o::Widget) = o[:widget][:focus]
setFocus(::MIME"application/x-qt", o::Widget, value::Bool) =  value && o[:widget][:setFocus]()

## Does not preserve types! (1,"one") -> [1, "one"]
getContext(::MIME"application/x-qt", o::Widget) = o[:widget][:attrs][:context]
function setContext(::MIME"application/x-qt", o::Widget, ctx)
     o.attrs[:context] = ctx
end
## this is called when a custom context menu is requested. Use pt to add informationt to
## a widget's context
update_context(::MIME"application/x-qt", o::Widget, pt) = nothing

getWidget(::MIME"application/x-qt", o::Widget) = o.o
getBlock(::MIME"application/x-qt", o::Widget) = o.block

function setSizepolicy(::MIME"application/x-qt", o::Widget, policies)
    o.attrs[:sizepolicy] = policies
    hpolicies = {:fixed => QtGui["QSizePolicy"]["Fixed"],
                :expand => QtGui["QSizePolicy"]["Expanding"],
                nothing => QtGui["QSizePolicy"]["Fixed"]
                }
    vpolicies = {:fixed => QtGui["QSizePolicy"]["Fixed"],
                :expand => QtGui["QSizePolicy"]["Expanding"],
                nothing => QtGui["QSizePolicy"]["Fixed"]
                }
    sizepolicy = [hpolicies[policies[1]], vpolicies[policies[2]]]
    o[:block][:setSizePolicy](sizepolicy...)
end

function get_alignment(o::Widget)
    halign = {:left => "AlignLeft",
              :center=> "AlignHCenter",
              :right => "AlignRight",
              :justify => "AlignJustify",
              nothing =>  "AlignHCenter"
              }
    valign = {:top => "AlignTop",
              :center => "AlignVCenter",
              :bottom => "AlignBottom",
              nothing => "AlignVCenter"
              }

    req_align = o[:alignment]
    if req_align == (nothing, nothing)
        return 0
    else
        align = [halign[req_align[1]], valign[req_align[2]]]
        qt_enum(align)
    end
end
## Containers

## get Qt layout
getLayout(::MIME"application/x-qt", widget::Container) = widget[:widget][:layout]()
getLayout(widget::Container) = getLayout(widget.toolkit, widget)

## Window
function window(::MIME"application/x-qt"; kwargs...)
    widget = Qt.QMainWindow()
    (widget, widget)
end

### window methods
function PySide.raise(::MIME"application/x-qt", o::Window)
    o[:widget][:show]()
    convert(Function, o[:widget][:raise])()
end
lower(::MIME"application/x-qt", o::Window) = o[:widget][:lower]()
destroy_window(::MIME"application/x-qt", o::Window) = o[:widget][:destroy](true)

## window properties
getTitle(::MIME"application/x-qt", o::Window) = o[:widget][:windowTitle]()
setTitle(::MIME"application/x-qt", o::Window, value::String) = o[:widget][:setWindowTitle](value)
getPosition(::MIME"application/x-qt", o::Window) = [o[:widget][:x](), o[:widget][:y]()]
setPosition(::MIME"application/x-qt", o::Window, value::Vector{Int}) = o[:widget][:move](value[1], value[2])

## XXX -- need to do these!
function getModal(::MIME"application/x-tcltk", o::Window)
    val = tcl("grab", "status", o.o)
    val == "" ? false : true
end
function setModal(::MIME"application/x-tcltk", o::Window, value::Bool)
    if value
        function callback(path)
            tcl("grab", "release", o.o)
            ## Insert method here ...
            destroy(o)
        end
        Tk.wm(o.o, "protocol", "WM_DELETE_WINDOW", callback)
#        tcl("tkwait", "window", o.o)
        ## This make Tk window modal, but not console...
        tcl("grab", "set", "-global", o.o)
    else
       tcl("grab", "release", o.o)
    end
end

function set_child(::MIME"application/x-qt", parent::Window, child::Widget)
    parent[:widget][:setCentralWidget](child.block)
end

## for BinContainer, only one child we pack and expand...
function set_child(::MIME"application/x-qt", parent::BinContainer, child::Widget)
    lyt = Qt.QHBoxLayout(parent[:widget])
    lyt[:addWidget](child.block)
    lyt[:setStretch](2,2)
    parent[:widget][:setLayout](lyt)
end

## Label frame
function labelframe(::MIME"application/x-qt", parent::BinContainer, label::String; alignment::Union(Nothing, Symbol)=nothing)
    widget = Qt.QFrame(parent[:widget])
    widget[:setFrameStyle](widget[:Sunken])

    if isa(alignment, Symbol)
        ## XXX how to set label
    end
    (widget, widget)
end

## Boxes
function boxcontainer(::MIME"application/x-qt", parent::Container, direction)
    widget = Qt.QFrame(parent[:widget])
    (widget, widget)
end

## set padx, pady for all the children
function setSpacing(::MIME"application/x-qt", parent::BoxContainer, px::Vector{Int})
    parent[:widget][:layout]()[:setSpacing](px[1]) # first one only
end

##
function setMargin(::MIME"application/x-qt", parent::BoxContainer, px::Vector{Int})
    parent[:widget][:setContentsMargin](px[1], px[2], px[1], px[2])
end

## stretch, strut, spacing
addspacing(::MIME"application/x-qt", parent::BoxContainer, val::Int) = parent[:widget][:layout]()[:addSpacing](val)
addsstrut(::MIME"application/x-qt", parent::BoxContainer, val::Int) = parent[:widget][:layout]()[:addStrut](val)
addstretch(::MIME"application/x-qt", parent::BoxContainer, val::Int) = parent[:widget][:layout]()[:addStretch](val)

function insert_child(::MIME"application/x-qt", parent::BoxContainer, index, child::Widget)

    direction = getProp(parent, :direction)
    lyt = parent[:layout]
    if isa(lyt, Nothing)
        lyt = (direction == :horizontal) ? Qt.QHBoxLayout(parent[:widget]) : Qt.QVBoxLayout(parent[:widget])
        parent[:widget][:setLayout](lyt)
    end

    stretch = child[:stretch]

    if direction == :horizontal
        if child[:sizepolicy][2] == nothing
            child[:sizepolicy] = (child[:sizepolicy][1], :expand)
        end
    else
        if child[:sizepolicy][1] == nothing
            child[:sizepolicy] = (:expand, child[:sizepolicy][1])
        end
    end

    alignment = get_alignment(child)

    lyt[:insertWidget](index - 1, child.block, stretch,  alignment)

end

function remove_child(::MIME"application/x-qt", parent::Container, child::Widget)
    child[:widget][:setVisible](false)
    parent[:widget][:layout]()[:removeWidget](child.block)
end

####
## make a grid
function grid(::MIME"application/x-qt", parent::Container)
    widget = Qt.QFrame(parent[:widget])
    lyt = Qt.QGridLayout(widget)
    widget[:setLayout](lyt)
    (widget, widget)
end

## size of grid
function grid_size(::MIME"application/x-qt", widget::GridContainer)
    lty = widget[:widget][:layout]()
    [lty[:rowCount](), lty[:columnCount]()]
end

## grid spacing
function setSpacing(::MIME"application/x-qt", parent::GridContainer, px::Vector{Int})
    lyt = parent[:widget][:layout]()
    lyt[:setHorizontalSpacing][px[1]]
    lyt[:setVerticalSpacing][px[2]]
end

## Need to do something to configure rows and columns
## grid add child
function grid_add_child(::MIME"application/x-qt", parent::GridContainer, child::Widget, i, j)
    lyt = parent[:widget][:layout]()
    alignment = get_alignment(child)
    lyt[:addWidget](child.block, minimum(i), minimum(j), maximum(i) - minimum(i) + 1, maximum(j) - minimum(j) + 1, alignment)
end

function grid_get_child_at(::MIME"application/x-qt", parent::GridContainer, i::Int, j::Int)
    pyo = parent[:widget][:itemAtPosition](i, j)
    child = filter(kid -> kid.o == pyo, children(parent))
    length(child) == 1 ? child[1] : nothing
end

##XXza
column_minimum_width(::MIME"application/x-qt", object::GridContainer, j::Int, width::Int) = object[:layout][:setColumnMinimumWidth](j-1, width)

row_minimum_height(::MIME"application/x-qt", object::GridContainer, j::Int, height::Int) = object[:layout][:setRowMinimumdHeight](i-1, height)

column_stretch(::MIME"application/x-qt", object::GridContainer, j::Int, weight::Int) = object[:layout][:setColumnStretch](j-1, weight)

row_stretch(::MIME"application/x-qt", object::GridContainer, i::Int, weight::Int) = object[:layout][:setRowStretch](i-1, weight)
##################################################

function formlayout(::MIME"application/x-qt", parent::Container)
    widget = Qt.QWidget(parent[:widget])
    lyt = Qt.QFormLayout(widget)
    widget[:setLayout](lyt)

    (widget, widget)
end

## XX labels..
function formlayout_add_child(::MIME"application/x-qt", parent::FormLayout, child::Widget, label::Union(Nothing, String))
    parent[:layout][:addRow](label, child.block)
end

function setSpacing(::MIME"application/x-qt", object::FormLayout, px::Vector{Int})
    parent[:layout][:setHorizontalSpacing](px[1])
    parent[:layout][:setVerticalSpacing](px[2])
end

## Notebook
function notebook(::MIME"application/x-qt", parent::Container, model::Model)
    widget = Qt.QTabWidget(parent[:widget])

    connect(model, "valueChanged", value -> widget[:setCurrentIndex](value-1))
    qconnect(widget, :currentChanged) do value # XXX check value is index 0-based
        setValue(model, value + 1)
        false
    end

    (widget, widget)
end

## XXX icon, order?
function notebook_insert_child(::MIME"application/x-qt", parent::NoteBook, child::Widget, i::Int, label::String)
    i = i > length(parent) ? length(parent) : i-1
    parent[:widget][:insertTab](i, child.block,  label)
end

function notebook_remove_child(::MIME"application/x-qt", parent::NoteBook, child::Widget)
    ## no findfirst
    n = length(parent.children)
    index = filter(i -> parent.children[i] == child, 1:n)
    parent[:widget][:removeTab](index[1] - 1)
end

##################################################
## Widgets
function label(::MIME"application/x-qt", parent::Container, model::Model)
    widget = Qt.QLabel(string(getValue(model)), parent[:widget])
    connect(model, "valueChanged", widget, (widget, value) -> widget[:setText](string(value)))

    (widget, widget)
end

## separator
function separator(::MIME"application/x-qt", parent::Container; orientation::Symbol=:horizontal)
    widget = Qt.QFrame(parent[:widget])
    shape = widget[orientation == :horizontal ? :HLine : :VLine]
    widget[:setFrameShape](shape)
    widget[:setFrameShadow](widget[:Sunken])

    (widget, widget)
end

## Controls
function button(::MIME"application/x-qt", parent::Container, model::Model)
    widget = Qt.QPushButton(getValue(model), parent[:widget])
    connect(model, "valueChanged", value -> widget[:setText](value))
    qconnect(widget, :clicked, () -> notify(model, "clicked"))

    (widget, widget)
end

function setIcon(::MIME"application/x-qt", widget::Button, icon::Union(Nothing, Icon); kwargs...)
    if isa(icon, Nothing)
        widget[:widget][:setIcon](Qt.QIcon())
    else
        if isa(icon.theme, Nothing)
            icon.theme = widget[:icontheme]
        end
        widget[:widget][:setIcon](get_icon(widget.toolkit, icon))
    end
end

## Need to subclass to get desired signals

qnew_class("OLineEdit", "QtGui.QLineEdit") ## QtGui -- not just Qt.
function lineedit(::MIME"application/x-qt", parent::Container, model::Model)
    widget = qnew_class_instance("OLineEdit")
    widget[:setParent](parent[:widget])
    widget[:setText](string(getValue(model)))
    connect(model, "valueChanged", value -> widget[:setText](string(value)))

    ## SIgnals: keyrelease (keycode), activated (value), focusIn, focusOut, textChanged
    qconnect(widget, :returnPressed, () -> notify(model, "editingFinished", getValue(model)))
    qconnect(widget, :textChanged) do txt
        setValue(model, txt)
        true
    end

    qset_method(widget, :focusOutEvent) do e
        notify(model, "focusOut", getValue(model))
        notify(model, "editingFinished", getValue(model))
        return(false)
    end
    qset_method(widget, :focusInEvent) do e
        notify(model, "focusIn")
        return(false)
    end
    # qset_method(widget, :keyPressEvent) do e
    #     notify(model, "textChanged", e[:text]())
    #     true
    # end
    qset_method(widget, :mousePressEvent) do e
        notify(model, "clicked")
        return(false)
    end

    connect(model, "placeholderTextChanged") do txt
        widget[:setPlaceholderText](txt)
    end

    (widget, widget)
end

function setTypeahead(::MIME"application/x-qt", obj::LineEdit, items)
    completer = Qt.QCompleter(items, obj[:widget])
    obj[:widget][:setCompleter](completer)
    qconnect(completer, :activated, (value) -> setValue(obj, value))
end

qnew_class("OTextEdit", "QtGui.QTextEdit") ## QtGui -- not just Qt.
function textedit(::MIME"application/x-qt", parent::Container, model::Model)
    widget = qnew_class_instance("OTextEdit")
    widget[:setParent](parent[:widget])

    connect(model, "valueChanged", value -> widget[:setPlainText](value))
    qconnect(widget, :textChanged, () -> model.value = widget[:toPlainText]()) # XXX Where to put cursor (and how)

    qset_method(widget, :focusOutEvent) do e
        notify(model, "focusOut", getValue(model))
        notify(model, "editingFinished", getValue(model))
        return(false)
    end
    qset_method(widget, :focusInEvent) do e
        notify(model, "focusIn")
        return(false)
    end
    # qset_method(widget, :keyPressEvent) do e
    #     notify(model, "textChanged", e[:text]())
    #     true
    # end

    qset_method(widget, :mousePressEvent) do e
        notify(model, "clicked")
        return(false)
    end

    (widget, widget)
end

## checkbox
function checkbox(::MIME"application/x-qt", parent::Container, model::Model, label::Union(Nothing, String))
    widget = Qt.QCheckBox(parent[:widget])
    if !isa(label, Nothing)
        widget[:setText](string(label))
    end
    widget[:setChecked](model.value)

    connect(model, "valueChanged", value -> widget[:setChecked](value))
    qconnect(widget, :stateChanged) do state
        setValue(model, state == int(qt_enum("Checked")))
    end
    (widget, widget)
end
getLabel(::MIME"application/x-qt", o::CheckBox) = o[:widget][:text]
setLabel(::MIME"application/x-qt", o::CheckBox, value::String) = o[:widget][:setText](string(value))

## radiogroup
function radiogroup(::MIME"application/x-qt", parent::Container, model::VectorModel; orientation::Symbol=:horizontal)
    block = Qt.QGroupBox(parent[:widget])
    widget = Qt.QButtonGroup(parent[:widget])

    lyt = orientation == :horizontal ? Qt.QHBoxLayout(block) : Qt.QVBoxLayout(block)
    block[:setLayout](lyt)

    btns = map(model.items) do label
        btn = Qt.QRadioButton(parent[:widget])
        btn[:setText](label)
        widget[:addButton](btn)
        lyt[:addWidget](btn)
        btn
    end

    selected = findfirst(model.items, model.value)
    btns[selected][:setChecked](true)

    connect(model, "valueChanged") do value
        ## need to look up which button to set
        selected = findfirst(model.items, value)
        if selected == 0
            error("$value is not one of the labels")
        else
            btns[selected][:setChecked](true)
        end
    end
    qconnect(widget, :buttonClicked) do btn
        setValue(model, btn[:text]())
    end

    (widget, block)
end

## buttongroup
function buttongroup(::MIME"application/x-qt", parent::Container, model::VectorModel; exclusive::Bool=false)
    block = Qt.QGroupBox(parent[:widget])
    lyt = Qt.QHBoxLayout(block)
    block[:setLayout](lyt)

    widget = Qt.QButtonGroup(parent[:widget])
    widget[:setExclusive](exclusive)

    value = model.value
    if !isa(value, Vector) value = [value] end

    btns = map(model.items) do label
        btn = Qt.QPushButton(parent[:widget])
        btn[:setText](label)
        btn[:setCheckable](true)
        btn[:setChecked](label in value)
        widget[:addButton](btn)
        lyt[:addWidget](btn)
        btn
    end

    connect(model, "valueChanged") do value

        ## XXX need to look up which button to set
        ## value is a vector of String[] type
        if !isa(value, Vector) value = [value] end
        btns = widget[:buttons]()
        map(btns) do btn
            btn[:setChecked](btn[:text]() in  value)
        end
    end

    qconnect(widget, :buttonReleased) do btn
        checked = filter(b -> b[:isChecked](), widget[:buttons]())
        vals = String[b[:text]() for b in  checked]
        if length(checked) == 0
            setValue(model, String[])
        else
            setValue(model, vals)
        end
    end

    (widget, block)
end

##
function combobox(::MIME"application/x-qt", parent::Container, model::VectorModel; editable::Bool=false)
    widget = Qt.QComboBox(parent[:widget])
    qmodel = Qt.QStandardItemModel(widget)
    widget[:setModel](qmodel)

    set_index(index) = widget[:setCurrentIndex](index-1)
    set_value(value) =  if isa(value, Nothing)
        set_index(0)
    else
        i = findfirst(model.items, value) # need index
        set_index(i)
    end

    function set_items(items, old_items)
        for i in 1:length(items)
            qmodel[:setItem](i-1, Qt.QStandardItem(items[i]))
        end
        set_index(0)            # clear selection?
    end

    set_items(model.items, nothing)
    set_value(model.value)

    connect(model, "valueChanged", value -> set_value(value))
    connect(model, "itemsChanged", set_items)

    qconnect(widget, :currentIndexChanged) do index
        if index == -1
            setValue(model, nothing)
        else
            setValue(model, model.items[index + 1])
        end
        false
    end

    (widget, widget)
end

## slider
## model stores value, slider is in 1:n
function slider(::MIME"application/x-qt", parent::Container, model::VectorModel; orientation::Symbol=:horizontal)
    items = model.items
    initial = model.value
    n = length(items)
    orient = orientation == :horizontal ? qt_enum("Horizontal") : qt_enum("Vertical")

    widget = Qt.QSlider(parent[:widget])
    widget[:setOrientation](orient)

    widget[:setRange](1, n)
    widget[:setPageStep](1)

    connect(model, "valueChanged") do value ## value is in model.items
        ## have to find index from items
        i = indmin(abs(model.items - value))
        widget[:setValue](i)
    end

    ## value is index
    qconnect(widget, :valueChanged, value -> setValue(model, model.items[value]))

    (widget, widget)
end

## out model stores index
function slider2d(::MIME"application/x-qt", parent::Container, model::TwoDSliderModel)
    rectF(x,y,w,h) = PySide.QtCore[:QRectF](x,y,w,h)
    lineF(x1, y1, x2, y2) = PySide.QtCore[:QLineF](x1,y1,x2,y2)

    scene = Qt.QGraphicsScene()
    scene[:setSceneRect](0.0, 0.0, 100.0, 100.0)

    ## styles
    dash = Qt.QPen()
    dash[:setStyle](qt_enum("DashDotLine"))
    dash[:setWidth](2)
    dash[:setBrush](qt_enum("gray"))

    solid = Qt.QPen()
    solid[:setWidth](3)
    solid[:setBrush](Qt.QBrush(qt_enum("black"), qt_enum("SolidPattern")))

    hor = scene[:addLine](lineF(0, 50, 100, 50), dash)
    ver = scene[:addLine](lineF(50, 0, 50, 100), dash)

    pt = Qt.QGraphicsEllipseItem()
    r = 3
    pt[:setRect](rectF(50-r, 50-r, 2r, 2r))
    pt[:setPen](solid)
    pt[:setFlags](pt[:ItemIsMovable])

    scene[:addItem](pt)

    qconnect(scene, :changed) do l
        x = pt[:x](); y = pt[:y]()
        ## move lines
        hor[:setY](y)
        ver[:setX](x)

        ## constrain
        x > 50 && pt[:setX](50)
        x <= -50 && pt[:setX](-49)
        y > 50 && pt[:setY](50)
        y <= -50 && pt[:setY](-49)

        ## put into coordinates
        i = min(100, max(1, x + 50))
        j = min(100, max(1, -y + 50))

        ## update model, but use index..
        if model.value != [i,j]
            model.value = [i,j]
            notify(model, "valueChanged", getValue(model))
        end
    end

    connect(model, "valueChanged") do value
        x, y = model.value

        x = x - 50
        y = 50 - y

        pt[:setX](x)
        pt[:setY](y)
    end

    view = Qt.QGraphicsView(scene)
    view[:setHorizontalScrollBarPolicy](qt_enum("ScrollBarAlwaysOff"))
    view[:setVerticalScrollBarPolicy](qt_enum("ScrollBarAlwaysOff"))
    view[:setSceneRect](1.0, 1.0, 100.0, 100.0)
    ## adjust to remove scrollbar
    view[:setMaximumWidth](100)
    view[:setMinimumWidth](100)
    view[:setMaximumHeight](100)
    view[:setMinimumHeight](100)

    (scene, view)
end
getValue(::MIME"application/x-qt", widget::Slider2D) = getValue(widget.model)
setValue(::MIME"application/x-qt", widget::Slider2D, value) = setValue(widget.model, value)

## spinbox
function spinbox(::MIME"application/x-qt", parent::Container, model::ItemModel, rng::Union(Range,Range1,Ranges))
    widget = isa(rng, Range) ? Qt.QDoubleSpinBox(parent[:widget]) : Qt.QSpinBox(parent[:widget])

    n = length([rng])             # expensive

    widget[:setMinimum](first(rng))
    widget[:setMaximum](first(rng) + (length(rng)*n-1)* step(rng))
    widget[:setSingleStep]( step(rng))

    qconnect(widget, :valueChanged, (value) -> setValue(model, value))
    connect(model, "valueChanged", value -> widget[:setValue](value))

    (widget, widget)
end

# function setRange(::MIME"application/x-qt", obj::SpinBox, rng)
#     step = isa(rng, Range1) ? 1 : rng.step
#     widget[:setMinimum](rng.start)
#     widget[:setMaximum](rng.start + (rng.len-1) * step)
#     widget[:setSingleStep](step)
# end

## PyPlot graphics Figure
## https://github.com/matplotlib/matplotlib/blob/master/examples/user_interfaces/embedding_in_qt4.py
import PyPlot: Gcf, pltm

type PyPlotGraphic <: Widget
    o
    block
    id::String
    parent
    toolkit
    attrs
end

## For use with PyPlot graphics.
##
## Very hacky
##
## Arguments:
## * `parent::Container` parent container
##
## Properties
## * `:active` call `obj[:active] = true` to make current figure
##
function pyplotgraphic(parent::Container)
    ## width height in inches, or pixels?
    widget = Qt.QWidget(parent[:widget])
    id = randstring(10)
    pltm[:figure](id) # super hacky
    manager = Gcf[:get_active]()

    canvas = manager["canvas"]

    canvas[:parent]()[:setVisible](false)
    canvas[:setParent](nothing)
    canvas[:setParent](widget)
    #canvas[:setSizePolicy]( QtGui["QSizePolicy"]["Expanding"], QtGui["QSizePolicy"]["Expanding"])

    lyt = Qt.QHBoxLayout()
    widget[:setLayout](lyt)
    lyt[:addWidget](canvas)

    PyPlotGraphic(canvas, widget, id, parent, parent.toolkit, Dict())
end

## set this as active
setActive(o::PyPlotGraphic, value) = pltm[:figure](o.id)

## Views
## StoreProxyModel
qnew_class("StoreProxyModel", "QtCore.QAbstractTableModel")
function store_proxy_model(parent, store::Store; tpl=nothing)

    ncols = length(store.types)
    nms = repmat(["X"], ncols) .* map(string, 1:ncols)

    m = qnew_class_instance("StoreProxyModel")
    m[:setParent](parent)
    ## add functions
    m[:rowCount] = (index) -> length(store.items)
    m[:columnCount] = (index) -> ncols
    m[:headerData] = (section::Int, orient, role) -> begin

        if orient.o ==  qt_enum("Horizontal").o #  match pointers
            ## column, section is column
            role == convert(Int, qt_enum("DisplayRole")) ?  nms[section + 1] : nothing ##replace(nms[section + 1],"_", " ") : nothing
        else
             role == convert(Int, qt_enum("DisplayRole")) ?  string(section + 1) : nothing
        end
    end
    m[:data] = (index, role) -> begin
        record = store.items[1]

        row = index[:row]() + 1
        col = index[:column]() + 1
        ## http://qt-project.org/doc/qt-5.0/qtcore/qt.html#ItemDataRole-enum
        roles = ["DisplayRole"]
        for r in roles
            if role == int(qt_enum(r))
                item = store.items[row]
                return(string(item[col]))
            end
        end
        if role == int(qt_enum("TextAlignmentRole"))
            return(qt_enum("AlignLeft")) ## XXX adjust to variable type?
        end
        if row == 1 && role == int(qt_enum("DecorationRole"))
            return(nothing) # XXX icons...
        end
        ## More roles, but not now...: "EditRole",  "BackgroundRole", "ForegroundRole", "ToolTipRole", "WhatsThisRole"]
        return(nothing)
    end

   m[:insertRows] = (row, count, index) -> begin
       m[:beginInsertRows](index, row-1, row-1)
       m[:endInsertRows]()
       ## notify model? Was getting recursive call in that case.
       true
   end
   m[:removeRows] = (row, count, index) -> begin
       m[:beginRemoveRows](index, row-1, row - 1 + count - 1)
       m[:endRemoveRows]()
       true
   end

   ## connect model to store so that store changes propogate XXX
   connect(store.model, "rowInserted") do i
       m[:insertRows](i, 1, PySide.QtCore[:QModelIndex]())
   end

    connect(store.model, "rowRemoved") do i
        m[:removeRows](i, 1, PySide.QtCore[:QModelIndex]())
    end

    function rowUpdated(i::Int)
        topleft = m[:index](i-1,0)
        lowerright = m[:index](i, 0)
        m[:emit](PySide.QtCore[:SIGNAL]("dataChanged"))#(topleft, lowerright)
    end
    connect(store.model, "rowUpdated", rowUpdated)

   ## return model
   m
end

## storeview
function storeview(::MIME"application/x-qt", parent::Container, store::Store, model::ItemModel; tpl=nothing)
    ## Widget
    widget = Qt.QTableView(parent[:widget])
    proxy_model = store_proxy_model(widget, store, tpl=tpl)
    widget[:setModel](proxy_model)

    widget[:setSelectionBehavior](widget[:SelectRows])

    ## configure
    widget[:setAlternatingRowColors](true)
    widget[:horizontalHeader]()[:setStretchLastSection](true)

    ## connect model to view on index changed
    connect(model, "valueChanged") do rows
        map(rows) do row
            widget[:selectRow](row - 1)
        end
    end
    ## set up callbacks
    ## this uses a slot
    qconnect(widget[:selectionModel](), :selectionChanged) do selected, deselected
        indices = unique([idx[:row]() + 1 for idx in selected[:indexes]()])
        setValue(model, indices)
        notify(model, "selectionChanged", indices)
    end
    qconnect(widget, :clicked) do index
        notify(model, "clicked", index[:row]() + 1, index[:column]() + 1)
    end
    qconnect(widget, :doubleClicked) do index
        notify(model, "doubleClick", index[:row]() + 1, index[:column]() + 1)
    end
    qconnect(widget[:horizontalHeader](), :sectionClicked) do index
        notify(model, "headerClicked", index + 1)
    end

    (widget, widget)
end

function getSelectmode(::MIME"application/x-qt", s::ModelView)
    s[:widget][:selectionMode]() == s[:widget][:SingleSelection] ? :single : :multiple
end
## val is single, multiple
function setSelectmode(::MIME"application/x-qt", s::ModelView, val::Symbol)
    s[:widget][:setSelectionMode](val == :single ? s[:widget][:SingleSelection] : s[:widget][:ExtendedSelection])
end

## getWidths
function getWidths(::MIME"application/x-qt", s::ModelView)
    n = size(s.store)[2]
    [s[:widget][:columnWidth](i-1) for i in 1:n]
end

function setWidths(::MIME"application/x-qt", s::ModelView, widths::Vector{Int})
    for i in 1:length(widths)
        s[:widget][:setColumnWidth](i-1, widths[i])
    end
end

## heights
function getHeights(::MIME"application/x-qt", s::ModelView)
    n = size(s.store)[1]
    [s[:widget][:rowHeight](i-1) for i in 1:n]
end

function setHeights(::MIME"application/x-qt", s::ModelView, heights::Vector{Int})
    for i in 1:size(s.store)[1]
        s[:widget][:setRowHeight](i-1, heights[i])
    end
end

function getNames(::MIME"application/x-qt", s::StoreView)
    n = size(s.store)[2]
    model = s[:widget][:model]()
    [model[:headerData](i-1, PySide.qt_enum("Horizontal"), 0) for i in 1:n]
end

## XXX this doesn't work!
function setNames(::MIME"application/x-qt", s::StoreView, nms::Vector)
    @assert length(nms) == size(s.store)[2]
    model = s[:widget][:model]()
    [model[:setHeaderData](i-1, PySide.qt_enum("Horizontal"), nm, 0) for (i,nm) in enumerate(nms)]
    s[:widget][:setModel](model)
end

function getHeadervisible(::MIME"application/x-qt", s::StoreView)
    s[:widget][:horizontalHeader]()[:isVisible]()
end
function setHeadervisible(::MIME"application/x-qt", s::StoreView, val::Bool)
    s[:widget][:horizontalHeader]()[val ? :show : :hide]()
end

function getRownamesvisible(::MIME"application/x-qt", s::StoreView)
    s[:widget][:verticalHeader]()[:isVisible]()
end
function setRownamesvisible(::MIME"application/x-qt", s::StoreView, val::Bool)
  s[:widget][:verticalHeader]()[val ? :show : :hide]()
end

function setIcon(::MIME"application/x-qt", s::StoreView, i::Int, icon::Icon)
    ## XXX need to set decoration role for item in row i, column 1
end

function update_context(::MIME"application/x-qt", s::StoreView, pt)
    view = s[:widget]
    index = view[:indexAt](pt)
    ## want [i,j] here for row and column
    s[:context] = [index[:row]() + 1, index[:column]() + 1]
end

##################################################
##
## StoreProxyModel
## XXX This failed due to inability to write [:parent] method...
# qnew_class("TreeStoreProxyModel", "QtCore.QAbstractItemModel")
# function tree_store_proxy_model(parent, store::TreeStore; tpl=nothing)
#     if tpl == nothing
#         record = store.items[1]
#     else
#         record = tpl
#     end
#     nms = names(record)

#     m = qnew_class_instance("TreeStoreProxyModel")

#     ## helpers to translate qtmodel <--> treestore
#     function index_to_path(index)
#         ind = index
#         ## return node in tree store for index
#         ## get path by crawling back
#         path = Int[]
#         println("index_to_node, index:", ind)
#         while ind[:isValid]()
#             println((ind[:isValid](),  ind[:row]() ))
#             path = unshift!(path, ind[:row]() + 1)
#             println((path, ind))
#             ind = ind[:parent]()
#         end
#         println("====")
#         path
#     end
#     function index_to_node(index)
#         path = index_to_path(index)
#         if length(path) > 0
#             path_to_node(store, path) # in models.jl
#         else
#             nothing
#         end
#     end
#     function node_to_index(node)
#         ## return qtindex fro a give node
#         path = node_to_path(node) # in models.jl
#         path_to_index(path)
#     end
#     function path_to_index(path)
#         ## return index from 1-based path
#         index = Qt.QModelIndex()
#         println("path to index")
#         println((path, index))
#         while length(path) > 0
#             r = shift!(path)
#             index = m[:index](r + 1, 0, index)
#             println((path, index))
#         end
#         println("====")
#         index
#     end

#     m[:setParent](parent)
#     ## add functions
#     ## parent, rowcount, columncount, data

#     ## Returns the parent of the model item with the given index.
#     ## If the item has no parent, an invalid QModelIndex is returned.
#     m[:parent] = (index) -> begin
#         path = index_to_path(index)
#         if length(path) > 1
#             pop!(path)
#             path_to_index(path)
#         else
#             PySide.QtCore.QModelIndex()
#         end
#     end
#     m[:rowCount] = (index) -> begin
#         println("rowCount")
#         node = index_to_node(index)
#         if isa(node, Nothing)
#             return 0
#         else
#             parent = node.parent
#             length(parent.items)
#         end
#     end
#     m[:columnCount] = (index) -> length(names(record))
#     m[:headerData] = (section::Int, orient, role) -> begin
#         if orient.o ==  qt_enum("Horizontal").o #  match pointers
#             ## column, section is column
#             role == convert(Int, qt_enum("DisplayRole")) ?  nms[section + 1] : nothing
#         else
#              role == convert(Int, qt_enum("DisplayRole")) ?  string(section + 1) : nothing
#         end
#     end
#     m[:data] = (index, role) -> begin
#         println("data")
#         node = index_to_node(index)
#         if isa(node, Nothing)
#             return nothing
#         end

#         record = node.data
#         nms = names(record)

#         col = index[:column]() + 1
#         ## http://qt-project.org/doc/qt-5.0/qtcore/qt.html#ItemDataRole-enum
#         if role == int(qt_enum("DisplayRole"))
#             nm = nms[col]
#             return(string(node.(symbol(nm))))
#         end
#         other_roles = ["EditRole", "DecorationRole", "TextAlignmentRole", "BackgroundRole", "ForegroundRole", "ToolTipRole", "WhatsThisRole"]
#         for r in other_roles
#             if role == int(qt_enum(r))
#                 if haskey(store.attrs, r)
#                     ## store roles in dict keyed by hash(row, col)
#                     ## e.g., store.attrs[RoleName][hash(row, col)] = value
#                     ## XXX need to do icon via
#                     ## store.attrs["DecorationRole"] = Dict()
#                     ## store.attrs["DecorationRole"][hash(1, 3)] = Qt.QIcon("ok.gif") ...
#                     d = store.attrs[r]
#                     key = hash(index[row]() + 1, col)
#                     if haskey(d, key)
#                         return string(d[key])
#                     end
#                 end
#                 return nothing
#             end
#         end
#         if role == int(qt_enum("TextAlignmentRole"))
#             return(qt_enum("AlignLeft")) ## XXX adjust to variable type?
#         end
#         return(nothing)
#     end
#     m[:index] = (row, column, parent) -> begin
#         m[:createIndex](row, column)
#     end
#     m[:insertRows] = (row, count, index) -> begin
#        println("insert rows")
#        m[:beginInsertRows](index, row-1, row-1)
#        m[:endInsertRows]()
#        notify(store.model, "rowInserted", index_to_node(index), row)
#        true
#    end
#    m[:removeRows] = (row, count, index) -> begin
#        println("remove rows")
#        m[:beginRemoveRows](index, row-1, 1)
#        m[:endRemoveRows]()
#        notify(store.model, "rowRemoved", index_to_node(index), row)
#        true
#    end

#    ## connect model to store so that store changes propagate XXX
#    connect(store.model, "rowInserted") do parent_node, i
#        m[:insertRows](i, 1, node_to_index(parent_node))
#    end

#    connect(store.model, "rowRemoved") do parent_node, i
#        m[:removeRows](i, 1, node_to_index(parent_node))
#    end
#    connect(store.model, "rowUpdated") do parent_node, i
#        ## XXX
#        topleft = m[:index](i-1,0)
#        lowerright = m[:index](i, 0)
#        m[:emit](PySide.QtCore[:SIGNAL]("dataChanged"))(topleft, lowerright)
#    end

#    ## return model
#    m
# end

## Tree view
## tpl: a template for the type, otherwise from tr.children[1]
function treeview(::MIME"application/x-qt", parent::Container, store::TreeStore, model::ItemModel)
    widget = Qt.QTreeWidget(parent[:widget])

    ## set flags ...

    ## helper functions relating
    function path_to_item(path)
        ## return QTreeWidgetItem from path
        path = copy(path)
        length(path) == 0 && return nothing
        root = shift!(path)
        item = widget[:topLevelItem](root - 1)
        while length(path) > 0
            i = shift!(path)
            item = item[:child](i-1)
        end
        item
    end
    function node_to_item(node)
        path_to_item(node_to_path(node))
    end
    function item_to_path(item)
        path = Int[]
        parent = item[:parent]()
        while !isa(parent, Nothing)
            unshift!(path, parent[:indexOfChild](item) + 1)
            item = parent
            parent = parent[:parent]()
        end
        unshift!(path, widget[:indexOfTopLevelItem](item) + 1)
        path
    end

    ###########################
    ## connect model and widget
    function insertNode(parentnode, i, childnode)
        vals = [childnode.text]
        if !isa(childnode.data, Nothing)
            vals = append!(vals, node_to_values(childnode))
        end
        item = PySide.Qt.QTreeWidgetItem(vals)

        if isa(parentnode, Union(Nothing, TreeStore)) # TreeStore is root, TreeNode is node
            widget[:insertTopLevelItem](i-1, item)
        else
            parent_path = node_to_path(parentnode)
            parent_item = path_to_item(parent_path)
            parent_item[:insertChild](i-1, item)
        end
    end
    connect(store.model, "insertNode", insertNode)

    function removeNode(parentnode, i)
        if isa(parentnode, TreeStore)
            widget[:takeTopLevelItem](i-1)
        else
            item = node_to_item(parentnode)
            item[:takeChild](i-1)
        end
    end
    connect(store.model, "removeNode", removeNode)

    function updatedNode(node)
        item = node_to_item(node)
        nms = names(node.data)
        [item[:setText](i-1, to_string(node, d)) for (i,d) in enumerate(node.data)]
    end
    connect(store.model, "updatedNode", updatedNode)

    ## expand Node on view model, not store model
    function expandNode(node)
        item = node_to_item(node)
        item[:setExpanded](true)
    end
    connect(model, "expandNode", expandNode)

    function collapseNode(node)
        item = node_to_item(node)
        item[:setExpanded](false)
    end
    connect(model, "collapseNode", collapseNode)

    connect(model, "valueChanged") do value
        item = path_to_item(value)
        widget[:setCurrentItem](item)
    end

    ## iterate of tstore children to set things
    function add_children(parentnode)
        if length(parentnode.children) > 0
            for i in 1:length(parentnode.children)
                child = parentnode.children[i]
                insertNode(parentnode, i, child)
                add_children(child)
            end
        end
    end
    add_children(store)

    ## connect widget to model
    qconnect(widget, :itemSelectionChanged) do
        sel_item = widget[:selectedItems]()[1]
        path = item_to_path(sel_item)
        ## some how this totally fails, yet
        setValue(model, path)
    end
    qconnect(widget, :itemExpanded) do item
        path = item_to_path(item)
        notify(model, "nodeExpand", path)
    end
    qconnect(widget, :itemCollapsed) do item
        path = item_to_path(item)
        notify(model, "nodeCollapse", path)
    end
    qconnect(widget, :itemClicked) do item, column
        path = item_to_path(item)
        notify(model, "clicked", path, column + 1)
    end
    qconnect(widget, :itemDoubleClicked) do item, column
        path = item_to_path(item)
        notify(model, "doubleClicked", path, column + 1)
    end

    (widget, widget)
end

## Properties
function getNames(::MIME"application/x-qt", tv::TreeView)
    tv.attrs[:headerLabels]
end

function setNames{T<:String}(::MIME"application/x-qt", tv::TreeView, nms::Vector{T})
    tv.attrs[:headerLabels] = nms
    widget = tv[:widget]
    widget[:setHeaderLabels](["",nms...])
end

function getKeyname(::MIME"application/x-qt", tr::TreeView)
    tr.attrs[:keyHeaderLabel]
end
function setKeyname(::MIME"application/x-qt", tr::TreeView, nm::String)
    tr.attrs[:keyHeaderLabel] = nm
    widget = tr[:widget]
    widget[:setHeaderLabels]([nm, tr.attrs[:headerLabels]...])
end

## keywidth is first column
function getKeywidth(::MIME"application/x-qt", tr::TreeView)
    tr[:widget][:columnWidth](0)
end
function setKeywidth(::MIME"application/x-qt", tr::TreeView, width::Int)
    tr[:widget][:setColumnWidth](0, width)
end
function getWidths(::MIME"application/x-qt", tr::TreeView)
    "XXX"
end
function setWidths(::MIME"application/x-qt", tr::TreeView, widths::Vector{Int})
    "XXX"
end
function getHeights(::MIME"application/x-qt", tr::TreeView)
    "XXX"
end
function setHeights(::MIME"application/x-qt", tr::TreeView, heights::Vector{Int})
    "XXX"
end

function setIcon(::MIME"application/x-qt", s::TreeView, path::Vector{Int}, icon::Icon)
    widget = s[:widget]
    function path_to_item(path) # DRY!!
        ## return QTreeWidgetItem from path
        length(path) == 0 && return nothing
        root = shift!(path)
        item = widget[:topLevelItem](root - 1)
        while length(path) > 0
            i = shift!(path)
            item = item[:child](i-1)
        end
        item
    end
    item = path_to_item(path)
    if isa(icon.theme, Nothing)
        icon.theme = s[:icontheme]
    end
    widget[:widget][:setIcon]()
    item[:setIcon](get_icon(s.toolkit, icon))
end

## Images

## place to put a png image
function imageview(::MIME"application/x-qt", parent::Container)
    ## use a QLabel to display an image
    widget = block = Qt.QLabel(parent[:widget])
    (widget, block)
end

function setImage(::MIME"application/x-qt", o::ImageView, img::String)
    pixmap = Qt.QPixmap(img)
    o[:widget][:setPixmap](pixmap)
end

##################################################
##
## Dialogs

function dialog(::MIME"application/x-qt", parent::Widget, model;
                buttons::Vector{Symbol}=[:ok],
                default::Union(Symbol, Nothing)=:ok,
                title::String="")

    dlg = Qt.QDialog(parent[:widget])

    if !isa(title, Nothing)
        dlg[:setWindowTitle](title)
    end

    button_box = Qt.QDialogButtonBox(dlg)
    defined_btns = {
                    :ok      => button_box[:Ok],
                    :cancel  => button_box[:Cancel],
                    :close   => button_box[:Close],
                    :apply  => button_box[:Apply],
                    :reset   => button_box[:Reset],
                    :help    => button_box[:Help],
                    }

    button_box[:setStandardButtons](sum(map(k -> int(defined_btns[k]), buttons)))
    if !isa(default, Nothing)
        button = button_box[:button](defined_btns[default])
        button[:setDefault](true)
    end

    ## Layout button box
    lyt = Qt.QVBoxLayout(dlg)
    dlg[:setLayout](lyt)
    lyt[:addWidget](button_box)

    (dlg, dlg)
end

## properties
getTitle(::MIME"application/x-qt", parent::Dialog) = dlg[:widget][:windowTitle]()
setTitle(::MIME"application/x-qt", parent::Dialog, value::String) = dlg[:widget][:setWindowTitle](value)

## make deafult button a property?

function set_child(::MIME"application/x-qt", parent::Dialog, child::Widget)
    lyt = parent[:layout]
#    child[:sizepolicy] = (:expand, :expand)
    lyt[:insertWidget](0, child.block, 100)
end

## add here so that we have dlg object, not QDialog
function add_bindings(::MIME"application/x-qt", dlg::Dialog)
    n = dlg[:widget][:layout]()[:count]()
    button_box = dlg[:widget][:layout]()[:itemAt](n-1)[:widget]()
     ## need to connect buttons and close dialog
    f(x, s) = () -> begin
        dlg.state = s
        dlg[:widget][:done](1)
        if !isa(x, Nothing)
            notify(dlg.model, x)
        end
        s
    end
    qconnect(button_box, :accepted,      f("accepted", :accept))
    qconnect(button_box, :rejected,      f("rejected", :reject))
    qconnect(button_box, :helpRequested, f(nothing,    :help))
    qconnect(button_box, :clicked) do button
        notify(dlg.model, "finished", button) # need to see what button is
    end

end
function show_dialog(::MIME"application/x-qt", dlg::Dialog, value::Bool)
    if value
        dlg[:widget][:show]()
        convert(Function, dlg[:widget][:raise])()
    else
        dlg[:widget][:hide]()
    end
end

function setModal(::MIME"application/x-qt", dlg::Dialog, value::Bool)
    if value
        convert(Function, dlg[:widget][:exec])()
    end
end
function setModaless(::MIME"application/x-qt", dlg::Dialog, value::Bool)
    convert(Function, dlg[:widget][:open])()
end
function destroy(::MIME"application/x-qt", dlg::Dialog)
    convert(Function, dlg[:widget][:destroy])()
end

### special dialogs
##
## Returns file, directory, files or nothing
function filedialog(::MIME"application/x-qt",
                    parent::Widget;
                    mode::Symbol=:open, ## :open, :multiple, :directory, :save
                    message::String="",
                    title::String="",
                    filetypes::Union(Nothing, Vector{Tuple})=nothing)

    dlg = (isa(parent, Nothing) ? Qt.QFileDialog() : Qt.QFileDialog(parent[:widget]))
    ## title
    dlg[:setWindowTitle](title)

    ## mode
    file_mode = {:open => :ExistingFile,
                 :multiple => :ExistingFiles,
                 :directory => :Directory,
                 :save => :AnyFile
                 }
    dlg[:setFileMode](dlg[file_mode[mode]])

    ##  Filter
    ## do filetypes an array [(name, ext)]
    if !isa(filetypes, Nothing)
        push!(filetypes, ("All files", "*.*"))
        dlg[:setNameFilters](["$val ($descr)" for (val, descr) in filetypes])
    end

    dlg[:show]()
    convert(Function, dlg[:raise])()
    ret = convert(Function, dlg[:exec])()
    if ret != 0
        files = dlg[:selectedFiles]()
        length(files) == 0 && return(nothing)
        mode == :multiple ? files : files[1]
    else
        nothing
    end
end

function messagebox(::MIME"application/x-qt", parent::Widget, text::String; icon::Union(Nothing, String)=nothing,
                    title::Union(Nothing, String)=nothing,
                    informativeText::Union(Nothing, String)=nothing)

    msgbox = Qt.QMessageBox(parent[:widget])
    msgbox[:setText](text)

    if !isa(icon, Nothing)
        icons = {:question=>:Question,
                 :info =>   :Information,
                 :warning=> :Warning,
                 :critical=>:Critical}
        msgbox[:setIcon](msgbox[icons[icon]])
    end
    if !isa(title, Nothing)
        msgbox[:setWindowTitle](title)
    end
    if !isa(informativeText, Nothing)
        msgbox[:setInformativeText](informativeText)
    end
    if !isa(icon, Nothing)
        args["icon"] = icons[icon]
    end

    msgbox[:show]()
    convert(Function, msgbox[:raise])()
    convert(Function, msgbox[:exec])()
end

function confirmbox(::MIME"application/x-qt", parent::Widget, text::String; icon::Union(Nothing, Symbol)=nothing,
                    title::Union(Nothing, String)=nothing,
                    informativeText::Union(Nothing, String)=nothing) # detail

    msgbox = Qt.QMessageBox(parent[:widget])
    msgbox[:setText](text)

    if !isa(icon, Nothing)
        icons = {:question=>:Question,
                 :info =>   :Information,
                 :warning=> :Warning,
                 :critical=>:Critical}
        msgbox[:setIcon](msgbox[icons[icon]])
    end
    if !isa(title, Nothing)
        msgbox[:setWindowTitle](title)
    end
    if !isa(informativeText, Nothing)
        msgbox[:setInformativeText](informativeText)
    end
    if !isa(icon, Nothing)
        args["icon"] = icons[icon]
    end

    msgbox[:setStandardButtons](int(msgbox[:Ok]) + int(msgbox[:Cancel]))

    msgbox[:show]()
    convert(Function, msgbox[:raise])()
    ret = convert(Function, msgbox[:exec])()
    ret == int(msgbox[:Ok]) ? :accept : :reject
end

##################################################
## Menus

function action(::MIME"application/x-qt", parent)
    Qt.QAction(parent[:widget])
end

getEnabled(::MIME"application/x-qt", action::Action) = action[:widget][:isEnabled]()
setEnabled(::MIME"application/x-qt", action::Action, value::Bool) = action[:widget][:setEnabled](value)

setLabel(::MIME"application/x-qt", action::Action, value::String) = action[:widget][:setText](value)

function setIcon(::MIME"application/x-qt", action::Action, value::Icon)
    if !isa(icon, Nothing)
        action[:widget][:setIcon](get_icon(widget.toolkit, icon))
    end
end

function setShortcut(::MIME"application/x-qt", action::Action, value::String)
    action[:widget][:setShortcut](value)
end

setTooltip(::MIME"application/x-qt", action::Action, value::String) = action[:widget][:setToolTip](value)

function setCommand(::MIME"application/x-qt", action::Action, value::Function)
    qconnect(action[:widget], :triggered, value)
end

## menus
function menubar(::MIME"application/x-qt", parent::Window)
    mainwindow = parent[:widget]
    mainwindow[:menuBar]()
end

## toplevel menu item
function menu(::MIME"application/x-qt", parent::MenuBar, label)
    parent[:widget][:addMenu](label)
end

## submenu
function menu(::MIME"application/x-qt", parent::Menu, label)
    parent[:widget][:addMenu](label)
end

## popup
## this is flaky! separator/checkbox overplot

function menu(::MIME"application/x-qt", parent::Widget)
    parent[:widget][:setContextMenuPolicy](PySide.QtCore["Qt"][:CustomContextMenu])
    m = PySide.Qt.QMenu(parent[:widget])

    qconnect(parent[:widget], :customContextMenuRequested) do pt
        update_context(parent.toolkit, parent, pt)
        m[:popup](parent[:widget][:mapToGlobal](pt))
    end

    m
end

## add actions
function addAction(::MIME"application/x-qt", parent::Menu, action::Action)
    parent[:widget][:addAction](action[:widget])
end

function addAction(::MIME"application/x-qt", parent::Menu, value::Separator)
    parent[:widget][:addSeparator]()
end

function addAction(::MIME"application/x-qt", parent::Menu, value::RadioGroup)
    widget = Qt.QActionGroup(parent[:widget])
    widget[:setExclusive](true)
    for item in value.model.items
        action = Qt.QAction(item, parent[:widget])
        action[:setCheckable](true)
        if item == value[:value]
            action[:setChecked](true)
        end
        widget[:addAction](action)
        parent[:widget][:addAction](action)
    end

    connect(value, :valueChanged) do val
        a = filter(a -> a[:text]() == value[:value], widget[:actions]())
        if length(a) >= 1
            a[1][:setChecked](true)
        end
    end

    qconnect(widget, :triggered) do action
        notify(value.model, "valueChanged", action[:text]())
    end

    widget
end

function addAction(::MIME"application/x-qt", parent::Menu, value::CheckBox)
    widget = Qt.QAction(value[:label], parent[:widget])
    widget[:setCheckable](true)
    widget[:setChecked](value[:value])
    connect(value, "valueChanged") do val
        widget[:setChecked](val)
    end
    ## widget[:setIcon](...)
    qconnect(widget, :changed) do
        notify(value.model, "valueChanged", widget[:isChecked]())
    end

    parent[:widget][:addAction](widget)
end

## manipulate
function Display(::MIME"application/x-qt", self::ManipulateObject, x; kwargs...)
    "XXX nothing here, though should do text output..."
end


text:
# colors chosen by according to http://seaborn.pydata.org/tutorial/color_palettes.html
# https://github.com/mwaskom/seaborn/blob/master/seaborn/palettes.py
# as proposed in https://github.com/JuliaPlots/Plots.jl/issues/2510

const seaborn_deep=[colorant"#4c72b0", colorant"#dd8452", colorant"#55a868", colorant"#c44e52", colorant"#8172b3",
                    colorant"#937860", colorant"#da8bc3", colorant"#8c8c8c", colorant"#ccb974", colorant"#64b5cd"]

const seaborn_deep6=[colorant"#4c72b0", colorant"#55a868", colorant"#c44e52",
                     colorant"#8172b3", colorant"#ccb974", colorant"#64b5cd"]

const seaborn_muted=[colorant"#4878d0", colorant"#ee854a", colorant"#6acc64", colorant"#d65f5f", colorant"#956cb4",
                     colorant"#8c613c", colorant"#dc7ec0", colorant"#797979", colorant"#d5bb67", colorant"#82c6e2"]

const seaborn_muted6=[colorant"#4878d0", colorant"#6acc64", colorant"#d65f5f",
                      colorant"#956cb4", colorant"#d5bb67", colorant"#82c6e2"]

const seaborn_pastel=[colorant"#a1c9f4", colorant"#ffb482", colorant"#8de5a1", colorant"#ff9f9b", colorant"#d0bbff",
                      colorant"#debb9b", colorant"#fab0e4", colorant"#cfcfcf", colorant"#fffea3", colorant"#b9f2f0"]

const seaborn_pastel6=[colorant"#a1c9f4", colorant"#8de5a1", colorant"#ff9f9b",
                       colorant"#d0bbff", colorant"#fffea3", colorant"#b9f2f0"]

const seaborn_bright=[colorant"#023eff", colorant"#ff7c00", colorant"#1ac938", colorant"#e8000b", colorant"#8b2be2",
                      colorant"#9f4800", colorant"#f14cc1", colorant"#a3a3a3", colorant"#ffc400", colorant"#00d7ff"]

const seaborn_bright6=[colorant"#023eff", colorant"#1ac938", colorant"#e8000b",
                       colorant"#8b2be2", colorant"#ffc400", colorant"#00d7ff"]

const seaborn_dark=[colorant"#001c7f", colorant"#b1400d", colorant"#12711c", colorant"#8c0800", colorant"#591e71",
                    colorant"#592f0d", colorant"#a23582", colorant"#3c3c3c", colorant"#b8850a", colorant"#006374"]

const seaborn_dark6=[colorant"#001c7f", colorant"#12711c", colorant"#8c0800",
                     colorant"#591e71", colorant"#b8850a", colorant"#006374"]

const seaborn_colorblind=[colorant"#0173b2", colorant"#de8f05", colorant"#029e73", colorant"#d55e00", colorant"#cc78bc",
                          colorant"#ca9161", colorant"#fbafe4", colorant"#949494", colorant"#ece133", colorant"#56b4e9"]

const seaborn_colorblind6=[colorant"#0173b2", colorant"#029e73", colorant"#d55e00",
                           colorant"#cc78bc", colorant"#ece133", colorant"#56b4e9"]

include("seaborn_color_gradients.jl")

_themes[:seaborn_deep] = PlotTheme(
    palette = expand_palette(colorant"white", seaborn_deep),
    colorgradient = seaborn_rocket_gradient,
)

_themes[:seaborn_deep6] = PlotTheme(
    palette = expand_palette(colorant"white", seaborn_deep6),
    colorgradient = seaborn_rocket_gradient,
)

_themes[:seaborn_muted] = PlotTheme(
    palette = expand_palette(colorant"white", seaborn_muted),
    colorgradient = seaborn_rocket_gradient,
)

_themes[:seaborn_muted6] = PlotTheme(
    palette = expand_palette(colorant"white", seaborn_muted6),
    colorgradient = seaborn_rocket_gradient,
)

_themes[:seaborn_pastel] = PlotTheme(
    palette = expand_palette(colorant"white", seaborn_pastel6),
    colorgradient = seaborn_rocket_gradient,
)

_themes[:seaborn_pastel6] = PlotTheme(
    palette = expand_palette(colorant"white", seaborn_pastel6),
    colorgradient = seaborn_rocket_gradient,
)

_themes[:seaborn_bright] = PlotTheme(
    palette = expand_palette(colorant"white", seaborn_bright),
    colorgradient = seaborn_rocket_gradient,
)

_themes[:seaborn_bright6] = PlotTheme(
    palette = expand_palette(colorant"white", seaborn_bright6),
    colorgradient = seaborn_rocket_gradient,
)
_themes[:seaborn_dark] = PlotTheme(
    palette = expand_palette(colorant"white", seaborn_dark),
    colorgradient = seaborn_rocket_gradient,
)

_themes[:seaborn_dark6] = PlotTheme(
    palette = expand_palette(colorant"white", seaborn_dark6),
    colorgradient = seaborn_rocket_gradient,
)

_themes[:seaborn_colorblind] = PlotTheme(
    palette = expand_palette(colorant"white", seaborn_colorblind),
    colorgradient = seaborn_rocket_gradient,
)

_themes[:seaborn_colorblind6] = PlotTheme(
    palette = expand_palette(colorant"white", seaborn_colorblind6),
    colorgradient = seaborn_rocket_gradient,
)


text:
## utils

function extend_deriv!(g::EinGraph, dg::EinGraph, dzdx::TensorDeriv)
    vname, idxs = split_indexed(single_var(dzdx))
    old_idxs = varidxs(dg[vname])
    var = make_indexed(vname, old_idxs)
    # substitution table to reindex new dzdx with the old indices
    # st = Dict(zip(idxs, old_idxs))
    subderivs = find_related(dg, vname)
    pos = indexof(dg, vname)
    if isempty(subderivs)

        # if wrtname(dzdx) == :dmu
        #     global _g = g
        #     global _dg = dg
        #     global _dzdx = dzdx
        #     error("here")
        # end

        # first split
        old_dzdx = TensorDeriv(g, to_expr(dg[vname]))
        dzdx = reindex_siblings_to_match(old_dzdx, dzdx)[2]
        ex_1 = getexpr(old_dzdx)
        ex_2 = getexpr(dzdx)
        vname_1 = Symbol("$(vname)__1")
        var_1 = make_indexed(vname_1, old_idxs)
        vname_2 = Symbol("$(vname)__2")
        var_2 = make_indexed(vname_2, old_idxs)
        sub_dg = EinGraph()
        parse!(sub_dg, :($var_1 = $ex_1))
        parse!(sub_dg, :($var_2 = $ex_2))
        parse!(sub_dg, :($var = $var_1 .+ $var_2))
        sub_dg = fuse_assigned(sub_dg)
        new_nodes = sub_dg.tape
    else
        # dg already contains subderivatives for dzdx_v
        last_idx = parse(Int, split(subderivs[end] |> String, "__")[end])
        new_var = make_indexed(Symbol("$(vname)__$(last_idx + 1)"), old_idxs)
        # TODO: should use reindex_sinlings_to_match instead
        # new_ex = subs(getexpr(dzdx), st)
        old_dzdx = TensorDeriv(g, to_expr(dg[vname]))
        dzdx = reindex_siblings_to_match(old_dzdx, dzdx)[2]
        new_ex = getexpr(dzdx)
        prev_ex = getexpr(dg[vname])
        sub_dg = EinGraph()
        parse!(sub_dg, :($new_var = $new_ex))
        parse!(sub_dg, :($var = $prev_ex .+ $new_var))
        sub_dg = fuse_assigned(sub_dg)
        new_nodes = sub_dg.tape
    end
    delete!(dg, pos)
    insert!(dg, pos, new_nodes)
    return dg
end

function permute_indices(lhs_idxs::Vector{T},
                         rhs_idxs::Vector{T}) where T
    diff_idxs = union(setdiff(Set(lhs_idxs), Set(rhs_idxs)),
                      setdiff(Set(rhs_idxs), Set(lhs_idxs)))
    f_rhs_idxs = [idx for idx in rhs_idxs if !in(idx, diff_idxs)]
    f_lhs_idxs = [idx for idx in lhs_idxs if !in(idx, diff_idxs)]
    st = Dict(zip(f_rhs_idxs, f_lhs_idxs))
    return [get(st, idx, idx) for idx in rhs_idxs]
end

function deriv_size(z_size::Expr, x_size::Expr)
    if z_size == :(())
        return x_size
    else
        # TODO: there should be a better representation of (z_size..., x_size...)
        return :($(z_size)..., $(x_size)...)

    end
end

## reverse pass

function rev_step!(g::EinGraph, dg::EinGraph, nd::ExNode{:(=)})
    z = g.ctx[:z_var]
    y = varname(nd)
    x = dependencies(nd)[1]
    dzdy_var = getvar(dg[deriv_name(z, y)])
    dzdx_vname = deriv_name(z, x)
    dzdx_idxs = varidxs(g[x])
    dzdx_var = make_indexed(dzdx_vname, dzdx_idxs)
    dzdx_full_ex = :($dzdx_var = $dzdy_var)
    parse!(dg, dzdx_full_ex)
end

function rev_step!(g::EinGraph, dg::EinGraph, nd::ExNode{:constant})
    # do nothing
end

function rev_step!(g::EinGraph, dg::EinGraph, nd::ExNode{:input})
    # do nothing
end

function rev_step!(g::EinGraph, dg::EinGraph, nd::ExNode{:call})
    y = varname(nd)
    iex = to_expr(nd)
    z = g.ctx[:z_var]
    cg = cat(g, dg)
    dzdy = TensorDeriv(g, dg[deriv_name(z, y)] |> to_expr)
    # sizes = g.ctx[:sizes]
    for x in dependencies(nd)
        if isa(g[x], ExNode{:constant})
            # don't clog dg with unnesessary derivs
            continue
        end
        # can find derivatives of special functions if `z` is a scalar
        if isspecial(getexpr(nd).args[1]) && isa(z, Symbol)
            dzdy_vname = deriv_name(z, y)
            dzdx = special_derivative(g, iex, dzdy_vname, z, x)
        else
            dydx = tderivative(iex, x)
            dzdx = dzdy ⊗ dydx
        end
        dzdx = expand_const(cg, dzdx) |> simplify
        dzdx_vname = split_indexed(single_var(dzdx))[1]
        if haskey(dg, dzdx_vname)
            extend_deriv!(g, dg, dzdx)
        else
            parse!(dg, to_expr(dzdx))
        end
        # sizes[dzdx_vname] = deriv_size(sizes[z], sizes[x])
    end
end

function make_dzdz_expr(z::Symbol, z_idxs::Vector)
    vname = deriv_name(z, z)
    wrt_idxs = next_indices(Set(z_idxs), 1, length(z_idxs))
    vidxs = vcat(z_idxs, wrt_idxs)
    var = make_indexed(vname, vidxs)
    guards = Expr[:($i == $j) for (i,j) in zip(z_idxs, wrt_idxs)]
    ex = with_guards(1.0, guards)
    return :($var = $ex)
end

function reverse_pass!(g::EinGraph)
    z_var = haskey(g.ctx, :cost) ? getvar(g[g.ctx[:cost]]) : getvar(g[end])
    z, z_idxs = split_indexed(z_var)
    g.ctx[:z_var] = z
    dzdz_ex = make_dzdz_expr(z, z_idxs)
    dg = EinGraph(dzdz_ex)
    cost_deps = collect_deps(g, z_var)
    for nd in reverse(g.tape)
        if varname(nd) in cost_deps
            rev_step!(g, dg, nd)
        end
    end
    outvars = [deriv_name(z, varname(nd)) for nd in g.tape if isa(nd, ExNode{:input})]
    return fuse_assigned(dg; outvars=outvars)
end