#
# fraktal - visualization of Mandelbrot and Julia sets
# Copyright (C) 1999-2026  Joachim Reichel <joachim.reichel@posteo.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

# USE_DEBUG := 1
USE_OPENMP  := 1
USE_GLEW    := 1
USE_PNG     := 1
USE_JPEG    := 1
# USE_CUDA    := 1

ifdef USE_DEBUG
  CXXFLAGS  += -g
else
  CXXFLAGS  += -O3
endif

CXXFLAGS    += -std=c++20 -fPIC
CXXFLAGS    += -Wall -Wextra -Wformat-security -Wlogical-op -Wold-style-cast -Wno-deprecated-enum-enum-conversion
CXXFLAGS    += -DFRAKTAL_PACKAGE=\"$(NAME)\" -DFRAKTAL_VERSION=\"$(VERSION)\"
LDFLAGS     += -fuse-ld=mold -fPIC

QT_MOC = qtchooser -run-tool=moc -qt=5
CXXFLAGS    += -I/usr/include/qt5 -I/usr/include/x86_64-linux-gnu/qt5 -DHAVE_QT
LDFLAGS     += -lQt5Widgets -lQt5Gui -lQt5OpenGL -lQt5Core -lGL

ifdef USE_OPENMP
  CXXFLAGS  += -fopenmp -DHAVE_OPENMP
  LDFLAGS   += -fopenmp
endif

ifdef USE_GLEW
  CXXFLAGS  += -DHAVE_GLEW
  LDFLAGS   += -lGLEW
endif

ifdef USE_PNG
  CXXFLAGS  += -DHAVE_PNG
  LDFLAGS   += -lpng
endif

ifdef USE_JPEG
  CXXFLAGS  += -DHAVE_JPEG
  LDFLAGS   += -ljpeg
endif

ifdef USE_CUDA
  CXXFLAGS += -DHAVE_CUDA
  LDFLAGS  += -lcudart
  NVCC      = nvcc -x cu
  NVCCFLAGS = -G -g -Xcompiler -Wall -Xcompiler -Wextra -Xcompiler -fPIC -DHAVE_CUDA
  ifdef USE_OPENMP
    NVCCFLAGS += -Xcompiler -fopenmp -DHAVE_OPENMP
  endif
endif

CLANG_TIDY_CHECKS     = bugprone-*,cppcoreguidelines-*,misc-*,modernize-*,performance-*,readability-*
CLANG_TIDY_EXCEPTIONS += -bugprone-easily-swappable-parameters
CLANG_TIDY_EXCEPTIONS += -cppcoreguidelines-avoid-c-arrays
CLANG_TIDY_EXCEPTIONS += -cppcoreguidelines-avoid-magic-numbers
CLANG_TIDY_EXCEPTIONS += -cppcoreguidelines-pro-bounds-array-to-pointer-decay
CLANG_TIDY_EXCEPTIONS += -cppcoreguidelines-pro-bounds-constant-array-index
CLANG_TIDY_EXCEPTIONS += -cppcoreguidelines-pro-bounds-pointer-arithmetic
# False positives
CLANG_TIDY_EXCEPTIONS += -cppcoreguidelines-pro-type-member-init
CLANG_TIDY_EXCEPTIONS += -misc-const-correctness
CLANG_TIDY_EXCEPTIONS += -misc-unused-parameters
CLANG_TIDY_EXCEPTIONS += -misc-use-internal-linkage
CLANG_TIDY_EXCEPTIONS += -modernize-avoid-c-arrays
CLANG_TIDY_EXCEPTIONS += -modernize-use-trailing-return-type
CLANG_TIDY_EXCEPTIONS += -performance-avoid-endl
CLANG_TIDY_EXCEPTIONS += -readability-braces-around-statements
CLANG_TIDY_EXCEPTIONS += -readability-else-after-return
CLANG_TIDY_EXCEPTIONS += -readability-identifier-length
CLANG_TIDY_EXCEPTIONS += -readability-implicit-bool-conversion
CLANG_TIDY_EXCEPTIONS += -readability-magic-numbers
CLANG_TIDY_EXCEPTIONS += -readability-math-missing-parentheses
ifdef USE_GTEST
CLANG_TIDY_EXCEPTIONS += -cppcoreguidelines-avoid-non-const-global-variables
CLANG_TIDY_EXCEPTIONS += -cppcoreguidelines-owning-memory
CLANG_TIDY_EXCEPTIONS += -cppcoreguidelines-pro-type-vararg
CLANG_TIDY_EXCEPTIONS += -cppcoreguidelines-special-member-functions
endif

