ifndef NEURAL_MODELLING_DIRS
    $(error NEURAL_MODELLING_DIRS is not set.  Please define NEURAL_MODELLING_DIRS (possibly by running "source setup" in the neural_modelling folder within the sPyNNaker source folder))
endif

print-%  : ; @echo $* = $($*)

MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
CURRENT_DIR := $(dir $(MAKEFILE_PATH))
EXTRA_SRC_DIR := $(abspath $(CURRENT_DIR))
APP_OUTPUT_DIR := $(abspath $(CURRENT_DIR)../../python_models/model_binaries/)/
CFLAGS += -I$(NEURAL_MODELLING_DIRS)/src



# include $(NEURAL_MODELLING_DIRS)/src/Makefile.common

MAKEFILE_PATH := $(abspath $(NEURAL_MODELLING_DIRS)/src/Makefile.common)
CURRENT_DIR := $(dir $(MAKEFILE_PATH))
SOURCE_DIR := $(abspath $(CURRENT_DIR))
SOURCE_DIRS += $(SOURCE_DIR)
ifndef APP_OUTPUT_DIR
    APP_OUTPUT_DIR := $(abspath $(CURRENT_DIR)../../spynnaker/pyNN/model_binaries/)/
endif



# include $(SPINN_DIRS)/make_lib/Makefile.SpiNNFrontEndCommon

# If SPINN_DIRS is not defined, this is an error!
ifndef SPINN_DIRS
    $(error SPINN_DIRS is not set.  Please define SPINN_DIRS (possibly by running "source setup" in the spinnaker package folder))
endif

ifndef SOURCE_DIRS
    $(error SOURCE_DIRS is not set.  Please define SOURCE_DIRS)
endif

ifndef APP_OUTPUT_DIR
    $(error APP_OUTPUT_DIRS is not set.  Please define APP_OUTPUT_DIRS)
endif

ifndef BUILD_DIR
    $(error BUILD_DIR is not set.  Please define BUILD_DIR)
endif

define define-build-code
$$(BUILD_DIR)%.o: $1/%.c
	-mkdir -p $$(dir $$@)
	$$(CC) $$(CFLAGS) -D__FILE__=\"$$(notdir $$*.c)\" -o $$@ $$<
endef

# Convert the objs into the correct format to work here
OBJS := $(abspath $(SOURCES))
$(foreach dir, $(SOURCE_DIRS), $(eval OBJS := $(OBJS:$(abspath $(dir))/%.c=$(BUILD_DIR)%.o)))
$(foreach dir, $(SOURCE_DIRS), $(eval $(call define-build-code,$(dir))))
OBJECTS += $(OBJS)

LIBRARIES += -lspinn_frontend_common -lspinn_common -lm
ifndef DEBUG
    DEBUG = PRODUCTION_CODE
endif
# Run md5sum on application name and extract first 8 bytes
SHELL = bash
APPLICATION_NAME_HASH = $(shell echo -n "$(APP)" | md5sum | cut -c 1-8)

CFLAGS += -Wall -Wextra -D$(DEBUG) -Ofast -DAPPLICATION_NAME_HASH=0x$(APPLICATION_NAME_HASH)



# include $(SPINN_DIRS)/Makefile.common

# Common includes for making SpiNNaker binaries

ifndef GNU
    GNU := 1
endif
ifndef LIB
    LIB := 0
endif

# If SPINN_DIRS is not defined, this is an error!
ifndef SPINN_DIRS
    $(error SPINN_DIRS is not set.  Please define SPINN_DIRS (possibly by running "source setup" in the spinnaker tools folder))
endif
SPINN_LIB_DIR = $(SPINN_DIRS)/lib
SPINN_INC_DIR = $(SPINN_DIRS)/include
SPINN_TOOLS_DIR = $(SPINN_DIRS)/tools
SPINN_MAKE_LIB_DIR = $(SPINN_DIRS)/make_lib

# ------------------------------------------------------------------------------
# Tools

