################################################################################
#                              General Variables                               #
################################################################################
BASE_NAME := Main

CASES_INPUT := $(sort $(wildcard *.in))
CASES_OUTPUT := $(sort $(wildcard *.out))
TESTS := $(filter $(basename $(CASES_INPUT)),$(basename $(CASES_OUTPUT)))

SHELL := /bin/sh

LIMITER ?= timeout
LIMITER_FLAGS ?= --preserve-status -s SIGKILL 10s

export TIME=real %es\nuser %Us\nsys  %Ss\nmem  %MKB

################################################################################
#                       Compilation/Execution Variables                        #
################################################################################
CC ?= gcc
CC_FLAGS ?= -Wall -Wextra -pedantic -Wshadow -Wformat=2 -Wfloat-equal \
            -Wconversion -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond \
            -Wcast-qual -Wcast-align -Wno-unused-result -Wno-sign-conversion

CXX ?= g++
CXX_FLAGS ?= -Wall -Wextra -pedantic -Wshadow -Wformat=2 -Wfloat-equal \
             -Wconversion -Wlogical-op -Wshift-overflow=2 -Wduplicated-cond \
             -Wcast-qual -Wcast-align -Wno-unused-result -Wno-sign-conversion

JC ?= javac
JC_FLAGS ?= -g
JAVA ?= java
JAVA_FLAGS ?=

PYTHON ?= python3
PYTHON_FLAGS ?=

RUBY ?= ruby
RUBY_FLAGS ?=

################################################################################
#                                File Variables                                #
################################################################################
COMPILED_SRCS :=
COMPILED_OBJS :=
INTERPRETED_SRCS :=
RESULTS :=

ifneq (,$(wildcard ./$(BASE_NAME).c))
	COMPILED_SRCS += $(BASE_NAME).c
	COMPILED_OBJS += $(BASE_NAME).c.exe
	RESULTS += $(addsuffix .$(BASE_NAME).c.exe.res,$(TESTS))
endif

ifneq (,$(wildcard ./$(BASE_NAME).cpp))
	COMPILED_SRCS += $(BASE_NAME).cpp
	COMPILED_OBJS += $(BASE_NAME).cpp.exe
	RESULTS += $(addsuffix .$(BASE_NAME).cpp.exe.res,$(TESTS))
endif

ifneq (,$(wildcard ./$(BASE_NAME).java))
	COMPILED_SRCS += $(BASE_NAME).java
	COMPILED_OBJS += $(BASE_NAME).class
	RESULTS += $(addsuffix .$(BASE_NAME).class.res,$(TESTS))
endif

ifneq (,$(wildcard ./$(BASE_NAME).py))
	INTERPRETED_SRCS += $(BASE_NAME).py
	RESULTS += $(addsuffix .$(BASE_NAME).py.res,$(TESTS))
endif

ifneq (,$(wildcard ./$(BASE_NAME).rb))
	INTERPRETED_SRCS += $(BASE_NAME).rb
	RESULTS += $(addsuffix .$(BASE_NAME).rb.res,$(TESTS))
endif

STATS := $(patsubst %.res,%.stat,$(RESULTS))

CLEANABLE := $(filter $(COMPILED_OBJS) $(RESULTS) $(STATS),$(wildcard *))

################################################################################
#                                 Phony Rules                                  #
################################################################################
.PHONY: all build clean retest test

all: test

build: $(COMPILED_OBJS)

clean:
ifneq (,$(CLEANABLE))
	rm $(CLEANABLE)
endif

retest: clean test

test: build $(RESULTS) $(STATS) $(CASES_OUTPUT)
	@pass=0; \
	fail=0; \
	for test in $(patsubst %.res,%,$(RESULTS)); do \
		echo "########################################"; \
		printf "# %-29s # %s #\n" $$test \
           $$(diff $$test.res "$$(echo "$$test" | cut -f 1 -d '.').out" \
              2>&1>/dev/null && echo 'PASS' || echo 'FAIL'); \
		echo "########################################"; \
		cat $$test.stat; \
		echo "########################################"; \
		diff --color $$test.res "$$(echo "$$test" | cut -f 1 -d '.').out" \
         && pass=$$((pass + 1)) || fail=$$((fail + 1)); \
		echo ; \
	done; \
	echo "########################################"; \
	printf "# TOTAL: %-5s # PASS: %-3s # FAIL: %-3s #\n" \
         $$((pass + fail)) $$pass $$fail; \
	echo "########################################"

################################################################################
#                               Compiling Rules                                #
################################################################################
$(BASE_NAME).c.exe: $(BASE_NAME).c
	$(CC) $(CC_FLAGS) $^ -o $@

$(BASE_NAME).cpp.exe: $(BASE_NAME).cpp
	$(CXX) $(CXX_FLAGS) $^ -o $@

$(BASE_NAME).class: $(BASE_NAME).java
	$(JC) $(JC_FLAGS) $^

################################################################################
#                                Running rules                                 #
################################################################################
%.$(BASE_NAME).c.exe.res %.$(BASE_NAME).c.exe.stat: %.in $(BASE_NAME).c.exe
	-@\time $(LIMITER) $(LIMITER_FLAGS) ./$(BASE_NAME).c.exe < $*.in \
          > $*.$(BASE_NAME).c.exe.res 2> $*.$(BASE_NAME).c.exe.stat

%.$(BASE_NAME).cpp.exe.res: %.in $(BASE_NAME).cpp.exe
	-@\time $(LIMITER) $(LIMITER_FLAGS) ./$(BASE_NAME).cpp.exe < $*.in \
          > $*.$(BASE_NAME).cpp.exe.res 2> $*.$(BASE_NAME).cpp.exe.stat

%.$(BASE_NAME).class.res: %.in $(BASE_NAME).class
	-@\time $(LIMITER) $(LIMITER_FLAGS) $(JAVA) $(JAVA_FLAGS) $(BASE_NAME) \
          < $*.in > $*.$(BASE_NAME).class.res 2> $*.$(BASE_NAME).class.stat

%.$(BASE_NAME).py.res: %.in $(BASE_NAME).py
	-@\time $(LIMITER) $(LIMITER_FLAGS) $(PYTHON) $(PYTHON_FLAGS) \
          $(BASE_NAME).py < $*.in > $*.$(BASE_NAME).py.res \
          2> $*.$(BASE_NAME).py.stat

%.$(BASE_NAME).rb.res: %.in $(BASE_NAME).rb
	-@\time $(LIMITER) $(LIMITER_FLAGS) $(RUBY) $(RUBY_FLAGS) \
          $(BASE_NAME).rb < $*.in > $*.$(BASE_NAME).rb.res \
          2> $*.$(BASE_NAME).rb.stat