"""
    macro @agent(name, varargs...) 

Basic Macro to create an instance of Agent. It generates an Agent type with the characteristics specified in its arguments.

The only compulsory input is the name of the agent.
 - **dims**: Dimensions.    
 
Then you can add an arbitrary set of additional parameters and rules for the agent. 
    
# Parameter Types

 - **:Local**
Local parameter with Float signature. It can represent the individual mass of each agent, its radius...
It is declared individually for each agent so it can be updated with UpdateLocal types.
Has to be declared as:

    localParameter::Local
    [localParameter1,localParameter2,localParameter3,...]::Local

 - **:Identity**
Parameter with Int signature that can be used to identify individually each agent in the model, its an agent type...
It is declared individually for each agent so it can be updated with UpdateLocal types.
Has to be declared as:

    idName::Identity
    [idName1,idName2,idName3,...]::Identity

 - **:Global**
Global parameter with Float signature. It can represent the temperature felt by all the agents...
It is declared globally for all agents and can be updated with UpdateGlobal.
Has to be declared as:

    globalParameter::Global
    [globalParameter1,globalParameter2,globalParameter3,...]::Global

 - **:GlobalArray**
Global array parameter with Float signature. It can represent the force or interaction matrix between different type of agents...
It is declared globally for all agents and can be updated with UpdateGlobal.
Has to be declared as:

    globalArrayParameter::GlobalArray
    [globalArrayParameter1,globalArrayParameter2,...]::GlobalArray

- **:Medium**
    Medium parameter with Float signature. These paremeters are the parameters of a medium in which the agents are embeded.
    Has to be declared as:
    
        mediumParameter::Medium
        [mediumParameter1,mediumParameter2,...]::Medium

# Update rules

 - **Equation**
Type for declaring dynamical equations for the variables.
Has to be declared as:

    Equation = 
        begin
            ... code including the differential equations ...
        end

 - **UpdateGlobal**
Type for declaring global update rules for the global parameters and global arrays.
Has to be declared as:

    UpdateGlobal = 
        begin
            ... code including the update rules ...
        end

 - **UpdateLocal**
Type for declaring local update rules for the local parameters and ids.
Has to be declared as:

    UpdateLocal = 
        begin
            ... code including the update rules ...
        end

 - **UpdateInteraction**
Type for declaring interaction update rules for the interaction parameters. 
The variables declared in these updates will be updated during the integration steps.
Has to be declared as:
    
    UpdateInteraction = 
        begin
            ... code including the update rules ...
        end

 - **UpdateLocalInteraction**
Type for declaring interaction update rules for the local parameters and ids. 
The variables declared in these updates will be updated just once per time step instead of once per integration point. 
For integrators that evaluate just once as the Euler integrator, UpdateInteraction and UpdateLocalInteraction are equivalent.
Has to be declared as:
        
    UpdateLocalInteraction = 
    begin
            ... code including the update rules ...
        end

 - **UpdateMedium**
Type for declaring the equations of motion that regulate the medium.
Has to be declared as:
        
    UpdateMedium = 
        begin
                ... code including the update rules ...
            end

 - **UpdateMediumInteraction**
Type for declaring the interaction between the agents and the medium. 
The agents are always modelled as delta sources of interaction with the medium located at their coordinates (x,y,z). 
Has to be declared as:
        
UpdateMediumInteraction = 
    begin
            ... code including the update rules ...
        end

# Custom made blocks of code

Any function which returns a piece of code with the structue as the above declared is valid for declaration. This allows repetitive algorithms to be reused and allow a fluent customization.

```
function localWithIncrements(variable,step)
    return begin
        \$variable::Local

        UpdateLocal = \$variable += \$step
    end
end

@agent(
    Agent, #Name of the agent

    l1::Local,

    localWithIncrement(:b)
)

```
"""
macro agent(dims, varargs...) 

    m = Agent()
    m.dims = dims

    if dims == 0
        nothing
    elseif dims == 1
        push!(m.declaredSymbols["Local"],:x)
    elseif dims == 2
        push!(m.declaredSymbols["Local"],:x,:y)
    elseif dims == 3
        push!(m.declaredSymbols["Local"],:x,:y,:z)
    else
        error("Dims has to be an integer between 0 and 3.")
    end
    push!(m.declaredSymbols["Identity"],:id)

    #Add all contributions
    for ii in varargs
        if typeof(ii) != Expr
            error(ii, " should be and expression containing at least a name and a type. Example variable::Variable.") 
        end

        if ii.head == :call
            t = eval(ii)
            if typeof(t) != Expr
                error("Function ", ii, " should return a piece of code to be added to the agent.")
            elseif t.head != :block
                t = quote $t end
            end

            t = t.args
        else 
            t = [ii]
        end

        for i in t
            if typeof(i) == LineNumberNode
                nothing
            elseif i.head == :(::)
                name = i.args[1]
                type = i.args[2]

                if !(type in VALID_TYPES)
                    error(i, " should be and expression containing at least a name and a type. Example variable::Variable.") 
                elseif typeof(name) == Expr
                    if !(name.head == :vect)
                        error(name, " should be a symbol or an array of symbols. Check how to declare ", type, " types.")
                    else
                        names = Symbol[]
                        for j in name.args
                            if typeof(j) != Symbol
                                error(j, " in ", i, " should be a symbol.  Check how to declare ", type, " types.")
                            end
                            push!(names,j)
                        end
                        name = names
                    end
                end    

                checkDeclared_(m,name)
                if typeof(name) == Array{Symbol,1}
                    append!(m.declaredSymbols[string(type)],name)
                else
                    push!(m.declaredSymbols[string(type)],name)
                end

            elseif i.head == :(=)
                if i.args[1] in VALID_UPDATES
                    if !(string(i.args[1]) in keys(m.declaredUpdates))
                        if i.args[2].head == :block
                            m.declaredUpdates[string(i.args[1])] = i.args[2]
                        else
                            m.declaredUpdates[string(i.args[1])] = quote $(i.args[2]) end
                        end
                    elseif i.args[2].head == :block
                        append!(m.declaredUpdates[string(i.args[1])].args,i.args[2].args)
                    end
                else
                    error(i.args[1], " is not a valid type.")
                end
            else
                error(i, " is not an understood rule or variable declaration. Error in ", ii)
            end 
        end
    end
        
    return m
end

