/*
 * TraitGuesser.java
 *
 * Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
 *
 * This file is part of BEAST.
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership and licensing.
 *
 * BEAST is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 *  BEAST 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with BEAST; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

package dr.app.beauti.options;

import dr.app.beauti.traitspanel.GuessTraitException;
import dr.evolution.util.TaxonList;

import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Andrew Rambaut
 * @author Walter Xie
 */
public class TraitGuesser implements Serializable {

    private static final long serialVersionUID = 1786080930502001515L;

    private final TraitData traitData;

    public TraitGuesser(TraitData traitData) {
        this.traitData = traitData;
    }

    public static enum GuessType {
        DELIMITER,
        REGEX
    }

    private GuessType guessType = GuessType.DELIMITER;

    private int order = 0;
    private String delimiter;
    private String regex;

    private static final String REGEX_CHARACTERS = "|[].*()-^$";

    ////////////////////////////////////////////////////////////////

    public TraitData getTraitData() {
        return traitData;
    }

    public GuessType getGuessType() {
        return guessType;
    }

    public void setGuessType(GuessType guessType) {
        this.guessType = guessType;
    }

    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    public String getDelimiter() {
        return delimiter;
    }

    public void setDelimiter(String delimiter) {
        this.delimiter = delimiter;
    }

    public String getRegex() {
        return regex;
    }

    public void setRegex(String regex) {
        this.regex = regex;
    }

    ////////////////////////////////////////////////////////////////
    public void guessTrait(TaxonList taxa) {

        for (int i = 0; i < taxa.getTaxonCount(); i++) {

            String value = null;

            try {
                switch (guessType) {
                    case DELIMITER:
                        value = guessTraitByDelimiter(taxa.getTaxonId(i), delimiter);
                        break;
                    case REGEX:
                        value = guessTraitFromRegex(taxa.getTaxonId(i), regex);
                        break;
                    default:
                        throw new IllegalArgumentException("unknown GuessType");
                }

            } catch (GuessTraitException gfe) {
                //
            }

            taxa.getTaxon(i).setAttribute(traitData.getName(), value);
        }
    }

    private String guessTraitByDelimiter(String label, String delimiter) throws GuessTraitException {

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < delimiter.length(); i++) {
            if (REGEX_CHARACTERS.contains("" + delimiter.charAt(i))) {
                sb.append("\\").append(delimiter.charAt(i));
            } else {
                sb.append(delimiter.charAt(i));
            }
        }

        if (sb.toString().length() < 1) {
            throw new IllegalArgumentException("No delimiter");
        }

        String[] tokens = label.split(sb.toString());

        if (tokens.length < 2) {
            throw new IllegalArgumentException("Can not find delimiter in taxon label (" + label + ")\n or invalid delimiter (" + delimiter + ").");
        }

        if (order >= 0) {
            if (order >= tokens.length) {
                throw new IllegalArgumentException("Insufficent delimiters in taxon lable (" + label + ")\n to find requested field.");
            }

            return tokens[order];
        } else {
            if (tokens.length + order < 0) {
                throw new IllegalArgumentException("Insufficent delimiters in taxon lable (" + label + ")\n to find requested field.");
            }

            return tokens[tokens.length + order];
        }
    }

    private String guessTraitFromRegex(String label, String regex) throws GuessTraitException {
        String t;

        if (!regex.contains("(")) {
            // if user hasn't specified a replace element, assume the whole regex should match
            regex = "(" + regex + ")";
        }

        try {
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(label);
            if (!matcher.find()) {
                throw new GuessTraitException("Regular expression doesn't find a match in taxon label, " + label);
            }

            if (matcher.groupCount() < 1) {
                throw new GuessTraitException("Trait value group not defined in regular expression");
            }

            t = matcher.group(1); // TODO: not working?
        } catch (NumberFormatException nfe) {
            throw new GuessTraitException("Badly formatted trait value in taxon label, " + label);
        }

        return t;
    }
}
