APP=theide
PKG=ide
DEP=$(PKG)

CINC+= -I./ -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
DEFS+= -DflagGUI -DflagGCC
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

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

define get-link-libs
$(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) $(2)))))
endef

define get-upp
$(if $(findstring /,$(1)),$(1)/$(notdir $(1)).upp,$(1)/$(1).upp)
endef

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

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

define get-deps
$(filter-out $(PKG), $(sort $(1) $(foreach d,$(call get-dep,$(1)), $(call get-deps,$(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
$(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

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, $(DEFS)), ,$(call get-link-libs, $(1),"library(.*$(PLATBSD).*NOGTK)"))
		LIBS+=$(if $(findstring XLFD, $(DEFS)), ,$(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+=$(if $(findstring NOGTK, $(DEFS)), ,$(call get-link-libs, $(1),"library(.*$(PLATFORM).*NOGTK)"))
	LIBS+=$(if $(findstring XLFD, $(DEFS)), ,$(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(.*$(PLATFORM).*!SHARED)")
	endif
endef

$(PKG)_SRCS=$(call get-impl-srcs, $(PKG))
$(PKG)_SPEED_SRCS=$(call get-speed-files, $(PKG))
$(PKG)_OBJS=$(call get-obj-files, $($(PKG)_SRCS))

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

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

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

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

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

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

all: $(TARGET)

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

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

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

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

clean:
	rm -f `find . -name "*.a"`
	rm -f `find . -name "*.o"`
