//*** EXPERIMENTATION CODE ***
/*
Online Java - IDE, Code Editor, Compiler
Online Java is a quick and easy tool that helps you to build, compile, test your programs
online.
*/
// This has been created to ensure I can utilize any random functions more efficiently.
// It is a creation of the permutations (with replacement) calculator.


// All done, see documentation
//Since there are not multiple instantiations similar to combinatins when r varied,
//I can safely reduce all variables without static

//all SET variables, cycles....these need to become Static variables.
//the rest can remain non-static
//values associated with storing the finished encoding => mappings into the final 

//in hindsight, perhaps did not need to even go through this process of waiting for first mapping??


//***CHANGES REQUIRED *******************************
//get rid of logic that it has to get L first time on sj2. This can be prepopulated
//everytime a valid mappping for secondScenarioMapping, 

//at the moment it is filing sj based on getting A=1 first and then any that appear
//cam just prep

//SEEMS LIKE A WASTE OF TIME THE CODE
//IT WILL ALREADY TAKE CARE OF THE SELECTION FROM possibleLetters!!!!!!!!!!
//ALL that was needed for preset first letter.......


import java.math.*;
import java.util.*;

class Staircase
{
    int backupFirstTemp1;
    int backupSecondTemp1;
    int temp1;
    String mapping;
    String possibleMappingFirst;
    String possibleMappingSecond;
    long permutations;
    StringJoiner sj;
    StringJoiner sj1;
    int num;
    int r;
    
    static int cycles=0;
    static int totalcycles=0;
    static int difference = 0;
    
    static List <String> backupValuesSet = new ArrayList<>();
    
