// ALL DONE
// JUST CAN NOT REMEMBER HOW TO FIGURE OUT IF LAST TOKEN....
// SINCE NO TREATMENT HAS TAKEN PLACE ON IT!!!
// THE LOGIC ON APPLICATION WILL FAIL IF ONE WORD
//SINCE IT WILL PROCESS HASMORETOKENS   AND ALSO  !HASMORETOKENS SINCE IT DOESN'T KNOW THE ONLY TOKEN
//TRIGGERS BOTH STATES.....
// I HAVE JUST USED TRY AND CATCH OUT OF THIS AREA OF CODE....
// ALL FIXED NOW!
//I have also avoided any booleans since also more prone to error having to toggle states back to original...

//THE CODE IS DESIGNED TO HAVE THREE WAYS OF OUTPUTTING THE COMPLETED LINE TO END USER:
//1) A line with content truncated....
//2) A line with truncated content appending to start of another line...
//3) Last line...

//TEST CASES:
//String text = "The quick brown fox jumps over the lazy dog";   (PASSED)
//String text = " This is a test but making it a bit longer!";  (PASSED)
//String text = "My";  (PASSED)
//String text = "My name is Amit Amlani. This is a sligthly longer test to see if the text can be spanned across multiple lines.";  (PASSED)
//String text ="";  (PASSED)
//String text = "This will be 16."  (PASSED)   - exact length of k = 16

/*
Online Java - IDE, Code Editor, Compiler

Online Java is a quick and easy tool that helps you to build, compile, test your programs online.
*/


import java.util.*;

