// 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
        
        //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...
            //System.out.println("your sentence getting bigger:" + line);
            
            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("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:" + 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);
                System.out.println("last word in tokenizer should be same as trunc word: " + temp);
                System.out.println("What is 12wordcount here: " + wordCount);
                System.out.println("What is truncated wordcount: " + 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....
                
                System.out.println("Buffer: " + buffer);
                
                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("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...
                        
                        completeLine=sj.toString();    // the line is now ready and meets all requirements
                        
                    }
                 
                } 
                
                // 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");
                
                //all spaces appended at front to fill the buffer if 1 word in the line....
                
                sb.append(lineBeforeLastWord);
                
                System.out.println("CURRENT LENGTH of line:" + lengthLineBeforeLastWord);
                
                
                // now its time to put padding in.....
                for (int b=0; b<buffer; b++)
                {
                    //count++;
                    //since zero index, need to minus 1 from length....
                    sb.insert(0," ");
                    //System.out.println(count + " padding added at front");
                    completeLine = sb.toString();
                    
                    completeLinelength=completeLine.length();
                    
                //System.out.println("First line completed:" + completeLine);
                System.out.println("*********************");
                //System.out.println("Completed line to end user:" + completeLine);
                System.out.println("$$$$$$$$new length of line:$$$$$$$$$$ " + completeLinelength);
                
                }
                
                //at this point, it needs to wipe out all the contents in the stringjoiner
                // it also needs to keep temp back into it since this was last word that was 
                //discarded.
                sj.add(temp);
               
                }
                
                completeLinelength=completeLine.length();
                
                System.out.println("Completed line to end user1:" +completeLine);
                
                //count=0;
                
                
                //System.out.println("before reset, check values:");
                //System.out.println(sj);
                //System.out.println(sj2);
                 System.out.println("This word will be carried over to next line:" + temp);
                 //System.out.println(lastLine);
                
                //reset all StringJoiners, delete all StringBuilders...
                 sj=new StringJoiner(delimiter);
                 //sj2=new StringJoiner(delimiter);
                 wordsIntoLineAfterTruncation=0;
                totalExtraPaddingBetweenWords="";
                //wordsIntoLineAfterTruncation=0;
                
                //it also has to remove contents of both StringBuilders....
                //sb1.delete(0,sb1.length());
                sb.delete(0,sb.length());
                
                }
                
            }
            
            
        }   //end while (has more tokens)
        
        //unfortunately it will repeat several lines of the code above...
        //but not prepared to change the structure code too much.....
        //so need to check if it fits into k=16
        // and also need to add spaces in front if the word is shorter than k
        
        
        lastLine=sj.toString();
        
        
        // if there is nothing in the StringJoiner, it has to be given what is in temp?
        if (lastLine=="")
        {
            lastLine=temp;
        }
        
        System.out.println("WHAT IS LAST LINE: " + lastLine);
        
        
        int lengthLastLine = lastLine.length();
        
        try
        {
        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 which is: lines
            
            //both same....
            System.out.println("wc:" + wordCount);
            System.out.println("wc1:" + wordsIntoLineAfterTruncation);
            
            if (wordsIntoLineAfterTruncation==0)
            {
                wordsIntoLineAfterTruncation++;
            }
            
            //wordsLastLine = wordCount - wordsIntoLineAfterTruncation;
            //System.out.println("wc2:" + wordsLastLineNoTruncation);
            
            System.out.println("The left over in StringJoiner:" + lastLine);
            System.out.println("words last line: " + wordsIntoLineAfterTruncation);
            System.out.println("Total running words: " + wordCount);
            System.out.println("length: " + lengthLastLine);
            
            // We know content that is remaining will not form any part of truncation, since
            //it has been handled in all scenarios above....
            
            
            buffer=k-lengthLastLine;
            //extraPaddingBetweenWords = buffer%(wordsIntoLineAfterTruncation-1);
            //require to minus 1 from word count since padding is divided in between words
            //i.e  3 words,  2 areas for padding....
            
            //System.out.println("padding:" + fa);
            
            System.out.println("Buffer is: " + buffer);
            
            if (buffer%(wordsLastLine-1)==0 && wordsIntoLineAfterTruncation!=1 || wordsIntoLineAfterTruncation!=0)
                {
                    extraPaddingBetweenWords = buffer/(wordsIntoLineAfterTruncation-1);
                    
                    System.out.println("It will now process the string with extra: " + extraPaddingBetweenWords + " padding between words:" + lastLine);
                  
                        //***************** need to think which loop is this going in.... ****************
                        // it can be nested in one above, but not required....
                        
                  //need to be careful here, since if 0 extra padding is required, it will still execute once!
                  
                  System.out.println("****current length totalExtraPaddingBetweenWords: " + totalExtraPaddingBetweenWords.length() );
                  
                  if (extraPaddingBetweenWords>0)
                  {
                  for (int j=0; j<=extraPaddingBetweenWords;j++)  
                  //note it has to process one extra since the convention is to put a space in between.
                  //hence j<=extraPaddingBetweenWords
                  
                  {
                      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 as below...
                    //same as above for other lines....
                    
                    st2=new StringTokenizer(lastLine, delimiter);
                    sj = new StringJoiner(totalExtraPaddingBetweenWords);
                    
                    while (st2.hasMoreTokens())
                    {
                        //we know that all tokens will be part of the same line...
                        
                        temp2=st2.nextToken();
                        sj.add(temp2);   // it has to also add the token that was removed from first string!!
                        
                        completeLine=sj.toString();
                    }
                 
                } 
              
                else
                {
                    System.out.println("99SHOULD STAY OUT OF HERE!!!! unless 1 word");
                
                //all spaces appended at end to fill the buffer
                
                sb.append(lastLine);
                
                for (int b=0; b<buffer; b++)
                {
                    //count++;
                    //padding at end
                    sb.insert(lengthLastLine," ");
                    
                }
                
                completeLine = sb.toString();
                //System.out.println("First line completed:" + completeLine);
                
                //CAREFUL
                //System.out.println(completeLine);
                
                //No real housekeeping required of variables...
                //But it has to break out of the the do while loop since it will loop infinitely..
                
                }
          
        break;    
        }
    }
    
    catch (ArithmeticException e)
    {
        System.out.println("Most likely due to having one or no words in the sentence");
        
        sb.append(temp);
        
        lengthLastLine=lastLine.length();
        
        buffer=k-lengthLastLine;
        
        System.out.println(lastLine + " qualifies for padding at end since it has:" +  wordsIntoLineAfterTruncation + " word(s)");
        
                for (int b=0; b<buffer; b++)
                {
                
                    sb.insert((lengthLastLine),"0");
                    
                    
                }
                
            System.out.println(lastLine + " qualifies for " + buffer + " padding at end since it has: " +  wordsLastLine + " word(s)");
                
                completeLine = sb.toString();
                //System.out.println("First line completed:" + completeLine);
                
                //CAREFUL
                //System.out.println(completeLine);
                //System.exit(0);
    }
        completeLinelength = completeLine.length();
        System.out.println("completed line to end user:" + completeLine);
        System.out.println("CURRENT LENGTH22 of line:" + completeLinelength);
        //count=0;
        
    }
    
}