    String originalString;
    Map<Integer,String> possibleLettersMap;
    Map<Integer, String> mp;
    Map<Character,Integer> mpReverse;
    static Set<String> st = new HashSet<>();
    Random rand = new Random();
    static String[] valuesSet; 
    static String []backupValuesSetBeforeModification;
    static int subsetEntry=1;
    
    
    public Staircase(long permutations, Map<Integer,String> mp, Map<Integer,String> possibleLettersMap, int r, String possibleMappingFirst, String possibleMappingSecond, String originalString, Map<Character, Integer> mpReverse)
    {
        this.originalString=originalString;
        this.permutations=permutations;
        this.mp=mp;
        this.possibleLettersMap=possibleLettersMap;
        //this.rMin=rMin;
        //this.rMax=rMax;
        this.possibleMappingFirst=possibleMappingFirst;
        this.possibleMappingSecond=possibleMappingSecond;
        this.r=r;
        
        sj  = new StringJoiner("");
        sj1 = new StringJoiner("");
        boolean invalidIndex=false;
        
        int currentSetSize=0;
        int newSetSize=0;
        int subsetNumber=1;
        int steps;
        String subsetIntToString="";
        int pos=0;
        int n=0;
        String secondScenarioMapping="";
        String firstScenarioMapping="";
        int q=0;
       
        System.out.println("*************INITIAL VALUE OF  CYCLES: " + cycles);
        
        do
        {
            //need to understand the boundaries here a bit better
            //this recursive method will be called for r = rMin   to  r< rMax
            //P(n,r)
            
            //When it gets to here, it needs to focus on r
            
            
            for (q=0; q<r;q++)
            {
                
            //NEED TO SORT THIS
            if (sj.length()>=r)
            {
                System.out.println("Will attempt to add String for analysis: " + sj.toString());
                st.add(sj.toString());
                
                System.out.println("-----ADDED INTO FINAL SET: " + sj.toString());
                
                
            System.out.println("clearing stringjoiner************");
            sj = new StringJoiner ("");
            cycles++;
            totalcycles++;
            newSetSize=st.size();
                
            }
            
             if (sj1.length()>=r)
            {
                System.out.println("Will attempt to add String for analysis: " + sj1.toString());
                st.add(sj1.toString());
                System.out.println("-----ADDED INTO FINAL SET: " + sj1.toString());
                
            System.out.println("clearing stringjoiner************");
            sj1 = new StringJoiner ("");
            cycles++;
            totalcycles++;
            newSetSize=st.size();
                
            }
                
                //this is fine, it would start again
                if (invalidIndex)
                {
                    //I do not think I need to reset StringJoiner since it would have not stored anything
                    //until it has checked q==1
                    //but no harm in performing
                    
                    System.out.println("Clearing StringJoiner************");
                    sj=new StringJoiner("");
                    sj1=new StringJoiner("");
                    
                    //I think q has to be 0 again since it has to find a suitable first mapping
                    
                    q=0;
                    System.out.println("----------Q reset to 0");
                    
                    invalidIndex=false;
                }
                
                temp1 = rand.nextInt(possibleLettersMap.size()) + 1;      //this should get random number between 0 and the set size
                
                System.out.println("this is random number: " + temp1);
                //System.out.println("mapping: " + )
                
                
                //this is done so that it gets random generated letter
                //and it will later check to see if it is eligible start letter
                
                if (q==0)
                {
                //using random number to get value from possibleLetters of the Map
                backupFirstTemp1 = temp1;
                firstScenarioMapping  = mp.get(backupFirstTemp1);
                System.out.println("Q is 0: " + firstScenarioMapping);
                mapping=firstScenarioMapping;
                }
                
                //this is the other possibility of the first letter in which it can be composed
                //from 2 digits wide
                if (q==1)
                {
                    backupSecondTemp1=temp1;
                    secondScenarioMapping = String.valueOf(backupFirstTemp1).concat(String.valueOf(backupSecondTemp1));
                    System.out.println("This is second: " + secondScenarioMapping);
                    int secondScenarioMappingInteger = Integer.valueOf(secondScenarioMapping);
                    System.out.println("int to check in the map: " + secondScenarioMappingInteger);
                    
                    secondScenarioMapping = mp.get(secondScenarioMappingInteger);
                    mapping=secondScenarioMapping;
                    
                    System.out.println("Q is 1: " + secondScenarioMapping);
                }
                
                //now at this point, we need perform several analysis
                
                if (q==1)
                {
                    // this is correct, it has received first random number, it has checked for the mapping and it
                    //has the letter in her
                    System.out.println("first scenario mappping: " + firstScenarioMapping);
                    
                    //this is correct, it has received two random numbers, merged them and checked for mapping.....
                    System.out.println("second second mapping: " + secondScenarioMapping);  //this is correct
                    
                    //these are from other class and these will always remain constant
                    //since it is based on the encoded message provided by end user.
                    System.out.println("check 1: " + possibleMappingFirst);
                    System.out.println("check 2: " + possibleMappingSecond);
                
                //if the first mapping is not
                
                
                try
                {
                    System.out.println("**********************8INSIDE TRY:");
                    
                //this now checks that if either of the randomly generated mappings are not possible mappings,
                //it would break execution....
                //it is in a try since there can be NullPointerException, we know this would occur
                //if the random number was let's say 43 (two digits wide)
                
                
                
                if (!(firstScenarioMapping.equals(possibleMappingFirst)  || secondScenarioMapping.equals(possibleMappingSecond)))
                {
                    System.out.println("none acceptable and none null");
                    invalidIndex=true;
                    break;
                }
                
                //this is where it will get tricky
                //since both are viable first mappings and have been translated successfully, we need to run a StringJoiner with both as options.
                
                //12 1222        L = 12    we know that the mappings can be completed with 3 letters (min)  -  5 max
                //  1  21222     A=1       we know mappings would complete with 4 letters (minimum)  and 6 maximum
                //so how would this be handled with respect to q which is based on i (which operates between rMin and rMax)
                
                //it potentially means that if it takes the secondScenarioMapping (wider 2 digits), then it would need to perform one less operation.
                
                  
                //A=1
                //L=12
                
                //it will be here if there is NOT a null
                //in mappings 
                
                else
                {
                     if (firstScenarioMapping.equals(possibleMappingFirst))
                    {
                    
                    System.out.println("this is valid first scenario: " + firstScenarioMapping);
                    System.out.println("addding to SJ: " + sj.add(firstScenarioMapping));
                    }
                    
                    //if it has found a mapping of a letter, it is also viable
                    //it would need to be populated into another sj1 as discussed
                    if (secondScenarioMapping.equals(possibleMappingSecond))
                    {
                        System.out.println("this is valid second scenario: " + secondScenarioMapping);
                        sj1.add(secondScenarioMapping);
                    }
                    
                }
                
                
                }  //end try
                
                //so we know if the two digit wide number was 43, it would enter here since it would not find a mapping
                //value would be null
                //but this does not mean it will meet the criteria that it has generated mapping with either A or L in front on this instance.
                //since neither would cause NullPointerException
                //if it a catch, we just need to verify that
                //firstScenarioMapping.equals(possibleMappingFirst  == true
                //if this is the case, it would proceed and perform mapping and store in the StringJoiner
            
                catch (NullPointerException e)
                {
                    System.out.println("INSIDE CATCH");
                    if (firstScenarioMapping.equals(possibleMappingFirst))
                    {
                        System.out.println("1adding to SJ: " + firstScenarioMapping);
                        sj.add(firstScenarioMapping);
                        currentSetSize=st.size();
                    }
                    
                }
                
                
                 
                }  //end of if q==1
                
                
                //now this is the reality situation, when dealing with getting a random letter from the possibleLetters
                //where everything is out of control
                
                
                //we know that this String is based on having a J=10 or higher as first mapping
                //so it will continue normal operation....
                //it should have one less than sj
                
                
                
                    if (!invalidIndex && sj.length()!=0)
                 {
                     
                     
                         if (q>1)
                {
                    sj.add(mp.get(temp1));
                    System.out.println("2Adding to sj: " + mp.get(temp1));
                }
                
                if (q>2 && sj1.length()!=0)
                {
                    
                
                //need to perform one less operation here if it has inserted a two digit wide mapping J=10 in first position onwards
                    //we would need to start from q=2 onwards here.....
                    sj1.add(mp.get(temp1));
                }
                
            
                     
                     
                     
                     
                     
                        //System.out.println("ADDING EXTRA LETTER TO THE MAPPING: " + );
                        System.out.println("MAPPING SO FAR (SJ): " + sj.toString());
                        System.out.println("MAPPING SO FAR (SJ1): " + sj1.toString());
                        
                        System.out.println("HERE");
            
            
            
            //**************************************************
            //AT THIS POINT WE CAN ADD THE STRINGJOINERS INTO THE ST (SET)
            
            System.out.println("******************Contents sj: " + sj.toString());
            System.out.println("******************Contents sj1: " + sj1.toString());
            
            
                    }  //end of if (!invalidIndex)
                
                
            }   //end of for (int q=0; q<r;q++)
            
            
            
        }while (cycles<permutations);

        valuesSet = st.toArray(new String[st.size()]);
        backupValuesSetBeforeModification = st.toArray(new String[st.size()]);
        
        System.out.println("******************Contents of the backup set");
        System.out.println("******************Contents of the valuesSet");
        
        for (String g: valuesSet)
        {
            System.out.println("Subset " + subsetNumber+": " + g);
            subsetNumber++;
        }
        
        subsetNumber=1;
        
        for (String m: backupValuesSet)
        {
            n=0;
            do
            {
                if (m==valuesSet[n])
                {
                }
                
                n++;
                
            }while (n<valuesSet.length);
            
        }  //end for processing backupValuesSet
        
        System.out.println("*************NEW VALUE CYCLES: " + cycles);
        System.out.println("*************RUNNING TOTAL CYCLES: " + totalcycles);
        
        System.out.println("***PROCESSING SET AT INDEX: " + (difference));
        System.out.println("**ENDING AT INDEX:***** " + st.size() +"\n");
        
        backupValuesSet= new ArrayList<>(Arrays.asList(backupValuesSetBeforeModification)); 
        
        for (int entry=0; entry<valuesSet.length; entry++)
        {
            if (valuesSet[entry]!="ALREADY PROCESSED")
            {
                System.out.println(valuesSet[entry] + "    Subset: " + subsetEntry  + "  at cycle number: " + totalcycles);
                subsetEntry++;
            
                //MAIN LOGIC OF THE NEW CODE OFFICIALLY STARTS HERE.
                checkMappings(valuesSet[entry]);
            }
        }
        difference = newSetSize;
    }  //end of constructor...
    
