# Requires GNU Make >= 4.3 (grouped targets &:) — use gmake on macOS
BINARY  := ../src/target/release/obikmer
VENV_PY := ../.venv/bin/python3

# All generated/downloaded artifacts live under RUN/ so the whole tree can be
# gitignored with a single entry (benchmark/run/) — see benchmark/README.md.
RUN := run

GENOMES := $(wildcard $(RUN)/genomes/*.fna.gz)

# SPECIMENS, SPECIES, and the full dependency graph are generated by
# make_deps.py from the genome FASTA headers — like .d files in C.
# Make rebuilds deps.mk whenever genomes/ changes and restarts.
-include deps.mk

REF_NPZS              := $(SPECIMENS:%=$(RUN)/reference_index/%.npz)
REF_DIST_CSVS         := $(addprefix $(RUN)/reference_dist/, \
	shared_kmers.csv hamming_dist.csv jaccard_dist.csv \
	bray_curtis_dist.csv relfreq_bray_curtis_dist.csv \
	euclidean_dist.csv relfreq_euclidean_dist.csv \
	hellinger_dist.csv hellinger_euclidean_dist.csv)
OBIKMER_PRESENCE_DIST := $(addprefix $(RUN)/obikmer_dist/presence/, \
	jaccard_dist.csv jaccard_shared.csv jaccard_nj.nwk \
	hamming_dist.csv hamming_nj.nwk)
OBIKMER_COUNT_DIST    := $(addprefix $(RUN)/obikmer_dist/count/, \
	jaccard_dist.csv jaccard_shared.csv jaccard_nj.nwk \
	bray_curtis_dist.csv bray_curtis_nj.nwk \
	relfreq_bray_curtis_dist.csv relfreq_bray_curtis_nj.nwk \
	euclidean_dist.csv euclidean_nj.nwk \
	relfreq_euclidean_dist.csv relfreq_euclidean_nj.nwk \
	hellinger_dist.csv hellinger_nj.nwk \
	hellinger_euclidean_dist.csv hellinger_euclidean_nj.nwk)
DIST_COMPARISON       := $(RUN)/stats/dist_comparison/summary.csv
PRESENCE_DONE         := $(SPECIMENS:%=$(RUN)/specimen_index_presence/%/index.done)
PRESENCE_STATS        := $(SPECIMENS:%=$(RUN)/stats/indexing_presence/%.stats)
COUNT_DONE            := $(SPECIMENS:%=$(RUN)/specimen_index_count/%/index.done)
COUNT_STATS           := $(SPECIMENS:%=$(RUN)/stats/indexing_count/%.stats)
VERIFY_PRESENCE_STATS := $(SPECIMENS:%=$(RUN)/stats/verify_presence/%.stats)
VERIFY_COUNT_STATS    := $(SPECIMENS:%=$(RUN)/stats/verify_count/%.stats)
SPECIFIC_PRESENCE_DONE  := $(SPECIES:%=$(RUN)/specific_index_presence/%/index.done)
SPECIFIC_PRESENCE_STATS := $(SPECIES:%=$(RUN)/stats/specific_kmer_presence/%.stats)
SPECIFIC_COUNT_DONE     := $(SPECIES:%=$(RUN)/specific_index_count/%/index.done)
SPECIFIC_COUNT_STATS    := $(SPECIES:%=$(RUN)/stats/specific_kmer_count/%.stats)
SIMULATED_READS := $(foreach s,$(SPECIMENS),$(RUN)/simulated_data/$(subst --,/,$s)/reads_R1.fastq.gz)
QUERY_READS      := $(foreach s,$(QUERY_SPECIMENS),$(RUN)/query_data/$(subst --,/,$s)/reads_R1.fastq.gz)
QUERY_PRESENCE_DENSE_DONE   := $(QUERY_SPECIMENS:%=$(RUN)/query_presence_dense/%.fasta.gz)
QUERY_PRESENCE_DENSE_STATS  := $(QUERY_SPECIMENS:%=$(RUN)/stats/query_presence_dense/%.stats)
QUERY_PRESENCE_SPARSE_DONE  := $(QUERY_SPECIMENS:%=$(RUN)/query_presence_sparse/%.fasta.gz)
QUERY_PRESENCE_SPARSE_STATS := $(QUERY_SPECIMENS:%=$(RUN)/stats/query_presence_sparse/%.stats)
QUERY_COUNT_DENSE_DONE      := $(QUERY_SPECIMENS:%=$(RUN)/query_count_dense/%.fasta.gz)
QUERY_COUNT_DENSE_STATS     := $(QUERY_SPECIMENS:%=$(RUN)/stats/query_count_dense/%.stats)
QUERY_COUNT_SPARSE_DONE     := $(QUERY_SPECIMENS:%=$(RUN)/query_count_sparse/%.fasta.gz)
QUERY_COUNT_SPARSE_STATS    := $(QUERY_SPECIMENS:%=$(RUN)/stats/query_count_sparse/%.stats)
VERIFY_QUERY_STATS := $(QUERY_SPECIMENS:%=$(RUN)/stats/verify_query/%.stats)

.NOTPARALLEL:

.PHONY: all simulate reference reference_dist \
        obikmer_dist obikmer_dist_presence obikmer_dist_count \
        dist_comparison \
        index_presence index_count \
        aggregate_index_presence aggregate_index_count \
        merge_presence merge_count \
        verify_presence verify_count \
        aggregate_verify_presence aggregate_verify_count \
        verify_merge_presence verify_merge_count \
        filter_presence filter_count \
        aggregate_filter_presence aggregate_filter_count \
        pack_dense_presence pack_dense_count simulate_query \
        query_presence_dense query_presence_sparse \
        query_count_dense query_count_sparse \
        aggregate_query_presence_dense aggregate_query_presence_sparse \
        aggregate_query_count_dense aggregate_query_count_sparse \
        verify_query aggregate_verify_query

verify_merge_presence: $(RUN)/stats/verify_merge_presence/current.csv
verify_merge_count:    $(RUN)/stats/verify_merge_count/current.csv

all: aggregate_verify_presence aggregate_verify_count \
     verify_merge_presence verify_merge_count \
     aggregate_filter_presence aggregate_filter_count \
     dist_comparison \
     aggregate_query_presence_dense aggregate_query_presence_sparse \
     aggregate_query_count_dense aggregate_query_count_sparse \
     aggregate_verify_query

# ── dependency file ───────────────────────────────────────────────────────────

deps.mk: $(GENOMES) make_deps.py
	$(VENV_PY) make_deps.py $(GENOMES) > $@

# ── simulation ────────────────────────────────────────────────────────────────
# Prerequisites (genome → reads) are in deps.mk; $< is the genome file.

$(SIMULATED_READS):
	bash simulate_one.sh $< $(dir $@)

simulate: $(SIMULATED_READS)

# ── query read simulation (fixed size, independent draw) ───────────────────────
# Prerequisites (genome → reads) are in deps.mk; $< is the genome file.

$(QUERY_READS):
	bash simulate_query_one.sh $< $(dir $@)

simulate_query: $(QUERY_READS)

# ── reference kmer sets ───────────────────────────────────────────────────────
# Prerequisites (reads → npz) are in deps.mk.

$(RUN)/reference_index/%.npz:
	bash build_reference.sh $*

reference: $(REF_NPZS)

# ── reference distance matrices ───────────────────────────────────────────────

$(REF_DIST_CSVS) &: $(REF_NPZS) build_reference_dist.py
	$(VENV_PY) build_reference_dist.py \
		--ref-dir $(RUN)/reference_index --out-dir $(RUN)/reference_dist

reference_dist: $(REF_DIST_CSVS)

# ── obikmer phylo (presence index) ──────────────────────────────────────────

$(OBIKMER_PRESENCE_DIST) &: $(RUN)/global_index_presence/index.done $(BINARY)
	mkdir -p $(RUN)/obikmer_dist/presence
	$(BINARY) phylo \
		--output $(RUN)/obikmer_dist/presence/jaccard \
		--distance jaccard --csv --shared-kmers --nj \
		$(RUN)/global_index_presence
	$(BINARY) phylo \
		--output $(RUN)/obikmer_dist/presence/hamming \
		--distance hamming --csv --nj \
		$(RUN)/global_index_presence

obikmer_dist_presence: $(OBIKMER_PRESENCE_DIST)

# ── obikmer phylo (count index) ─────────────────────────────────────────────

$(OBIKMER_COUNT_DIST) &: $(RUN)/global_index_count/index.done $(BINARY)
	mkdir -p $(RUN)/obikmer_dist/count
	$(BINARY) phylo \
		--output $(RUN)/obikmer_dist/count/jaccard \
		--distance jaccard --csv --shared-kmers --nj \
		$(RUN)/global_index_count
	$(BINARY) phylo \
		--output $(RUN)/obikmer_dist/count/bray_curtis \
		--distance bray-curtis --csv --nj \
		$(RUN)/global_index_count
	$(BINARY) phylo \
		--output $(RUN)/obikmer_dist/count/relfreq_bray_curtis \
		--distance relfreq-bray-curtis --csv --nj \
		$(RUN)/global_index_count
	$(BINARY) phylo \
		--output $(RUN)/obikmer_dist/count/euclidean \
		--distance euclidean --csv --nj \
		$(RUN)/global_index_count
	$(BINARY) phylo \
		--output $(RUN)/obikmer_dist/count/relfreq_euclidean \
		--distance relfreq-euclidean --csv --nj \
		$(RUN)/global_index_count
	$(BINARY) phylo \
		--output $(RUN)/obikmer_dist/count/hellinger \
		--distance hellinger --csv --nj \
		$(RUN)/global_index_count
	$(BINARY) phylo \
		--output $(RUN)/obikmer_dist/count/hellinger_euclidean \
		--distance hellinger-euclidean --csv --nj \
		$(RUN)/global_index_count

obikmer_dist_count: $(OBIKMER_COUNT_DIST)

obikmer_dist: obikmer_dist_presence obikmer_dist_count

# ── distance comparison ───────────────────────────────────────────────────────

$(DIST_COMPARISON): $(REF_DIST_CSVS) $(OBIKMER_PRESENCE_DIST) $(OBIKMER_COUNT_DIST) compare_all_dist.py
	$(VENV_PY) compare_all_dist.py --run-dir $(RUN) --out $(DIST_COMPARISON)

dist_comparison: $(DIST_COMPARISON)

# ── per-specimen indexing ─────────────────────────────────────────────────────
# Prerequisites (reads → index.done + .stats) are in deps.mk.

$(RUN)/specimen_index_presence/%/index.done \
$(RUN)/stats/indexing_presence/%.stats &: $(BINARY)
	bash index_one_presence.sh $*

$(RUN)/specimen_index_count/%/index.done \
$(RUN)/stats/indexing_count/%.stats &: $(BINARY)
	bash index_one_count.sh $*

index_presence: $(PRESENCE_DONE)
index_count:    $(COUNT_DONE)

# ── indexing stats aggregation ────────────────────────────────────────────────

aggregate_index_presence: $(PRESENCE_STATS)
	bash aggregate_stats.sh indexing_presence

aggregate_index_count: $(COUNT_STATS)
	bash aggregate_stats.sh indexing_count

# ── global merge ──────────────────────────────────────────────────────────────

$(RUN)/global_index_presence/index.done: $(PRESENCE_DONE) $(BINARY)
	bash merge_presence.sh

$(RUN)/global_index_count/index.done: $(COUNT_DONE) $(BINARY)
	bash merge_count.sh

merge_presence: $(RUN)/global_index_presence/index.done
merge_count:    $(RUN)/global_index_count/index.done

# ── per-specimen verification ─────────────────────────────────────────────────
# Prerequisites (index.done + npz → .stats) are in deps.mk.

$(RUN)/stats/verify_presence/%.stats:
	bash verify_one_presence.sh $*

$(RUN)/stats/verify_count/%.stats:
	bash verify_one_count.sh $*

verify_presence: $(VERIFY_PRESENCE_STATS)
verify_count:    $(VERIFY_COUNT_STATS)

# ── verification stats aggregation ───────────────────────────────────────────

aggregate_verify_presence: $(VERIFY_PRESENCE_STATS)
	bash aggregate_stats.sh verify_presence

aggregate_verify_count: $(VERIFY_COUNT_STATS)
	bash aggregate_stats.sh verify_count

# ── species-specific indexes ──────────────────────────────────────────────────
# Prerequisites (global index → specific index) are in deps.mk.

$(RUN)/specific_index_presence/%/index.done \
$(RUN)/stats/specific_kmer_presence/%.stats &: $(BINARY)
	bash filter_one_presence.sh $*

$(RUN)/specific_index_count/%/index.done \
$(RUN)/stats/specific_kmer_count/%.stats &: $(BINARY)
	bash filter_one_count.sh $*

filter_presence: $(SPECIFIC_PRESENCE_DONE)
filter_count:    $(SPECIFIC_COUNT_DONE)

aggregate_filter_presence: $(SPECIFIC_PRESENCE_STATS)
	bash aggregate_stats.sh specific_kmer_presence

aggregate_filter_count: $(SPECIFIC_COUNT_STATS)
	bash aggregate_stats.sh specific_kmer_count

# ── merged index verification ─────────────────────────────────────────────────

$(RUN)/stats/verify_merge_presence/current.csv: $(REF_NPZS) $(RUN)/global_index_presence/index.done
	bash verify_merge_presence.sh

$(RUN)/stats/verify_merge_count/current.csv: $(REF_NPZS) $(RUN)/global_index_count/index.done
	bash verify_merge_count.sh

# ── dense variants (query benchmark) ────────────────────────────────────────────
# `merge` packs sparse by default (2026-08-28) — global_index_presence/
# global_index_count *are* the sparse variants already; the dense ones are
# built explicitly here, from a hard-link-based copy (see
# copy_index_hardlink.sh) rather than a full `cp -r`.

$(RUN)/global_index_presence_dense/index.done: $(RUN)/global_index_presence/index.done $(BINARY)
	bash pack_dense.sh presence

# Rebuilt from the per-specimen count sources directly (via `merge --dense`),
# not repacked from global_index_count — see pack_dense.sh's own comment.
$(RUN)/global_index_count_dense/index.done: $(COUNT_DONE) $(BINARY)
	bash pack_dense.sh count

pack_dense_presence: $(RUN)/global_index_presence_dense/index.done
pack_dense_count:    $(RUN)/global_index_count_dense/index.done

# ── query: dense vs sparse, presence and count ──────────────────────────────────
# Prerequisites (reads + index → output + .stats) are in deps.mk.

$(RUN)/query_presence_dense/%.fasta.gz \
$(RUN)/stats/query_presence_dense/%.stats &: $(BINARY) $(RUN)/global_index_presence_dense/index.done
	bash query_one.sh presence dense $*

$(RUN)/query_presence_sparse/%.fasta.gz \
$(RUN)/stats/query_presence_sparse/%.stats &: $(BINARY) $(RUN)/global_index_presence/index.done
	bash query_one.sh presence sparse $*

$(RUN)/query_count_dense/%.fasta.gz \
$(RUN)/stats/query_count_dense/%.stats &: $(BINARY) $(RUN)/global_index_count_dense/index.done
	bash query_one.sh count dense $*

$(RUN)/query_count_sparse/%.fasta.gz \
$(RUN)/stats/query_count_sparse/%.stats &: $(BINARY) $(RUN)/global_index_count/index.done
	bash query_one.sh count sparse $*

query_presence_dense:  $(QUERY_PRESENCE_DENSE_DONE)
query_presence_sparse: $(QUERY_PRESENCE_SPARSE_DONE)
query_count_dense:     $(QUERY_COUNT_DENSE_DONE)
query_count_sparse:    $(QUERY_COUNT_SPARSE_DONE)

aggregate_query_presence_dense: $(QUERY_PRESENCE_DENSE_STATS)
	bash aggregate_stats.sh query_presence_dense

aggregate_query_presence_sparse: $(QUERY_PRESENCE_SPARSE_STATS)
	bash aggregate_stats.sh query_presence_sparse

aggregate_query_count_dense: $(QUERY_COUNT_DENSE_STATS)
	bash aggregate_stats.sh query_count_dense

aggregate_query_count_sparse: $(QUERY_COUNT_SPARSE_STATS)
	bash aggregate_stats.sh query_count_sparse

# ── query: dense/sparse regression ──────────────────────────────────────────────

$(RUN)/stats/verify_query/%.stats:
	bash verify_query_one.sh $*

verify_query: $(VERIFY_QUERY_STATS)

aggregate_verify_query: $(VERIFY_QUERY_STATS)
	bash aggregate_stats.sh verify_query