.SUFFIXES:

.SECONDARY:

.PHONY: default check clean

SOURCES_C = $(wildcard *.c)

SOURCES_CXX = $(wildcard *.cpp)

HEADERS = $(wildcard *.h)

CHECK_HEADERS = $(HEADERS)

TARGETS = \
  fraktal \

OBJECTS = \
  fraktal.cpp.o \
  glwidget.cpp.o \
  glwidget.moc.cpp.o \
  mainwindow.cpp.o \
  mainwindow.moc.cpp.o \
  renderer.cpp.o

ifdef USE_CUDA
  OBJECTS += compute.cu.o
else
  OBJECTS += compute.cpp.o
endif

QUIET = $(if $(quiet),@echo "  $1	$(if $2,$2,$@)";,)
VERY_QUIET = $(if $(quiet),@,)

NULL  =
SPACE = $(NULL) #
COMMA = ,

default: $(TARGETS)

all: $(TARGETS) check

fraktal: $(OBJECTS)
	$(call QUIET,LD)$(CXX) -o $@ $^ $(LDFLAGS)

%.c.o: %.c Makefile
	$(call QUIET,CC)$(CC) -o $@ -c $< $(CFLAGS) -MD -MF $@.d

%.cpp.o: %.cpp Makefile
	$(call QUIET,CXX)$(CXX) -o $@ -c $< $(CXXFLAGS) -MD -MF $@.d

%.cu.o: %.cpp Makefile
	$(call QUIET,NVCC)$(NVCC) -o $@ -c $< $(NVCCFLAGS)

%.moc.cpp: %.h
	$(QT_MOC) -o $@ $<

-include $(SOURCES_C:.c=.c.o.d)
-include $(SOURCES_CXX:.cpp=.cpp.o.d)

STANDALONE_HEADER_TEST_SOURCES := \
  $(addprefix test_standalone_header_, $(addsuffix .cpp, $(CHECK_HEADERS)))

MINMAX_HEADER_TEST_SOURCES := \
  $(addprefix test_minmax_header_, $(addsuffix .cpp, $(CHECK_HEADERS)))

$(STANDALONE_HEADER_TEST_SOURCES):test_standalone_header_%.cpp:
	@echo "#include <$*>" >$@

$(MINMAX_HEADER_TEST_SOURCES):test_minmax_header_%.cpp:
	@echo "#define min(a,b) dummy_min" > $@
	@echo "#define max(a,b) dummy_max" >>$@
	@echo "#include <$*>" >>$@

STANDALONE_HEADER_TEST_OBJECTS := $(STANDALONE_HEADER_TEST_SOURCES:.cpp=.o)

MINMAX_HEADER_TEST_OBJECTS := $(MINMAX_HEADER_TEST_SOURCES:.cpp=.o)

$(STANDALONE_HEADER_TEST_OBJECTS):%.o:%.cpp
	$(call QUIET,CXX)$(CXX) -o $@ -c $< $(CXXFLAGS) -I.
	$(call VERY_QUIET)rm -f $@ $<

$(MINMAX_HEADER_TEST_OBJECTS):%.o:%.cpp
	$(call QUIET,CXX)$(CXX) -o $@ -c $< $(CXXFLAGS) -I.
	$(call VERY_QUIET)rm -f $@ $<

check: default $(STANDALONE_HEADER_TEST_OBJECTS) $(MINMAX_HEADER_TEST_OBJECTS)

clean::
	rm -f test_standalone_header_* test_minmax_header_*

tidy:
	clang-tidy --checks=$(CLANG_TIDY_CHECKS),$(subst $(SPACE),$(COMMA),$(CLANG_TIDY_EXCEPTIONS)) \
	  $(filter-out %.moc.cpp, $(wildcard *.cpp)) -- \
	  -I. $(CXXFLAGS) -x cpp -std=c++20 >clang-tidy.out || /bin/true
	grep -Eo "\[[a-zA-Z0-9\.,-]+\]" clang-tidy.out | grep -- - | sort | uniq -c | sort -n -r

clean::
	rm -f clang-tidy.out

tarball:
	git archive --prefix=$(NAME)-$(VERSION)/ HEAD | bzip2 >$(NAME)-$(VERSION).tar.bz2

clean::
	rm -f $(NAME)-$(VERSION).tar.bz2

clean::
	rm -f *~ *.o *.d $(TARGETS) *.moc.cpp
