package obitax

import (
	"fmt"
	"log"
)

type TaxName struct {
	name      *string
	nameclass *string
}

type Taxonomy struct {
	nodes *TaxonSet
	alias map[int]*TaxNode
	index map[string]*TaxonSet
}

func NewTaxonomy() *Taxonomy {
	set := make(TaxonSet)
	taxonomy := Taxonomy{
		nodes: &set,
		alias: make(TaxonSet),
		index: make(map[string]*TaxonSet)}
	return &taxonomy
}

func (taxonomy *Taxonomy) TaxonSet() *TaxonSet {
	return taxonomy.nodes
}

func (taxonomy *Taxonomy) Alias() *map[int]*TaxNode {
	return &(taxonomy.alias)
}

func (taxonomy *Taxonomy) Index() *map[string]*TaxonSet {
	return &(taxonomy.index)
}

func (taxonomy *Taxonomy) Length() int {
	return len(*taxonomy.nodes)
}

func (taxonomy *Taxonomy) AddNewTaxa(taxid, parent int, rank string, replace bool, init bool) (*TaxNode, error) {
	if !replace {
		_, ok := (*taxonomy.nodes)[taxid]
		if ok {
			return nil, fmt.Errorf("trying to add taxoon %d already present in the taxonomy", taxid)
		}
	}

	n := NewTaxNode(taxid, parent, rank)
	(*taxonomy.nodes)[taxid] = n

	return n, nil
}

func (taxonomy *Taxonomy) Taxon(taxid int) (*TaxNode, error) {
	t, ok := (*taxonomy.nodes)[taxid]

	if !ok {
		a, aok := taxonomy.alias[taxid]
		if !aok {
			return nil, fmt.Errorf("Taxid %d is not part of the taxonomy", taxid)
		}
		log.Printf("Taxid %d is deprecated and must be replaced by %d", taxid, a.taxid)
		t = a
	}
	return t, nil
}

func (taxonomy *Taxonomy) AddNewName(taxid int, name, nameclass *string) error {
	node, node_err := taxonomy.Taxon(taxid)
	if node_err != nil {
		return node_err
	}

	if *nameclass == "scientific name" {
		node.scientificname = name
	} else {
		names := node.alternatenames
		if names == nil {
			n := make(map[string]*string)
			names = &n
			node.alternatenames = names
		} else {
			(*names)[*name] = nameclass
		}
	}

	i, ok := taxonomy.index[*name]
	if !ok {
		tnm := make(TaxonSet)
		i = &tnm
		taxonomy.index[*name] = i
	}
	(*i)[taxid] = node

	return nil
}

func (taxonomy *Taxonomy) ReindexParent() error {
	var ok bool
	for _, taxon := range *taxonomy.nodes {
		taxon.pparent, ok = (*taxonomy.nodes)[taxon.parent]
		if !ok {
			return fmt.Errorf("Parent %d of taxon %d is not defined in taxonomy",
				taxon.taxid,
				taxon.parent)
		}
	}

	return nil
}

func MakeTaxName(name, nameclass *string) *TaxName {
	tn := TaxName{name, nameclass}
	return &tn
}

func (taxonomy *Taxonomy) AddNewAlias(newtaxid, oldtaxid int) error {
	n, node_err := taxonomy.Taxon(newtaxid)
	if node_err != nil {
		return node_err
	}

	taxonomy.alias[oldtaxid] = n

	return nil
}
