PKG:=ide
APPNAME:=$(PKG).out
ASMBLY:= $(ASSEMBLY) uppsrc

CINC:= $(INCLUDES) -I/usr/include/freetype2 -I/usr/include/gtk-2.0 -I/usr/local/include/gtk-2.0 -I/usr/include/glib-2.0 -I/usr/local/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/local/lib/glib-2.0/include -I/usr/lib/gtk-2.0/include -I/usr/local/lib/gtk-2.0/include -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/atk-1.0 -I/usr/X11R6/include -I/usr/X11R6/include/freetype2 -I/usr/X11R6/include/gtk-2.0 -I/usr/X11R6/include/glib-2.0 -I/usr/X11R6/lib/glib-2.0/include -I/usr/X11R6/lib/gtk-2.0/include -I/usr/X11R6/include/cairo -I/usr/X11R6/include/pango-1.0 -I/usr/X11R6/include/atk-1.0 -I/usr/local/include/cairo -I/usr/local/include/pango-1.0 -I/usr/local/include/atk-1.0 -I/usr/local/include -I/usr/local/include/libpng
PKGDEFS:= $(foreach f, $(DEFS), -Dflag$(f))
OBJDIR:= _out
CC:= gcc
CXX:= c++
FLAGS:= -c -ffunction-sections -fdata-sections
OPTFLAGS:= -Os -finline-limit=20
CFLAGS:= $(CC) $(FLAGS)
CXXFLAGS:= $(CXX) $(FLAGS)

AR:= ar -src
SHARED:=y
LIBPATH:= -L/usr/X11R6/lib -L/usr/lib -L/usr/local/lib

ifeq ($(filter 3.8%, $(MAKE_VERSION)), )
$(error This Makefile only supports GNU make version 3.80 and newer)
endif

ifneq ($(VERBOSE),)
SILENT:=
else
SILENT:=@
endif

PLATFORM:=$(shell uname | tr a-z A-Z)

SRC_EXTS:= .c .cpp .icpp
SRC_EXTS_PAT:= $(foreach e,$(SRC_EXTS),%$(e))
IMPL_FILES:= $(SRC_EXTS_PAT) %.h %.iml

DIRS:=$(foreach a, $(ASMBLY), $(foreach p, $(shell ls -R $(a)|grep ".*:"|sed 's/://'), $(p)))
PKGDIR:=$(filter %$(PKG),$(DIRS))
ifeq ($(PKGDIR),)
$(error Cannot find package $(PKG) in specified assemblies: $(ASMBLY))
endif
CINC+=$(foreach a,$(ASMBLY),-I$(a))

define get-link-libs
$(if $(1),$(shell cat $(1)/$(notdir $(1)).upp | grep $(2) | sed "s/lib.*(.*)\(.*\);/\1/" | tr -d '"'))
endef

define gen-rule
$(if $(patsubst %.icpp,,$(2)), $(strip $(1)/$(filter-out \, $(shell $(CXX) -M $(CINC) $(MAINDEFS) $(2)))))
endef

define get-key
$(strip $(if $(1),$(shell cat $(1)/$(notdir $(1)).upp | tr ",\n;" "  \n" | grep $(2) | tr -d '"')))
endef

define get-dep
$(subst \,/,$(filter-out uses%,$(call get-key,$(1),"uses")))
endef

define pkg-cache
$(if $($(1).cached),$($(1).cached),$(eval $(1).cached=$(call get-deps,$(filter $(foreach a,$(ASMBLY),$(a)/$(d)),$(DIRS))))$($(1).cached))
endef
 
define get-deps
$(filter-out $(PKGDIR), $(sort $(1) $(foreach d,$(call get-dep,$(1)),$(call pkg-cache,$(d)))))
endef

define get-pkg-files
$(foreach f, $(filter $(IMPL_FILES), $(subst \,/,$(filter-out file%,$(call get-key,$(1),"file")))), $(1)/$(f))
endef