    public void checkMappings (String storedEntry)
    {
        //can put the characters through a switch statement
        
        String conversion="";
        
        for (char c: storedEntry.toCharArray())
        {
            if (mpReverse.containsKey(c))
            {
                conversion = conversion + mpReverse.get(c);
            }
        }
        
        
        if (conversion.equals(originalString))
        {
            System.out.println("This is a valid translation: " + storedEntry + " for encoding: " + originalString);
        }
        else
        {
            System.out.println("INVALID");
        }
        
        conversion="";
        
    }
   
} //end of class.

public class Permutation
{
    public static void main(String[] args) 
    {
        //Keys k = Keys.A;
        //int m = k.map;
        
        Set<String> possibleLetters = new HashSet<>();
        System.out.println("Welcome to Online IDE!! Happy Coding :)");
        
        Map<Integer,String> possibleLettersMap = new HashMap<>();
        Map <Integer,String> mp = new HashMap<>();
        Map <Character,Integer> mpReverse = new HashMap<>();
        String possibleMappingFirst="";
        String possibleMappingSecond="";
        Staircase sc;
        String temp="";
        
        mpReverse.put('A',1);
        mpReverse.put('B',2);
        mpReverse.put('C',3);
        mpReverse.put('D',4);
        mpReverse.put('E',5);
        mpReverse.put('F',6);
        mpReverse.put('G',7);
        mpReverse.put('H',8);
        mpReverse.put('I',9);
        mpReverse.put('J',10);
        mpReverse.put('K',11);
        mpReverse.put('L',12);
        mpReverse.put('M',13);
        mpReverse.put('N',14);
        mpReverse.put('O',15);
        mpReverse.put('P',16);
        mpReverse.put('Q',17);
        mpReverse.put('R',18);
        mpReverse.put('S',19);
        mpReverse.put('T',20);
        mpReverse.put('U',21);
        mpReverse.put('V',22);
        mpReverse.put('W',23);
        mpReverse.put('X',24);
        mpReverse.put('Y',25);
        mpReverse.put('Z',26);
        
            
        mp.put(1,"A");
        mp.put(2,"B");
        mp.put(3,"C");
        mp.put(4,"D");
        mp.put(5,"E");
        mp.put(6,"F");
        mp.put(7,"G");
        mp.put(8,"H");
        mp.put(9,"I");
        mp.put(10,"J");
        mp.put(11,"K");
        mp.put(12,"L");
        mp.put(13,"M");
        mp.put(14,"N");
        mp.put(15,"O");
        mp.put(16,"P");
        mp.put(17,"Q");
        mp.put(18,"R");
        mp.put(19,"S");
        mp.put(20,"T");
        mp.put(21,"U");
        mp.put(22,"V");
        mp.put(23,"W");
        mp.put(24,"X");
        mp.put(25,"Y");
        mp.put(26,"Z");
        
        int rMin;
        int r=0;
        
        //REQUIRES 4 DIGIT WIDE
        String str = "12122";   //this is encoded message I have referenced in my logic documentation
        
        
        int rMax = str.length();
        int n=str.length();
        int rLimit = 64;  //taken to be 64 from past experience.
        int digitsToInteger=0;
        Character firstDigit;
        Character secondDigit;
        int index=0;
            
        for (int i=0; i<str.length();i++)
        {
            //System.out.println("Value of i: " + i);
            
            //also need to work with scenario that from i= onwards
            //it can join with the digit before and form a valid letter
            //this is how all permutations are generated
            
            if (i>=1)
            {
                String possibleEncoding = String.valueOf(str.charAt(i-1)).concat(String.valueOf(str.charAt(i)));
                digitsToInteger = Integer.valueOf(possibleEncoding);
            }
            
            
            if (mp.containsKey(digitsToInteger))
            {
                temp = mp.get(digitsToInteger);
                //tempString = Object.toString(temp);
                
                possibleLetters.add(temp);   //only need to store the String here since should not be indexing again?
                System.out.println("Following at index " + i + ":" + "("+str.charAt(i)+")"+ "  has been filled with temp: " + temp);
            }
            
            if (i==1)
            {
                possibleMappingSecond = temp;
            }
            
            
            //need to get the digits out, and search the map for the key  and get the value
            
            //ALSO NEED TO EXAMINE THIS AGAIN TO GET THE LETTERS
            
            
            int digitToInteger = Character.getNumericValue(str.charAt(i));
            
            
            if (mp.containsKey(digitToInteger))
            {
                temp = mp.get(digitToInteger);
                //tempString = Object.toString(temp);
                
                possibleLetters.add(temp);   //only need to store the String here since should not be indexing again?
                System.out.println("Following at index " + i + ":" + "("+str.charAt(i)+")"+ "  has been filled with temp: " + temp);
                
            }
            
            
            //reason for this section is that once it starts generating the 
            //selection, it will know that the first two digits (12) can be accepted
            //so that encoding starts with A=1 or  L=12
            
            if (i==0)
            {
                //temp is the letter from encoded number => LETTER
                //THIS IS CORRECT
                possibleMappingFirst = temp;
                
            }
            
            
        }
        
        //in practice we can complete same process for the last mapping
        //for instance if the String ends with   123   ,  last mapping could be  C=3  or W=23
        //but it would also mean that in random number selection it would need to process
        //last encoding ahead of the sequence...
        
        
        if (str.length()%2==1)
        {
            rMin=(str.length()+1)/2;
        }
        else
        {
            rMin=str.length()/2;
        }
        
        
        if (rMax>=rLimit)
        {
            System.out.println("rMax " + " will affect recursion");
        }
    
        System.out.println("***PERMUTATIONS***(WITH REPLACEMENT)");
        System.out.println("P^R(n,r) = " + "Math.pow(n,r)");
        
        System.out.println("**********THE ENCODED MESSAGE TRANSLATION***********");
        System.out.println("This is encoded message: " + str);
        System.out.println("This is rMin: " + rMin);
        System.out.println("This is rMax: " + rMax);
        
        Iterator<String> iterator = possibleLetters.iterator();
        
        while (iterator.hasNext()) 
        {
        //Integer key = iterator.next();
        //System.out.println(iterator.next());
        possibleLettersMap.put(index,iterator.next());
        index++;
        }
        
        
        
        for (int i=rMin; i<rMax; i++)
        {
        sc = new Staircase (Permutations (n,i), mp,possibleLettersMap, i, possibleMappingFirst,possibleMappingSecond, str, mpReverse);
        }
    }
        
        public static long Permutations (int n, int i)
        {
            System.out.println("P^R(" + n+","+i+") = " + "Math.pow("+n+","+i+")");
            
            
            System.out.println("Permutations: " + (long)Math.pow(n,i));
            return (long)Math.pow(n,i);
        }
}//end permutation class