package gov.nih.nlm.nls.lvg.Flows;
import java.util.*;
import java.sql.*;
import gov.nih.nlm.nls.lvg.Lib.*;
import gov.nih.nlm.nls.lvg.Util.*;
import gov.nih.nlm.nls.lvg.Db.*;
import gov.nih.nlm.nls.lvg.Trie.*;
/*****************************************************************************
* This class provides features of generating derivational variants.  
* Derivational variants are terms which are somehow related to the original 
* term but do not share the same meaning.  Often, the derivational variant 
* changes syntactic category from the original term.  Derivational variants 
* are pre-computed and are put in Derivation table in Lvg database (facts).
* Derivations can also be generated by derivation rules through Lvg trie.
*
* <p><b>History:</b>
*
* @author NLM NLS Development Team
*
* @see <a href="../../../../../../../designDoc/UDF/flow/fd.html">
* Design Document </a>
*
* @version    V-2025
****************************************************************************/
public class ToDerivation extends Transformation implements Cloneable
{
    // public methods
    /**
    * Performs the mutation of this flow component.
    *
    * @param   in   a LexItem as the input for this flow component
    * @param   conn   LVG database connection
    * @param   trie   LVG persistent trie
    * @param   restrictFlag   a numberical flag to restrict out into LVG_ONLY
    * LVG_OR_ALL, or ALL (defined in LvgFlowSpecificOption).
    * @param   derivationType   a numberical flag to restrict derivation type
    * D_TYPE_ZERO, D_TYPE_PREFIX, D_TYPE_SUFFIX, D_TYPE_ZERO_PREFIX,
    * D_TYPE_ZERO_SUFFIX, D_TYPE_PREFIX_SUFFIX, D_TYPE_ALL (defined in 
    * LvgFlowSpecificOption);
    * @param   derivationNegation   a numberical flag to restrict derivation 
    * negation D_NEGATION_OTHERWISE, D_NEGATION_NEGATIVE, D_NEGATION_BOTH
    * @param   detailsFlag   a boolean flag for processing details information
    * @param   mutateFlag   a boolean flag for processing mutate information
    *
    * @return  the results from this flow component - a collection (Vector)
    * of LexItems
    *
    * @see DbBase
    * @see LvgFlowSpecificOption
    */
    public static Vector<LexItem> Mutate(LexItem in, Connection conn, 
        RamTrie trie, int restrictFlag, int derivationType, 
        int derivationNegation, boolean detailsFlag, boolean mutateFlag)
    {
        // Mutate: 
        Vector<LexItem> out = GetDerivations(in, conn, trie, restrictFlag, 
            derivationType, derivationNegation, INFO, detailsFlag, mutateFlag);
        return out;
    }
    /**
    * Performs the mutation of this flow component. Use D_TYPE_ALL for 
    * for derivation type and D_NEGATION_OTHERWISE for derivation negation flag.
    *
    * @param   in   a LexItem as the input for this flow component
    * @param   conn   LVG database connection
    * @param   trie   LVG persistent trie
    * @param   restrictFlag   a numberical flag to restrict out into LVG_ONLY
    * LVG_OR_ALL, or ALL (defined in LvgFlowSpecificOption).
    * @param   detailsFlag   a boolean flag for processing details information
    * @param   mutateFlag   a boolean flag for processing mutate information
    *
    * @return  A Vector of LexItem - results from this flow component
    *
    * @see DbBase
    * @see LvgFlowSpecificOption
    */
    public static Vector<LexItem> Mutate(LexItem in, Connection conn, 
        RamTrie trie, int restrictFlag, boolean detailsFlag, boolean mutateFlag)
    {
        // Mutate: 
        Vector<LexItem> out = GetDerivations(in, conn, trie, restrictFlag, 
            // TBD-2022
            LvgFlowSpecificOption.KDN_VALUE_DEFAULT, 
            LvgFlowSpecificOption.KDN_VALUE_DEFAULT,
            INFO, detailsFlag, mutateFlag);
        return out;
    }
    /**
    * A unit test driver for this flow component.
    *
    * @param args arguments
    */
    public static void main(String[] args)
    {
        // load config file
        Configuration conf = new Configuration("data.config.lvg", true);
        String testStr = GetTestStr(args, "help");      // get input String
        int minTermLen = Integer.parseInt(
            conf.GetConfiguration(Configuration.MIN_TERM_LENGTH));
        String lvgDir = conf.GetConfiguration(Configuration.LVG_DIR);
        int minTrieStemLength = Integer.parseInt(
            conf.GetConfiguration(Configuration.DIR_TRIE_STEM_LENGTH));
        // Mutate: connect to DB
        LexItem in = new LexItem(testStr, Category.ALL_BIT_VALUE, 
            Inflection.ALL_BIT_VALUE);
        Vector<LexItem> outs = new Vector<LexItem>();
        try
        {
            Connection conn = DbBase.OpenConnection(conf);
            boolean isInflection = false;
            RamTrie trie = new RamTrie(isInflection, minTermLen, lvgDir,
                minTrieStemLength);
            if(conn != null)
            {
                outs = ToDerivation.Mutate(in, conn, trie, 
                    LvgFlowSpecificOption.LVG_ONLY, true, true);
            }
            DbBase.CloseConnection(conn, conf);
        }
        catch (Exception e)
        {
            System.err.println(e.getMessage());
        }
        PrintResults(in, outs);     // print out results
    }
    // private methods
    /**
    * Get the derivational variants using both facts (database) and rules
    * (trie).  The implementation algorithm is:
    * <ul>
    * <li>Facts:
    *      <ol>
    *    <li> Performs a case insensitive search on the input term and term1 
    *     in the derivation table.
    *    <li> Performs a case insensitive search on the input term and term2 
    *     in the derivation table.
    *    <li> Assigns term and category for both source and target.
    *      </ol>
    * <li>Rules:
    *      <ol>
    *    <li> Uses persistent trie to apply rules (and check exceptions) on 
    *     the input term.
    *    <li> Assigns term and category for both source and target. 
    *      </ol>
    * <li>Display results according to the restriction filter.
    * <li>Sort the output by the frequency of categories.
    * </ul>
    *
    * @param   in   a LexItem as the input for this flow component
    * @param   conn   LVG database connection
    * @param   trie   LVG persistent trie
    * @param   restrictFlag   a numberical flag to restrict out into LVG_ONLY
    * LVG_OR_ALL, or ALL (defined in LvgFlowSpecificOption).
    * @param   derivationType P|S|Z for prefixD, suffixD, and zeroD
    * @param   derivationNegation N|O for negative and otherwise
    * @param   infoStr   the header of detail information, usually is the 
    * full name of the current flow
    * @param   detailsFlag    a boolean flag for processing details information
    * @param   mutateFlag    a boolean flag for processing mutate information
    *
    * @return  A Vector of LexItem - results from this flow component
    *
    * @see DbBase
    * @see LvgFlowSpecificOption
    */
    protected static Vector<LexItem> GetDerivations(LexItem in, Connection conn,
        RamTrie trie, int restrictFlag, int derivationType, 
        int derivationNegation, String infoStr, boolean detailsFlag, 
        boolean mutateFlag)
    {
        // init the input string and output Vector
        String inStr = in.GetSourceTerm();
        long inCat = in.GetSourceCategory().GetValue();
        long inInfl = in.GetSourceInflection().GetValue();
        Vector<LexItem> outs = new Vector<LexItem>();
        try
        {
            // Fact: get derivation from database
            Vector<DerivationRecord> factList 
                = DbDerivation.GetDerivationsByOption(
                inStr, conn, derivationType, derivationNegation);
            Vector<LexItem> facts = new Vector<LexItem>();    
            // update LexItems
            for(int i = 0; i < factList.size(); i++)
            {
                DerivationRecord record = factList.elementAt(i);
                String term = record.GetTarget();
                long curCat = record.GetSourceCat();
                // input filter for category; 
                // inflection is not in the database table, can't be checked
                if(InputFilter.IsLegal(inCat, curCat) == false)
                {
                    continue;
                }
                // details & mutate
                String details = null;
                String mutate = null;
                if(detailsFlag == true)
                {
                    details = infoStr + " (FACT)";
                }
                if(mutateFlag == true)
                {
                    String fs 
                        = GlobalBehavior.GetInstance().GetFieldSeparator();
                    mutate = "FACT" + fs + record.GetString(fs);
                }
                LexItem temp = UpdateLexItem(in, term, Flow.DERIVATION,
                    record.GetTargetCat(), 
                    Inflection.GetBitValue(Inflection.BASE_BIT), 
                    details, mutate);
                facts.addElement(temp);
            }
            // Rule: rule generated derivations
            Vector<LexItem> rules = new Vector<LexItem>();    
            if((restrictFlag == LvgFlowSpecificOption.ALL)
            || ((restrictFlag == LvgFlowSpecificOption.LVG_OR_ALL)
             && (facts.size() == 0)))
            {
                // Rule: Use trie to get the result from rule
                Vector<RuleResult> ruleList = 
                    trie.GetDerivationsByRules(inStr, inCat, inInfl, true);
                // update LexItems
                for(int i = 0; i < ruleList.size(); i++)
                {
                    RuleResult record = ruleList.elementAt(i);
                    String term = record.GetOutTerm();
                    // heuristic rules: check if derivation in Lexicon
                    // the rule generated derivatin must not in Lexicon
                    if(DbUninflection.IsExistUninflectedTerm(term, conn) 
                        == false)
                    {
                        // details & mutate
                        String details = null;
                        String mutate = null;
                        if(detailsFlag == true)
                        {
                            details = infoStr + " (RULE"
                                + GlobalBehavior.GetInstance().GetFieldSeparator()
                                + record.GetRuleString() + ")";
                        }
                        if(mutateFlag == true)
                        {
                            String fs 
                            = GlobalBehavior.GetInstance().GetFieldSeparator();
                            mutate = "RULE" + fs + record.GetRuleString() 
                                + fs;
                        }
                        LexItem temp = UpdateLexItem(in, term, Flow.DERIVATION,
                            Category.ToValue(record.GetOutCategory()),
                            Inflection.ToValue(record.GetOutInflection()), 
                            details, mutate);
                    
                        rules.addElement(temp);
                    }
                }
            }
            // form output by the restrict flag
            outs = LvgFlowSpecificOption.RestrictOption(facts, rules, 
                restrictFlag);
        }
        catch (SQLException e) 
        { 
            System.err.println("** Error: Sql Exception in ToDerivation Flow.");
        }
        return outs;
    }
    // protected methods
    // data members
    private final static String INFO = "Derivation";
}