define get-obj-files
$(foreach f,$(1),$(foreach e,$(SRC_EXTS),$(filter %.o,$(f:$(e)=.o))))
endef

define get-impl-srcs
$(filter $(SRC_EXTS_PAT), $(call get-pkg-files,$(1)))
endef

define get-icpp-files
$(filter %.icpp, $(call get-pkg-files,$(1)))
endef

define get-speed-pkg
$(if $(1),$(shell cat $(1)/$(notdir $(1)).upp | grep "optimize_speed.*;"))
endef

define get-speed-files
$(strip $(foreach f, $(shell cat $(1)/$(notdir $(1)).upp | grep "c.*optimize_speed" | sed 's/\(.*.cpp\).*/\1/'), $(1)/$(f)))
endef

# this function should return the rvalue of first entry inside the mainconfig section
define get-mainconfig
$(strip $(filter-out mainconfig =%, $(shell cat $(1)/$(notdir $(1)).upp |grep "=" |sed q|sed s'/.*=.*\"\(.*\)\".*/\1/')))
endef

define get-all-libs
	ifeq ($(findstring BSD, $(PLATFORM)),BSD)
		PLATBSD:=$(strip $(findstring BSD, $(PLATFORM)))
		LIBS+=$(call get-link-libs, $(1), "library($(PLATBSD))")
		LIBS+=$(if $(findstring NOGTK, $(PKGDEFS)), ,$(call get-link-libs, $(1),"library(.*$(PLATBSD).*NOGTK)"))
		LIBS+=$(if $(findstring XLFD, $(PKGDEFS)), ,$(call get-link-libs, $(1),"library(.*$(PLATBSD).*XLFD)"))
		ifeq ($(SHARED),)
			LIBS+=$(call get-link-libs, $(1),"library(.*$(PLATBSD).*!SHARED)")
		endif
	endif
	LIBS+=$(call get-link-libs, $(1), "library($(PLATFORM))")
	LIBS+=$(call get-link-libs, $(1), "library(.*POSIX.*)")
	LIBS+=$(if $(findstring NOGTK, $(PKGDEFS)), ,$(call get-link-libs, $(1),"library(.*POSIX.*NOGTK)"))
	LIBS+=$(if $(findstring NOGTK, $(PKGDEFS)), ,$(call get-link-libs, $(1),"library(.*$(PLATFORM).*NOGTK)"))
	LIBS+=$(if $(findstring XLFD, $(PKGDEFS)), ,$(call get-link-libs, $(1),"library(.*POSIX.*XLFD)"))
	LIBS+=$(if $(findstring XLFD, $(PKGDEFS)), ,$(call get-link-libs, $(1),"library(.*$(PLATFORM).*XLFD)"))
	LIBS+=$(call get-link-libs, $(1),"library(.*\!WIN32.*)")
	ifeq ($(SHARED),)
		LIBS+=$(call get-link-libs, $(1),"library(.*POSIX.*!SHARED)")
		LIBS+=$(call get-link-libs, $(1),"library(.*$(PLATFORM).*!SHARED)")
	endif
endef

define path-part
$(strip $(if $(findstring $(1),$(PKGDEFS)),$(1)-,))
endef

$(PKG)_SRCS:=$(call get-impl-srcs, $(PKGDIR))
$(PKG)_SPEED_SRCS:=$(call get-speed-files, $(PKGDIR))
$(PKG)_OBJDIR=$(OBJDIR)/$(call path-part,GUI)$(call path-part,NOGTK)$(call path-part,MT)$(strip $(if $(findstring y,$(SHARED)),SHARED,))/
$(PKG)_OBJS=$(strip $(foreach f, $(call get-obj-files, $($(PKG)_SRCS)), $($(PKG)_OBJDIR)$(f)))

EXTRDEFS:=$(foreach f, $(call get-mainconfig, $(PKGDIR)),$(f))

