001package squidpony;
002
003import squidpony.squidmath.GapShuffler;
004import squidpony.squidmath.StatefulRNG;
005
006/**
007 * Combines {@link Messaging} with {@link Thesaurus} and optionally {@link NaturalLanguageCipher} to make variations on
008 * a sentence structure.
009 * <br>
010 * Created by Tommy Ettinger on 11/20/2017.
011 */
012public class ProceduralMessaging {
013    /**
014     * Data class that stores a name String and one or more Strings that may be used as part of a title with that name,
015     * typically using categories from {@link Thesaurus} to add variety.
016     */
017    public static class AssociatedName {
018        public String name;
019        public Messaging.NounTrait pronoun;
020        public GapShuffler<String> themes, titles;
021        public StatefulRNG srng;
022
023        /**
024         * Creates an AssociatedName with the being's name as a String and any associated themes and titles as String
025         * arrays, with a boolean after the name that determines whether the name should be "translated" using a
026         * NaturalLanguageCipher to some other form. If you gave this:
027         * <code>"Brunhilda", true, new String[]{"ice`noun`"}, "Goddess`noun` of Ice`nouns`", "Winter-Empress`noun`", "Heroine`noun` of the North"</code>,
028         * it could use any of the terms in {@link Thesaurus} associated with the category {@code "ice`noun`} as themes,
029         * could generate titles like "Mother of Blizzards", "Winter-Queen", and "Maiden of the North", and would not
030         * actually show the name "Brunhilda" in use, instead producing some similar-length name using the
031         * NaturalLanguageCipher that a ProceduralMessaging is created with (defaulting to generic fantasy names).
032         * This overload always treats the being as if it is being addressed directly, in second-person singular form.
033         * @param name the String name for the being, which will be changed if {@code cipherName} is true
034         * @param cipherName if true, the name will be changed using a NaturalLanguageCipher before being shown
035         * @param themes a String array (which may be null) of words that may appear more often regarding this being
036         * @param titles a String array or vararg (which should probably not be null) of special titles for the being
037         */
038        public AssociatedName(String name, boolean cipherName, String[] themes, String... titles)
039        {
040            this.name = cipherName ? "[?]" + name + "[?]" : name;
041            this.pronoun = Messaging.NounTrait.SECOND_PERSON_SINGULAR;
042            srng = new StatefulRNG(name);
043            this.themes = (themes == null) ? null : new GapShuffler<String>(themes, srng);
044            this.titles = (titles == null) ? null : new GapShuffler<String>(titles, srng);
045        }
046        /**
047         * Creates an AssociatedName with the being's name as a String and any associated themes and titles as String
048         * arrays, with a boolean after the name that determines whether the name should be "translated" using a
049         * NaturalLanguageCipher to some other form. If you gave this:
050         * <code>"Brunhilda", true, new String[]{"ice`noun`"}, "Goddess`noun` of Ice`nouns`", "Winter-Empress`noun`", "Heroine`noun` of the North"</code>,
051         * it could use any of the terms in {@link Thesaurus} associated with the category {@code "ice`noun`} as themes,
052         * could generate titles like "Mother of Blizzards", "Winter-Queen", and "Maiden of the North", and would not
053         * actually show the name "Brunhilda" in use, instead producing some similar-length name using the
054         * NaturalLanguageCipher that a ProceduralMessaging is created with (defaulting to generic fantasy names).
055         * This overload allows the {@link Messaging.NounTrait} to be specified, which allows various ways of addressing
056         * the being (first person, second person, or third person; singular or plural; various gender options in the
057         * third person).
058         * @param name the String name for the being, which will be changed if {@code cipherName} is true
059         * @param cipherName if true, the name will be changed using a NaturalLanguageCipher before being shown
060         * @param pronoun a NounTrait enum that designates how this being should be addressed (often second person singular, but not always)
061         * @param themes a String array (which may be null) of words that may appear more often regarding this being
062         * @param titles a String array or vararg (which should probably not be null) of special titles for the being
063         */
064        public AssociatedName(String name, boolean cipherName, Messaging.NounTrait pronoun, String[] themes, String... titles) {
065            this.name = cipherName ? "[?]" + name + "[?]" : name;
066            this.pronoun = pronoun;
067            srng = new StatefulRNG(name);
068            this.themes = (themes == null) ? null : new GapShuffler<String>(themes, srng);
069            this.titles = (titles == null) ? null : new GapShuffler<String>(titles, srng);
070        }
071    }
072    public Thesaurus thesaurus;
073    public NaturalLanguageCipher language;
074
075    public ProceduralMessaging() {
076        thesaurus = new Thesaurus();
077        language = new NaturalLanguageCipher(FakeLanguageGen.FANTASY_NAME);
078    }
079    public ProceduralMessaging(long seed)
080    {
081        thesaurus = new Thesaurus(seed);
082        language = new NaturalLanguageCipher(FakeLanguageGen.FANTASY_NAME); 
083    }
084    public ProceduralMessaging(long seed, FakeLanguageGen nameLanguage)
085    {
086        thesaurus = new Thesaurus(seed);
087        language = new NaturalLanguageCipher(nameLanguage == null ? FakeLanguageGen.FANTASY_NAME : nameLanguage);
088    }
089    public ProceduralMessaging(Thesaurus existingThesaurus, NaturalLanguageCipher existingLanguage)
090    {
091        thesaurus = (existingThesaurus == null)
092                ? new Thesaurus()
093                : existingThesaurus;
094        language = (existingLanguage == null)
095                ? new NaturalLanguageCipher(FakeLanguageGen.FANTASY_NAME)
096                : existingLanguage;
097    }
098    public String transform(CharSequence message, String userName, Messaging.NounTrait userTrait, String targetName, Messaging.NounTrait targetTrait)
099    {
100        return language.cipherMarkup(thesaurus.process(Messaging.transform(message, userName, userTrait, targetName, targetTrait)));
101    }
102    public String transform(CharSequence message, AssociatedName user, String targetName, Messaging.NounTrait targetTrait)
103    {
104        return language.cipherMarkup(thesaurus.process(Messaging.transform(message, user.name, user.pronoun, targetName, targetTrait,
105                (user.titles == null) ? "The Great" : user.titles.next(), (user.themes == null) ? "uncertainty" : user.themes.next())));
106    }
107    public String transform(CharSequence message, String userName, Messaging.NounTrait userTrait, AssociatedName target)
108    {
109        return language.cipherMarkup(thesaurus.process(Messaging.transform(message, userName, userTrait, target.name, target.pronoun,
110                (target.titles == null) ? "The Great" : target.titles.next(), (target.themes == null) ? "uncertainty" : target.themes.next())));
111    }
112}