ifeq ($(GNU),1)

    # GNU Compiler (gcc) settings
    GP := arm-none-eabi
    AS := $(GP)-as --defsym GNU=1 -mthumb-interwork -march=armv5te
    
    ifeq ($(LIB), 1)
        CC := $(GP)-gcc -c -Os -mthumb-interwork -march=armv5te -std=gnu99 -I $(SPINN_INC_DIR)
        CFLAGS += -fdata-sections -ffunction-sections
        LD := $(GP)-ld -i
    else
        CC := $(GP)-gcc -mthumb-interwork -march=armv5te -std=gnu99 -I $(SPINN_INC_DIR) -c
        LD := $(GP)-gcc -T$(SPINN_TOOLS_DIR)/sark.lnk -Wl,-e,cpu_reset -Wl,-static -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--use-blx -nostartfiles -static
        LFLAGS += -L $(SPINN_LIB_DIR)
    endif
    
    CC_THUMB := $(CC) -mthumb -DTHUMB
    AR := $(GP)-ar -rcs
    OC := $(GP)-objcopy
    OD := $(GP)-objdump -dxt
    NM := $(GP)-nm
    SPINN_LIBS += $(SPINN_LIB_DIR)/libspin1_api.a

else
    # ARM Compiler settings
    AS := armasm --keep --cpu=5te --apcs /interwork
    CC := armcc -c --c99 --cpu=5te -Ospace --apcs /interwork --min_array_alignment=4 -I $(SPINN_INC_DIR)
    CC_THUMB := $(CC) --thumb -DTHUMB
    
    ifeq ($(LIB), 1)
        CFLAGS += --split_sections
        LD := armlink --partial
    else
        LD = armlink --scatter=$(SPINN_TOOLS_DIR)/sark.sct --remove --entry cpu_reset
    endif
    
    AR := armar -rsc
    OC := fromelf
    OD := fromelf -cds --output
    NM := nm
    SPINN_LIBS += $(SPINN_LIB_DIR)/spin1_api.a
endif

RM := rm -f
CAT := cat
LS := ls -l
MKDIR := mkdir -p
CP := cp


# Primary target is an APLX file - built from the ELF
#  1) Create a binary file which is the concatenation of RO and RW sections
#  2) Make an APLX header from the ELF file with "mkaplx" and concatenate
#     that with the binary to make the APLX file
#  3) Remove temporary files and "ls" the APLX file
$(APP_OUTPUT_DIR)%.aplx: $(BUILD_DIR)%.elf
	$(MKDIR) $(APP_OUTPUT_DIR)
ifeq ($(GNU),1)
	$(OC) -O binary -j RO_DATA $< $(BUILD_DIR)RO_DATA.bin
	$(OC) -O binary -j RW_DATA $< $(BUILD_DIR)RW_DATA.bin
	$(SPINN_TOOLS_DIR)/mkbin $(BUILD_DIR)RO_DATA.bin $(BUILD_DIR)RW_DATA.bin > $(BUILD_DIR)$*.bin
else
	$(OC) --bin --output $*.bin $<
endif
	$(SPINN_TOOLS_DIR)/mkaplx -nm $(NM) $< | $(CAT) - $(BUILD_DIR)$*.bin > $@
	$(RM) $(BUILD_DIR)$*.bin $(BUILD_DIR)RO_DATA.bin $(BUILD_DIR)RW_DATA.bin
	$(LS) $@

# Build the ELF file
#  1) Make a "sark_build.c" file containing app. name and build time
#     with "mkbuild" and compile it
#  2) Link application object(s), build file and library to make the ELF
#  3) Tidy up temporaries and create a list file
$(BUILD_DIR)%.elf: $(OBJECTS) $(BUILD_DIR)neuron/fp_math.o $(SCRIPT) $(SPINN_LIBS)
	$(MKDIR) $(BUILD_DIR)
	$(SPINN_TOOLS_DIR)/mkbuild $* > $(BUILD_DIR)sark_build.c
	$(CC) -o $(BUILD_DIR)sark_build.o $(BUILD_DIR)sark_build.c
	$(LD) $(LFLAGS) $(OBJECTS) $(BUILD_DIR)neuron/fp_math.o $(BUILD_DIR)sark_build.o $(LIBRARIES) $(SPINN_LIBS) -o $@
	$(RM) $(BUILD_DIR)sark_build.c $(BUILD_DIR)sark_build.o
	$(OD) > $(BUILD_DIR)$*.txt $@

$(BUILD_DIR)%.o: %.c
	$(MKDIR) $(BUILD_DIR)
	$(CC) $(CFLAGS) -o $@ $<



# include $(SPINN_DIRS)/make_lib/Makefile.SpiNNFrontEndCommon (continued)

all: $(APP_OUTPUT_DIR)$(APP).aplx

# Tidy and cleaning dependencies
clean:
	$(RM) $(OBJECTS) $(BUILD_DIR)$(APP).elf $(BUILD_DIR)$(APP).txt $(APP_OUTPUT_DIR)$(APP).aplx