ifneq ($(APPNAME),)
$(info Package informations are gathered, this may take some time, please wait ...)
	TARGET:=$(APPNAME)
	PKGDEFS+= -DflagGCC $(foreach f, $(EXTRDEFS), -Dflag$(f))
	MAINDEFS:= $(PKGDEFS) -DflagMAIN
	$(PKG)_DEPS_REC:=$(call get-deps,$(PKGDIR))
	$(PKG)_LINK_LIBS:=$(foreach d,$($(PKG)_DEPS_REC),$($(PKG)_OBJDIR)$(d).a)
	ICPPFILES:=$(foreach d, $($(PKG)_DEPS_REC), $(call get-icpp-files,$(d)))
	ICPPOBJS:=$(foreach f,$(ICPPFILES:.icpp=.o),$($(PKG)_OBJDIR)$(f))
$(foreach d, $($(PKG)_DEPS_REC), $(eval $(call get-all-libs, $(d))))
$(eval $(call get-all-libs, $(PKGDIR)))
	LIBS:=$(sort $(foreach l, $(LIBS), -l$(l)))
else
	TARGET:=$($(PKG)_OBJDIR)$(PKG).a
	MAINDEFS:=$(PKGDEFS)
endif

COMPILE_MSG= "  ===> Compiling $< ..."
DEP_MSG=  "===> Building package $@ ..."
AR_MSG=  "  ===> Building archive $@ ..."

$($(PKG)_OBJDIR)%.o: %.c
	@echo $(COMPILE_MSG)
	$(SILENT)$(CFLAGS) $(if $(findstring $<, $($(PKG)_SPEED_SRCS)),-O2,$(OPTFLAGS)) $(CINC) $(MAINDEFS) $< -o $@

$($(PKG)_OBJDIR)%.o: %.cpp
	@echo $(COMPILE_MSG)
	$(SILENT)$(CXXFLAGS) $(if $(findstring $<, $($(PKG)_SPEED_SRCS)),-O2,$(OPTFLAGS)) $(CINC) $(MAINDEFS) $< -o $@

$($(PKG)_OBJDIR)%.o: %.icpp
	@echo $(COMPILE_MSG)
	$(SILENT)$(CXXFLAGS) $(if $(findstring $<, $($(PKG)_SPEED_SRCS)),-O2,$(OPTFLAGS)) -x c++ $(CINC) $(MAINDEFS) $< -o $@

.PHONY: all objdir gen-deps $($(PKG)_DEPS_REC)

all: objdir $(TARGET)

$(foreach f, $($(PKG)_SRCS), $(eval $(call gen-rule,$($(PKG)_OBJDIR)$(PKGDIR),$(f))))

gen-deps: $($(PKG)_DEPS_REC)

$($(PKG)_DEPS_REC):
	@echo $(DEP_MSG)
	@$(MAKE) --no-print-directory PKG=$@ APPNAME= $(if $(call get-speed-pkg, $@), OPTFLAGS=-O2) DEFS="$(EXTRDEFS) $(DEFS)"

$($(PKG)_OBJDIR)$(PKG).a: $($(PKG)_OBJS)
	@echo $(AR_MSG)
	$(SILENT)$(AR) $@ $?
	
$(APPNAME): $($(PKG)_DEPS_REC) $($(PKG)_OBJS)
	@echo "==> Linking $@ ..."
	$(SILENT)$(CXX) -o $@ $(if $(SHARED),-s,--gc-sections) $(LIBPATH) $(LDFLAGS) $($(PKG)_OBJS) $(ICPPOBJS) -Wl,--start-group $($(PKG)_LINK_LIBS) $(LIBS) -Wl,--end-group
	@echo "Build successfully completed."

objdir:
	@echo "  Creating directories for $(PKGDIR) object files ..."
	@mkdir -p $(sort $(foreach f, $($(PKG)_OBJS), $(dir $(f))))

clean:
	@echo "Cleaning source tree ..."
	@rm -rf $(OBJDIR) || true
	@rm -f $(APPNAME) || true
