export dimension

mutable struct ModAlgAss{S, T, V}
  base_ring::S
  action::Vector{T}
  dimension::Int
  M::AbstractAlgebra.FPModule{V}
  isirreducible::Int
  dimension_splitting_field::Int
  algebra::AlgAss{V}
  action_of_gens::Vector{T}
  action_of_basis::Vector{T}

  function ModAlgAss{S, T}(action::Vector{T}) where {S, T}
    z = new{S, T, elem_type(S)}()
    z.action = action
    z.dimension = ncols(action[1])
    z.base_ring = base_ring(action[1])
    if z.dimension == 1
      z.isirreducible = 1
      z.dimension_splitting_field = 1
    else
      z.isirreducible = 0
      z.dimension_splitting_field = 0
    end
    return z
  end
end

mutable struct Lat{S, T}
  mod::S
  order::AlgAssAbsOrd
  basis_matrix::T

  function Lat{S, T}(mod::S, order::AlgAssAbsOrd, basis_matrix::T) where {S, T}
    z = new{S, T}(mod, order, basis_matrix)
    return z
  end
end

function vector_space(M::ModAlgAss)
  return M.M
end

function algebra(M::ModAlgAss)
  if isdefined(M :algebra)
    return M.algebra
  else
    A = algebra(M.action)
    M.algebra = A
    M.action_of_gens = M.action
    return A
  end
end

function action(a::AlgAssElem, M::ModAlgAss)
  parent(a) == algebra(M)
end

function basis_matrix(L::Lat)
  return L.basis_matrix
end

function action(a::AlgAssAbsOrdElem, L::Lat)
end

function ModAlgAss(action::Vector{T}) where {T}
  @assert length(action) > 0
  S = typeof(base_ring(action[1]))
  return ModAlgAss{S, T}(action)
end

function ModAlgAss(M::AbstractAlgebra.FPModule{T}, action::Vector{<:Generic.ModuleHomomorphism{T}}) where {T <: FieldElem}
  all(x->domain(x) == codomain(x) == M, action) || error("module and maps do not match")
  V = ModAlgAss([x.matrix for x = action])
  V.M = M
  return V
end

function isirreducible_known(M::ModAlgAss)
  return M.isirreducible != 0
end

function isirreducible(M::ModAlgAss)
  if M.isirreducible != 0
    return M.isirreducible == 1
  else
    fl, N = meataxe(M)
    return fl
  end
end

function dimension(M::ModAlgAss)
  return M.dimension
end

function base_ring(M::ModAlgAss)
  return M.base_ring
end

# Compute the algebra generated by M and images
# of the elements in M

function algebra(M::Vector{T}) where {T <: MatElem}
  @assert length(M) > 0
  A = M[1]
  n = nrows(A)
  n2 = n^2
  @assert n == ncols(A)
  K = base_ring(A)
  Mprod = M
  Morig = copy(M)

  current_dim = -1

  B = zero_matrix(K, length(Mprod) + 1, n2)

  l = 0
  while true
    if l != 0
      B = zero_matrix(K, length(Mprod), n2)
    end
    for k in 1:length(Mprod)
      for i in 1:n
        for j in 1:n
          B[k, (i - 1)* n  + j] = Mprod[k][i, j]
        end
      end
    end
    # Add the identity
    if l == 0
      for i in 1:n
        B[length(M) + 1, (i - 1)*n + i] = one(K)
      end
    end
    new_dim = rref!(B)
    if new_dim == current_dim
      break
    end
    current_dim = new_dim
    M = [ matrix(K, n, n, [B[k, (i - 1)*n + j] for i in 1:n for j in 1:n]) for k in 1:new_dim]
    Mprod = [ M[i] * M[j] for i in 1:length(M) for j in 1:length(M) ]
    l = l + 1
  end

  dim = current_dim
  B = sub(B, 1:dim, 1:ncols(B))

  basis = [ matrix(K, n, n, [B[k, (i - 1)*n + j] for i in 1:n for j in 1:n]) for k in 1:dim]

  @assert isone(basis[1])

  v = zero_matrix(K, 1, n2)

  structure = Array{elem_type(K), 3}(dim, dim, dim)

  for k in 1:dim
    for l in 1:dim
      N = basis[k] * basis[l]
      for i in 1:n
        for j in 1:n
          v[1, (i - 1)* n  + j] = N[i, j]
        end
      end
      b, u = can_solve_with_solution(B, v, side = :left)
      error("NOT HERE!")
      @assert b
      @assert N == sum(u[i]*basis[i] for i in 1:dim)
      for m in 1:dim
        structure[k, l, m] = u[m, 1]
      end
    end
  end

  A = AlgAss(K, structure)

  gens = Vector{AlgAssElem{elem_type(K)}}(length(Morig))

  for l in 1:length(Morig)
    N = Morig[l]
    for i in 1:n
      for j in 1:n
        v[1, (i - 1)* n  + j] = N[i, j]
      end
    end
    b, u = can_solve_with_solution(B, v, side = :left)
    gens[l] =  A([u[1, m] for m in 1:dim])
  end

  A.gens = gens

  return A
end

#function gens(A::AlgAss{T}) where {T}
#  #return A.gens::Vector{AlgAssElem{T}}
#end