public class Main
{
    public static void main(String[] args) {
        System.out.println("Welcome to Online IDE!! Happy Coding :)");
        
        //THESE ARE ALL TEST CASES......
        //String text = "The quick brown fox jumps over the lazy dog";
        //String text="";
        //String text = " This is a test but making it a bit longer!";
        //String text = "My";
        String text = "My name is Amit Amlani. This is a sligthly longer test to see if the text can be spanned across multiple lines.";
        //String text = "This will be 16.";
        
        String lastLine="";   //last completed line
        //int count=0;
        int k=16;   // this is the length of the line, the example states 16
        String tokentoString;  //tokens require conversion to String...
        String line;  // this will be the line before any padding has been applied..
        int wordCount=0;  // a running total of number words.. This will be useful notably when it reaches !hasMoreTokens
        int lineLength; // this is the length of the line before it applies any
        String lineBeforeLastWord="";  //not most friendly name, but it refers to the line with truncation undertaken...
        int lengthLineBeforeLastWord;  // length of the line as described above...
        String completeLine="";  //this will be presented to end user in maximum three possible ways...
        String totalExtraPaddingBetweenWords=""; // This is the padding which needs to be divided equally inter-words..
        int extraPaddingBetweenWords=0;  //this will work in conjunction with totalExtraPaddingBetweenWords to generate the correct amount "  "
        
        //int lengthfinalToken;  //
        int extraFrontPadding=0;   // total front padding...
        String truncatedWord="";   // this will either contain a single word overspilt or be blank...  
        String delimiter=" ";  // this is used to process any StringTokenizers and get the words...
        int wordsIntoLineAfterTruncation=0;  //  words on line after truncation.. This is very important to understand where padding can be applied..
        int buffer=0; // this is simply   k - (length of line after truncation occured);
        int wordsLastLine=0;   // number words last line..
        int completeLinelength=0;  // used for onscreen validation to ensure it has reached k
        int lengthLastLine;
        
        //totalExtraPaddingBetweenWords=delimiter; // is this correct?
        //String sentence="";
        
        // I initally wanted to mantain several StringJoiners.
        // During the coding I used 2 or 3... But it could be hazardous if it was not cleared.
        //Also they were used mutually exclusively, so it was an extra risk having different instances..
        
        // So I kept one instance, and it was used:
        StringJoiner sj = new StringJoiner(delimiter);
        
        int tokenLength;
        
        //to avoid mistakes in coding, and setting wrong parameter into the constructor, I have mantained
        //three for readability..
        //First argument is the String text.
        //delimiter is set as " "  since each word has this delimiter...
        StringTokenizer st = new StringTokenizer(text, delimiter);
        String temp="";  // this will hold the value of the token in while loop above to process tokens.
        
        StringTokenizer st1; 
        // constructor will be initial during code as follows:
        //= new StringTokenizer (lineBeforeLastWord, delimiter); 
        String temp1;  // this will hold the value of the token in while loop above to process tokens.
        
        StringTokenizer st2;
        // constructor will be initial during code as follows:
        //=new StringTokenizer(lastLine, delimiter);
        String temp2; // this will hold the value of the token in while loop above to process tokens..
        
        StringBuilder sb=new StringBuilder(); // used so that the string can be manipulated before stored into completed line..
       
       //need to think about how to allocate spaces...
        //need to keep a count of the words
       
       
        while (st.hasMoreTokens())  //checking text for tokens....
        {
            //need be careful truncatedWord needs to be added once only, 
            //not each time the hasMoreTokens called...
            //this situation is only valid once it has displayed first line to end user on the screen...
            if (truncatedWord!="")
            {
                sb.append(truncatedWord);  // StringBuffer will be clear and ommited word from first line is stored here...
                sj.add(sb);   //first word added to StringJoiner....
                truncatedWord="";  //the content is no longer required, it also prevents re-entering until next line is processed..
                sb.delete(0,sb.length());  //the Stringbuffer has to be emptied..
                //otherwise this word will form part of consecutive words again...
                
                //normally this would not be the case... On this instance, it will end up adding the word strinbuffer again into StringJoiner
                //once the line is finalised.. This would be repeat word.
                //In practice, the following is also valid and saves code:
                //sj.add(truncatedWord) since there are no considerations to format it given its first word..
                
                wordsIntoLineAfterTruncation++;  //total number of words on line increases...
                
            }
            
            wordCount++;  //overall total number of words increases....
            
            
            temp=st.nextToken();   //value of the token...
            tokentoString = temp.toString();   //token converted to String...
            tokenLength=tokentoString.length();  //length of token....
            //this is only needed for one sole purpose and ascertain if any word is too long..
            //Unfortunately it can not preemptively get this until token arrives....
            
            //The code has to terminate otherwise it would cause severe issues in execution given k limit..
            if (tokenLength>k)
            {
                System.out.println("The following word exceeds word limit of k" + "(" + k + "):  " + temp);
                System.exit(0);  //exit
            }
            
            sb.append(tokentoString);  //the StringBuffer will be empty to store a token. 
            
            lineBeforeLastWord = sj.toString();  
            //always keeps and overwrites this... It is also one word less than what will be sent into 
            //StringJoiner..  Reason is if the k limit is exceeded, it has the line available to roll back to without
            //having to process all tokens again..
            
            sj.add(sb);  //  StringBuffer added into StringJoiner.... This is a continuous process with delimiter defined...
            
            sb.delete(0,sb.length());  //remove contents of StringBuffer ready for another word...
            
            
            line = sj.toString();  // it also has to keep track of line....
            
            lineLength=line.length();  //determines length of the line...
            
            //Used during testing to ensure that the line was infact getting bigger...
            
            
            wordsIntoLineAfterTruncation++;
            //note this value should be exactly same as wordCount if all the words fit naturally and exactly at K boundaries on a line
            //Alternatively, it should also be the same with wordCount on the first line, up to point a word is truncated for exceeding k
            
            System.out.println("your sentence getting bigger:" + line + "(" + lineLength + " chars inc white space)");
            System.out.println("Words after truncation: " + wordsIntoLineAfterTruncation);
            
            
            //This is just some pseudo code.....
            //first thing is if the natural line has taken it over k,
            // need to truncate a word.....
            //count length of words...
            // subtract it from k....
            //if less than (wordCount-1)
            //pads at front....    (COMPLETE...)
            
            if (lineLength>k)   
            //if the value in lineLength (formed via StringJoiner)  is greater than k
            {
                //need remove last token....
                //can not remember how I did that last time..remember
                //kept copy of stringjoiner...
                
                lengthLineBeforeLastWord= lineBeforeLastWord.length();
                
                System.out.println("Rolled back to(due to exceeding " + k +"):" + lineBeforeLastWord);
                System.out.println("Length rolled back line: " + lengthLineBeforeLastWord);
                
                //at this point there clearly is not enough buffer to allow an additional space between 
                //the characters...
                //it has to therefore force the spaces on the left hand side...
                
                //also, if it has rolled back, it needs to store a copy of the last word that was 
                //truncated...
                //otherwise nextToken will bypass it since it is already processed...
                truncatedWord=temp;
                
                System.out.println("This word getting cut off:" + truncatedWord);
                
                //it now reduces the count since one word has been removed...
                wordsIntoLineAfterTruncation--;
                
                
                buffer = k-lengthLineBeforeLastWord;
                //this is amount excess room on the line..
                
                //Screen outputs to ensure execution is ok..
                System.out.println("The current buffer is: " + buffer);
                System.out.println("Number words to accomodate for: " + wordsIntoLineAfterTruncation + "("+ lineBeforeLastWord+")");
                System.out.println("Last word in tokenizer should be same as truncated word: " + temp);
                System.out.println("What is wordcount here: " + wordCount);
                System.out.println("What is truncated word count: " + wordsIntoLineAfterTruncation +"(" + lineBeforeLastWord +")  " + lengthLineBeforeLastWord + " chars" );
                System.out.println("The current buffer is: " + buffer);
                
                                
                //if there is additional room..
                //if it fills exactly length K the sentence, it does not qualify for padding...
                
                if (buffer>0)
                {
                    
                
                // if extra spaces can go between words (such as two between words),
                //this is preferable than adding padding at front... as per requirements....
                
               
                sj = new StringJoiner(" ");  //resets StringJoiner....
                
                //this will be empty for first line, but needs to be cleared as below thereafter...
                completeLine="";
                
                //note that for every 3 words, inter-word padding is considered at two locations
                // so 1 is subtracted from wordsIntoLineAfterTruncation..
                //it is only possible if there is no remainder from the buffer, so modulus will be taken...
                //the same inter-word spacing has to be adhered to at all times...
                
                //Also need to be careful and consider minimum words in line has to be greater than 1...
                //Otherwise it will start putting inter-word padding which is not possible.
                if (buffer%(wordsIntoLineAfterTruncation-1)==0 && wordsIntoLineAfterTruncation>1)
                {
                    //uniform extra padding between words...
                    extraPaddingBetweenWords = buffer/(wordsIntoLineAfterTruncation-1);
                
                System.out.println("CURRENT LENGTH of line: " + lengthLineBeforeLastWord);
                
                System.out.println("Qualified for extra padding");
                
                //informs end user...
                System.out.println(extraPaddingBetweenWords + " extra padding between:" + lineBeforeLastWord);
                
                  // This now configures extra padding..
                  // Note this is unrelated to existing space between words.... 
                   for (int j=0; j<extraPaddingBetweenWords;j++)
                  {
                      totalExtraPaddingBetweenWords = totalExtraPaddingBetweenWords + " ";
                      
                  }
                  
                    //the only way to get extra spaces between the existing lineBeforeLastWord
                    // is to create another StringTokenizer with same delimiter " "
                    //Then to set up another StringJoiner with new delimiter (delimiter + totalExtraPaddingBetweenWords)
                   
                    System.out.println("It will now process truncated string with extra " + extraPaddingBetweenWords + " padding between the words:" + lineBeforeLastWord);
                    
                    //this is unrelated to padding.. It is merely splitting the line with delimiter...
                    st1=new StringTokenizer(lineBeforeLastWord, delimiter); 
                    
                    // it will now increase delimiter with additional padding...
                    sj = new StringJoiner(delimiter+totalExtraPaddingBetweenWords); 
                    
                    //getting all tokens..
                    while (st1.hasMoreTokens())
                    {
                        //we know that all tokens will be part of the same line...
                        
                        temp1=st1.nextToken();  //storing token....
                        sj.add(temp1);  //adding token to StringJoiner...
                        
                        sb.append(sj);
                        
                        //again completeLine could be sj.toString()
                        
                        // the line is now ready and meets all requirements
                        completeLine=sb.toString();
                        
                        
                        
                        sb.delete(0,sb.length()); //removes content...
                        
                    }
                    System.out.println("New length of line after formatting: " + completeLinelength);
                 
                } 
                
                // it is entering here since there is uneven buffer to split amongst the words...
                //Note there is buffer>0 since it is nested within this loop.....
                else
                {
                    extraFrontPadding=buffer; //assigning all spare space at front....
                    System.out.println(lineBeforeLastWord + "   qualifies for " + extraFrontPadding + " padding at front since it has:" +  wordsIntoLineAfterTruncation + " words");
                   
                   //preparing the unformatted line for modification.... 
                   sb.append(lineBeforeLastWord);
                
                System.out.println("CURRENT LENGTH of line: " + lengthLineBeforeLastWord);
                
                // now its time to put padding in..... much easier this time...
                for (int b=0; b<buffer; b++)
                {
                    
                    //for testing best to use commented version.... to see the padding visibly..
                    //sb.insert(0,"%");
                    sb.insert(0," ");
                }
                
                //the line is now complete...
                completeLine = sb.toString();
                
                completeLinelength=completeLine.length();
               
                System.out.println("New length of line after formatting: " + completeLinelength);
                
                // it also needs to keep temp back into StringJoiner since this was last word that was 
                //discarded.
                //note this is not related temp1 even though it has been used recently..
                //temp1 is processing the tokenizer for the line formed in which truncation has occured.
                //Truncation has occured earlier in first StringTokenizer
                
                sj.add(temp);
               
                }
                
                completeLinelength=completeLine.length();
                
                System.out.println("Completed line:" +completeLine);
                
                //System.out.println("before reset, check values:");
                //System.out.println(sj);
                //System.out.println(sj2);
                //System.out.println(lastLine);
                
                System.out.println("This word will be carried over to next line:" + temp +"\n");
                 
                
                //reset StringJoiner
                 sj=new StringJoiner(delimiter);
                 
                 //it is now ready to start a new line....
                 wordsIntoLineAfterTruncation=0;
                 
                 //important to set the padding between words back to none...
                 totalExtraPaddingBetweenWords="";
                
                //it also has to remove contents of both StringBuilder
                sb.delete(0,sb.length());
                
                }  //end of if buffer is greater than 0
                
            }  //end of if linelength is greater than k
            
            
        }   //end while (has more tokens)
        
        //unfortunately it will repeat several lines of the code as above...
        //I attempted to change structure of the code, but could not get any consolidation...
        // the same rules apply as above.
        
        // this was a bit challenging, since it required my first usage of try and catch
        // within my programming...
        // I will look to find future uses, if there is a genuine requirement as below....
        
        //Also need to consider why code has reached here given that all tokens have been examined.... 
        //At this point it has reached the last line...
        //NEED EXPAND LATER.....
        
        lastLine=sj.toString();
        
        // if there is nothing in the lastline, it has to be assigned what is in temp
        //The logic is a bit difficult to explain here....
        
        //There would be nothing in the lastline if ONLY it has undergone no formatting...
        //since the StringJoiners would have cleared....  In which case it would take value temp
        // which is the single truncated word... And the truncated word that will remain in this variable would be 
        //the word cut off the penultimate completed line
        
        //otherwise, lastline will form sj.toString();
        //So it is picking up the StringJoiner formed right within the first Tokenizer....
        //The question would be as to whether StringJoiner would have have truncated word stored in temp?
        //And in the initial T
        // I am not entirely sure, but I know that having lots System.out.println helped to navigate without issues.
        
        if (lastLine=="")
        {
            lastLine=temp;
        }
        
        System.out.println("Finally at last line:" + lastLine);
        
        
        lengthLastLine = lastLine.length();  // holds the length last line...
        
        
        // I had to force this since there were several calculations involving 1-0  such as if 1
        //word existed in wordsIntoLineAfterTruncation
        
        try
        {
        
        //at this point it has processed all tokens in the text...
        //it is a case of trying to get what is not formatted..
        
        while (!st.hasMoreTokens())
        {
            //need to think here
            // can there be two words here left over in StringJoiner, yes can be any length...
            //but what we know that it has hit the last token....
            
            //both same here if there is exactly one line printed on screen..
            //no other circumstance can give this..
            //having a count of this is important since the differential is only way
            //to determine word count of what has not been processed....
            
            
            if (wordsIntoLineAfterTruncation==0)
            {
                wordsIntoLineAfterTruncation++;
            }
            
             //Need to understand how this knows the words in the last line even though it has not processed above...
            System.out.println("Left over StringJoiner: " + wordsIntoLineAfterTruncation + " word " +  "("+lastLine+")" + "     (" + lengthLastLine + " chars inc white space)");
            
            // Need to understand why the wordsIntoLineAfterTruncation would be 0, yet it reaches this far...
            // Reason is it has not had chance to increment this value anywhere...
            //After each line processed above, it has set value back to 0....
            
       
            //System.out.println("The left over in StringJoiner:" + lastLine);
            //System.out.println("words last line: " + wordsIntoLineAfterTruncation);
            System.out.println("Total running words: " + wordCount);
           
            
            // We know content that is remaining will not form any truncation, since
            //it has been handled in all scenarios above....
            
            buffer=k-lengthLastLine;  //seting buffer...
            
            System.out.println("Buffer is: " + buffer);
            
            //a slightly different if condition is required..
            // first part it checks if  buffer can be spread evenly.. And as before, it needs to be longer than
            //1 word long in order for inter-word padding...
            // it can be seen here why the try & catch was required...
            //If there was 1 word in last line, subtracting 1 would give 0...
            //Buffer/0  would give an overflow.......
            // We know however there is a legitimate cause for one word in the line...
            //since it can qualify for padding...
            
            if ((buffer%(wordsLastLine-1)==0 && wordsIntoLineAfterTruncation>1) )
                {
                    //same logic as above.....
                    extraPaddingBetweenWords = buffer/(wordsIntoLineAfterTruncation-1);
                    
                    System.out.println("It will now process the string with extra: " + extraPaddingBetweenWords + " padding between words:" + lastLine);
                  
                     System.out.println("****current length totalExtraPaddingBetweenWords: " + totalExtraPaddingBetweenWords.length() );
                  
                  //now creating extra padding....
                  if (extraPaddingBetweenWords>0)
                  {
                  for (int j=0; j<extraPaddingBetweenWords;j++)  
                  
                  {
                      totalExtraPaddingBetweenWords = totalExtraPaddingBetweenWords + " ";
                      
                  }
                  }
                    //the only way to get extra spaces between the existing lastline
                    // is to create another StringTokenizer with same delimiter ""
                    //Then to set up another StringJoiner with new delimiter as below...
                    //same as above for other lines....
                    
                    st2=new StringTokenizer(lastLine, delimiter);
                    
                    //the overall delimiter has been increased as previously...
                    sj = new StringJoiner(delimiter + totalExtraPaddingBetweenWords);
                    
                    //processing tokens for lastline
                    while (st2.hasMoreTokens())
                    {
                        temp2=st2.nextToken();
                        sj.add(temp2); 
                        
                        //The line is now ready.. This is the last line....
                        //It was not so obvious where this message would be provided to end user...
                        //but the fact that it reached here in the try block which still extends downwards,
                        //meant that variable could be accessed...
                        // I knew if the catch executed, this section of code would become irrelevant similar to if else statement...
                        
                        completeLine=sj.toString();
                    }
                 
                }   //end if checking if multiple words and buffer can be divided inter-word....
              
              //still in the try loop
              
              
                // it is here because words in last line is equal to 1....
                else
                {
                    //at this point, temp1 and temp2 are not initialised at all.
                    // it has to be remembered that scope of temp are within their respective do while loops
                    
                    //temp was associated with the main StringTokenizer for all text...
                    //temp1 was part of the lineBeforeLastWord...   this was all the strings that had formatting...
                    //temp2 was processed just above... again this was part of do while loop, but it has an initialised value...
                    //I found by this point my head was extremely tired on remembering all logic...
                    //But having screen outputs gave me a sense of direction...
                    
                    System.out.println("One word in last line:" + temp);
                    
                    //this variable tokentoString should also be initialised, but apparently failing..
                    //so having to convert temp back to String...
                    
                    tokentoString=temp;
                    
                    System.out.println("CURRENT LENGTH of line: " + tokentoString.length());
                    
                    //last line is added and being prepared for formatting....
                    sb.append(lastLine);
                
                //System.out.println(temp + " qualifies for padding at end since it has:" +  wordsIntoLineAfterTruncation + " word(s)");
                
                System.out.println(lastLine + "   qualifies for " + buffer + " padding at end since it has:" +  wordsIntoLineAfterTruncation + " word(s)");
                
                //since it is one word, the requirements are different.
                //all padding is fitted at the end inside the StringBuilder.....
                //only a subtle change required  in the insert....
                for (int b=0; b<buffer; b++)
                {
                    //for testing best to use the commented version to see padding come into effect...
                    //sb.insert((lengthLastLine),"0");
                    
                    sb.insert(lengthLastLine," ");
                    
                }
                
                //The complete line is now ready.....
                completeLine = sb.toString();
                
                //No real housekeeping required of variables...
                //since main logical processing is complete unless it enters the catch section...
                //But more importantly, it has to break out of the the do while loop since it will loop infinitely..
                //since it will ALWAYS evaluate !hasMoreTokens
                
                }  //end of else statement....
          
        break;    //breaking out of do while loop
        }    //end of while loop
    }        //end of try loop
    
    
    //it has reached here because there was a thrown error such as X/0 
    //it has recognised this since wordsIntoLineAfterTruncation-1 = 0   (assuming 1 word was left....)
    
    catch (ArithmeticException e)
    {
        System.out.println("Most likely due to having one or no words in the sentence");
        System.out.println("So the StringBuilder should be empty: " + sb.toString());
        
        //Again it has to be questioned, why temp is initalised now and not few lines earlier...
        //I relied severely on screen outputs which got me luckily over the line....
        
        //the last token processed in the first StringTokenizer is stored in StringBuilder
        //temp1 and temp2 have not been initialised at this point......
        //but it becomes slightly easier to explain that the StringBuffer 
        sb.append(temp);
        
        //Again last time it used Lastline was when following comment inputted into my code....
        //NEED EXPAND LATER.....
        //It is unfortunately becoming difficult to explain, but logic is sustaining for all test scenarios!
        
        lengthLastLine=lastLine.length();   //length of the last line....
        
        buffer=k-lengthLastLine;  //buffer is remaining from k.
        
        //Information to end user....
        System.out.println(lastLine + "   qualifies for " + buffer + " padding at end since it has:" +  wordsIntoLineAfterTruncation + " word(s)");
        
                for (int b=0; b<buffer; b++)
                {
                    //for testing purpose it is important to use the commented version for sb.insert
                    //reason is an output such as following:
                    //completed line to end user:lines.   //there are 10 whitespaces at end but its not visible...
                    
                    //However with this:
                    //completed line to end user:lines.0000000000
                    //sb.insert((lengthLastLine),"0");
                    
                    sb.insert((lengthLastLine)," ");
                }
            
            //Information to end user.....    
            System.out.println(lastLine + "   qualifies for " + buffer + " padding at end since it has: " +  wordsLastLine + " word(s)");
                
                completeLine = sb.toString();
               
               
    }  //end of catch statement...
    
        completeLinelength = completeLine.length();
        System.out.println("completed line:" + completeLine);
        System.out.println("NEW LENGTH of line:" + completeLinelength);
        
        
    }  //end of main method.
    
}  //end of single class used......
