export DiskVector

type DiskVector{T} <: AVector{T}
  length::Int
  file::String
  handle::IOStream
  pos::Int
end

typealias DiskVectorT{T} Type{DiskVector{T}}

function (::DiskVectorT{T}){T}()
  file = tempname()
  v = DiskVector{T}(0, file, open(file, "a+"), 1)
  finalizer(v, close)
  return v
end

function (::DiskVectorT{T}){T}(xs)
  v = DiskVector{T}()
  for x in xs
    push!(v, x)
  end
  return v
end

DiskVector() = DiskVector{Any}()
DiskVector(xs) = DiskVector{eltype(xs)}(xs)

function Base.close(v::DiskVector)
  isopen(v.handle) && close(v.handle)
  isfile(v.file) && rm(v.file)
end

Base.size(v::DiskVector) = (v.length,)

function Base.push!{T}(v::DiskVector{T}, x::T)
  # warn("pushing single value")
  seekto(v, :end)
  if isbits(T)
    write(v.handle, x)
  else
    serialize(v.handle, x)
  end
  v.length += 1
  v.pos += 1
  return v
end

function Base.append!{T}(v::DiskVector{T}, xs::AbstractVector{T})
  seekto(v, :end)
  @assert isbits(T)
  write(v.handle, xs)
  v.length += length(xs)
  v.pos = v.length+1
  return v
end

function seekto(v::DiskVector, i)
  if i == :end
    if v.pos ≤ v.length
      seekend(v.handle)
      v.pos = v.length+1
    end
  elseif v.pos ≠ i
    @assert isbits(eltype(v))
    v.pos = i
    seek(v.handle, sizeof(eltype(v))*(i-1))
  end
end

function Base.getindex(v::DiskVector, i::Integer)
  # warn("reading single value")
  seekto(v, i)
  v.pos += 1
  return read(v.handle, eltype(v))
end

function Base.setindex!{T}(v::DiskVector{T}, x::T, i::Integer)
  @assert isbits(x)
  # warn("writing single value")
  seekto(v, i)
  write(v.handle, x)
  v.pos += 1
  return x
end

function Base.copy!{T}(v::AbstractVector{T}, xs::DiskVector{T})
  @assert isbits(T)
  seekstart(xs.handle)
  read!(xs.handle, v)
  return v
end

function Base.collect(xs::DiskVector)
  if isempty(xs)
    Vector{eltype(xs)}()
  else
    copy!(Vector{eltype(xs)}(length(xs)), xs)
  end
end

function Base.similar(v::DiskVector, n::Integer)
  @assert n == 0
  DiskVector{eltype(v)}()
end

# run(`open $(dirname(tempname()))`)
